github.com/argoproj/argo-cd/v3@v3.2.1/util/db/repository_secrets.go (about) 1 package db 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 log "github.com/sirupsen/logrus" 9 "google.golang.org/grpc/codes" 10 "google.golang.org/grpc/status" 11 corev1 "k8s.io/api/core/v1" 12 apierrors "k8s.io/apimachinery/pkg/api/errors" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 15 "github.com/argoproj/argo-cd/v3/common" 16 appsv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 17 "github.com/argoproj/argo-cd/v3/util/git" 18 ) 19 20 var _ repositoryBackend = &secretsRepositoryBackend{} 21 22 type secretsRepositoryBackend struct { 23 db *db 24 // If true, the backend will manage write only credentials. If false, it will manage only read credentials. 25 writeCreds bool 26 } 27 28 func (s *secretsRepositoryBackend) CreateRepository(ctx context.Context, repository *appsv1.Repository) (*appsv1.Repository, error) { 29 secName := RepoURLToSecretName(repoSecretPrefix, repository.Repo, repository.Project) 30 31 repositorySecret := &corev1.Secret{ 32 ObjectMeta: metav1.ObjectMeta{ 33 Name: secName, 34 }, 35 } 36 37 updatedSecret := s.repositoryToSecret(repository, repositorySecret) 38 39 _, err := s.db.createSecret(ctx, updatedSecret) 40 if err != nil { 41 if apierrors.IsAlreadyExists(err) { 42 hasLabel, err := s.hasRepoTypeLabel(secName) 43 if err != nil { 44 return nil, status.Error(codes.Internal, err.Error()) 45 } 46 if !hasLabel { 47 msg := fmt.Sprintf("secret %q doesn't have the proper %q label: please fix the secret or delete it", secName, common.LabelKeySecretType) 48 return nil, status.Error(codes.InvalidArgument, msg) 49 } 50 return nil, status.Errorf(codes.AlreadyExists, "repository %q already exists", repository.Repo) 51 } 52 return nil, err 53 } 54 55 return repository, s.db.settingsMgr.ResyncInformers() 56 } 57 58 // hasRepoTypeLabel will verify if a secret with the given name exists. If so it will check if 59 // the secret has the proper label argocd.argoproj.io/secret-type defined. Will return true if 60 // the label is found and false otherwise. Will return false if no secret is found with the given 61 // name. 62 func (s *secretsRepositoryBackend) hasRepoTypeLabel(secretName string) (bool, error) { 63 noCache := make(map[string]*corev1.Secret) 64 sec, err := s.db.getSecret(secretName, noCache) 65 if err != nil { 66 if apierrors.IsNotFound(err) { 67 return false, nil 68 } 69 return false, err 70 } 71 _, ok := sec.GetLabels()[common.LabelKeySecretType] 72 if ok { 73 return true, nil 74 } 75 return false, nil 76 } 77 78 func (s *secretsRepositoryBackend) GetRepoCredsBySecretName(_ context.Context, name string) (*appsv1.RepoCreds, error) { 79 secret, err := s.db.getSecret(name, map[string]*corev1.Secret{}) 80 if err != nil { 81 return nil, fmt.Errorf("failed to get secret %s: %w", name, err) 82 } 83 return s.secretToRepoCred(secret) 84 } 85 86 func (s *secretsRepositoryBackend) GetRepository(_ context.Context, repoURL, project string) (*appsv1.Repository, error) { 87 secret, err := s.getRepositorySecret(repoURL, project, true) 88 if err != nil { 89 if status.Code(err) == codes.NotFound { 90 return &appsv1.Repository{Repo: repoURL}, nil 91 } 92 93 return nil, err 94 } 95 96 repository, err := secretToRepository(secret) 97 if err != nil { 98 return nil, err 99 } 100 101 return repository, err 102 } 103 104 func (s *secretsRepositoryBackend) ListRepositories(_ context.Context, repoType *string) ([]*appsv1.Repository, error) { 105 var repos []*appsv1.Repository 106 107 secrets, err := s.db.listSecretsByType(s.getSecretType()) 108 if err != nil { 109 return nil, err 110 } 111 112 for _, secret := range secrets { 113 r, err := secretToRepository(secret) 114 if err != nil { 115 if r == nil { 116 return nil, err 117 } 118 modifiedTime := metav1.Now() 119 r.ConnectionState = appsv1.ConnectionState{ 120 Status: appsv1.ConnectionStatusFailed, 121 Message: "Configuration error - please check the server logs", 122 ModifiedAt: &modifiedTime, 123 } 124 125 log.Warnf("Error while parsing repository secret '%s': %v", secret.Name, err) 126 } 127 128 if repoType == nil || *repoType == r.Type { 129 repos = append(repos, r) 130 } 131 } 132 133 return repos, nil 134 } 135 136 func (s *secretsRepositoryBackend) UpdateRepository(ctx context.Context, repository *appsv1.Repository) (*appsv1.Repository, error) { 137 repositorySecret, err := s.getRepositorySecret(repository.Repo, repository.Project, false) 138 if err != nil { 139 if status.Code(err) == codes.NotFound { 140 return s.CreateRepository(ctx, repository) 141 } 142 return nil, err 143 } 144 145 updatedSecret := s.repositoryToSecret(repository, repositorySecret) 146 147 _, err = s.db.kubeclientset.CoreV1().Secrets(s.db.ns).Update(ctx, updatedSecret, metav1.UpdateOptions{}) 148 if err != nil { 149 return nil, err 150 } 151 152 return repository, s.db.settingsMgr.ResyncInformers() 153 } 154 155 func (s *secretsRepositoryBackend) DeleteRepository(ctx context.Context, repoURL, project string) error { 156 secret, err := s.getRepositorySecret(repoURL, project, false) 157 if err != nil { 158 return err 159 } 160 161 if err := s.db.deleteSecret(ctx, secret); err != nil { 162 return err 163 } 164 165 return s.db.settingsMgr.ResyncInformers() 166 } 167 168 func (s *secretsRepositoryBackend) RepositoryExists(_ context.Context, repoURL, project string, allowFallback bool) (bool, error) { 169 secret, err := s.getRepositorySecret(repoURL, project, allowFallback) 170 if err != nil { 171 if status.Code(err) == codes.NotFound { 172 return false, nil 173 } 174 175 return false, fmt.Errorf("failed to get repository secret for %q: %w", repoURL, err) 176 } 177 178 return secret != nil, nil 179 } 180 181 func (s *secretsRepositoryBackend) CreateRepoCreds(ctx context.Context, repoCreds *appsv1.RepoCreds) (*appsv1.RepoCreds, error) { 182 secName := RepoURLToSecretName(credSecretPrefix, repoCreds.URL, "") 183 184 repoCredsSecret := &corev1.Secret{ 185 ObjectMeta: metav1.ObjectMeta{ 186 Name: secName, 187 }, 188 } 189 190 updatedSecret := s.repoCredsToSecret(repoCreds, repoCredsSecret) 191 192 _, err := s.db.createSecret(ctx, updatedSecret) 193 if err != nil { 194 if apierrors.IsAlreadyExists(err) { 195 return nil, status.Errorf(codes.AlreadyExists, "repository credentials %q already exists", repoCreds.URL) 196 } 197 return nil, err 198 } 199 200 return repoCreds, s.db.settingsMgr.ResyncInformers() 201 } 202 203 func (s *secretsRepositoryBackend) GetRepoCreds(_ context.Context, repoURL string) (*appsv1.RepoCreds, error) { 204 secret, err := s.getRepoCredsSecret(repoURL) 205 if err != nil { 206 if status.Code(err) == codes.NotFound { 207 return nil, nil 208 } 209 210 return nil, err 211 } 212 213 return s.secretToRepoCred(secret) 214 } 215 216 func (s *secretsRepositoryBackend) ListRepoCreds(_ context.Context) ([]string, error) { 217 var repoURLs []string 218 219 secrets, err := s.db.listSecretsByType(common.LabelValueSecretTypeRepoCreds) 220 if err != nil { 221 return nil, err 222 } 223 224 for _, secret := range secrets { 225 repoURLs = append(repoURLs, string(secret.Data["url"])) 226 } 227 228 return repoURLs, nil 229 } 230 231 func (s *secretsRepositoryBackend) UpdateRepoCreds(ctx context.Context, repoCreds *appsv1.RepoCreds) (*appsv1.RepoCreds, error) { 232 repoCredsSecret, err := s.getRepoCredsSecret(repoCreds.URL) 233 if err != nil { 234 if status.Code(err) == codes.NotFound { 235 return s.CreateRepoCreds(ctx, repoCreds) 236 } 237 return nil, err 238 } 239 240 updatedSecret := s.repoCredsToSecret(repoCreds, repoCredsSecret) 241 242 repoCredsSecret, err = s.db.kubeclientset.CoreV1().Secrets(s.db.ns).Update(ctx, updatedSecret, metav1.UpdateOptions{}) 243 if err != nil { 244 return nil, err 245 } 246 247 updatedRepoCreds, err := s.secretToRepoCred(repoCredsSecret) 248 if err != nil { 249 return nil, err 250 } 251 252 return updatedRepoCreds, s.db.settingsMgr.ResyncInformers() 253 } 254 255 func (s *secretsRepositoryBackend) DeleteRepoCreds(ctx context.Context, name string) error { 256 secret, err := s.getRepoCredsSecret(name) 257 if err != nil { 258 return err 259 } 260 261 if err := s.db.deleteSecret(ctx, secret); err != nil { 262 return err 263 } 264 265 return s.db.settingsMgr.ResyncInformers() 266 } 267 268 func (s *secretsRepositoryBackend) RepoCredsExists(_ context.Context, repoURL string) (bool, error) { 269 _, err := s.getRepoCredsSecret(repoURL) 270 if err != nil { 271 if status.Code(err) == codes.NotFound { 272 return false, nil 273 } 274 275 return false, err 276 } 277 278 return true, nil 279 } 280 281 func (s *secretsRepositoryBackend) GetAllHelmRepoCreds(_ context.Context) ([]*appsv1.RepoCreds, error) { 282 var helmRepoCreds []*appsv1.RepoCreds 283 284 secrets, err := s.db.listSecretsByType(common.LabelValueSecretTypeRepoCreds) 285 if err != nil { 286 return nil, err 287 } 288 289 for _, secret := range secrets { 290 if strings.EqualFold(string(secret.Data["type"]), "helm") { 291 repoCreds, err := s.secretToRepoCred(secret) 292 if err != nil { 293 return nil, err 294 } 295 296 helmRepoCreds = append(helmRepoCreds, repoCreds) 297 } 298 } 299 300 return helmRepoCreds, nil 301 } 302 303 func (s *secretsRepositoryBackend) GetAllOCIRepoCreds(_ context.Context) ([]*appsv1.RepoCreds, error) { 304 var ociRepoCreds []*appsv1.RepoCreds 305 306 secrets, err := s.db.listSecretsByType(common.LabelValueSecretTypeRepoCreds) 307 if err != nil { 308 return nil, err 309 } 310 311 for _, secret := range secrets { 312 if strings.EqualFold(string(secret.Data["type"]), "oci") { 313 repoCreds, err := s.secretToRepoCred(secret) 314 if err != nil { 315 return nil, err 316 } 317 318 ociRepoCreds = append(ociRepoCreds, repoCreds) 319 } 320 } 321 322 return ociRepoCreds, nil 323 } 324 325 func secretToRepository(secret *corev1.Secret) (*appsv1.Repository, error) { 326 secretCopy := secret.DeepCopy() 327 328 repository := &appsv1.Repository{ 329 Name: string(secretCopy.Data["name"]), 330 Repo: string(secretCopy.Data["url"]), 331 Username: string(secretCopy.Data["username"]), 332 Password: string(secretCopy.Data["password"]), 333 BearerToken: string(secretCopy.Data["bearerToken"]), 334 SSHPrivateKey: string(secretCopy.Data["sshPrivateKey"]), 335 TLSClientCertData: string(secretCopy.Data["tlsClientCertData"]), 336 TLSClientCertKey: string(secretCopy.Data["tlsClientCertKey"]), 337 Type: string(secretCopy.Data["type"]), 338 GithubAppPrivateKey: string(secretCopy.Data["githubAppPrivateKey"]), 339 GitHubAppEnterpriseBaseURL: string(secretCopy.Data["githubAppEnterpriseBaseUrl"]), 340 Proxy: string(secretCopy.Data["proxy"]), 341 NoProxy: string(secretCopy.Data["noProxy"]), 342 Project: string(secretCopy.Data["project"]), 343 GCPServiceAccountKey: string(secretCopy.Data["gcpServiceAccountKey"]), 344 } 345 346 insecureIgnoreHostKey, err := boolOrFalse(secretCopy, "insecureIgnoreHostKey") 347 if err != nil { 348 return repository, err 349 } 350 repository.InsecureIgnoreHostKey = insecureIgnoreHostKey 351 352 insecure, err := boolOrFalse(secretCopy, "insecure") 353 if err != nil { 354 return repository, err 355 } 356 repository.Insecure = insecure 357 358 enableLfs, err := boolOrFalse(secretCopy, "enableLfs") 359 if err != nil { 360 return repository, err 361 } 362 repository.EnableLFS = enableLfs 363 364 enableOCI, err := boolOrFalse(secretCopy, "enableOCI") 365 if err != nil { 366 return repository, err 367 } 368 repository.EnableOCI = enableOCI 369 370 insecureOCIForceHTTP, err := boolOrFalse(secretCopy, "insecureOCIForceHttp") 371 if err != nil { 372 return repository, err 373 } 374 repository.InsecureOCIForceHttp = insecureOCIForceHTTP 375 376 githubAppID, err := intOrZero(secretCopy, "githubAppID") 377 if err != nil { 378 return repository, err 379 } 380 repository.GithubAppId = githubAppID 381 382 githubAppInstallationID, err := intOrZero(secretCopy, "githubAppInstallationID") 383 if err != nil { 384 return repository, err 385 } 386 repository.GithubAppInstallationId = githubAppInstallationID 387 388 forceBasicAuth, err := boolOrFalse(secretCopy, "forceHttpBasicAuth") 389 if err != nil { 390 return repository, err 391 } 392 repository.ForceHttpBasicAuth = forceBasicAuth 393 394 useAzureWorkloadIdentity, err := boolOrFalse(secretCopy, "useAzureWorkloadIdentity") 395 if err != nil { 396 return repository, err 397 } 398 repository.UseAzureWorkloadIdentity = useAzureWorkloadIdentity 399 400 return repository, nil 401 } 402 403 func (s *secretsRepositoryBackend) repositoryToSecret(repository *appsv1.Repository, secret *corev1.Secret) *corev1.Secret { 404 secretCopy := secret.DeepCopy() 405 406 if secretCopy.Data == nil { 407 secretCopy.Data = make(map[string][]byte) 408 } 409 410 updateSecretString(secretCopy, "name", repository.Name) 411 updateSecretString(secretCopy, "project", repository.Project) 412 updateSecretString(secretCopy, "url", repository.Repo) 413 updateSecretString(secretCopy, "username", repository.Username) 414 updateSecretString(secretCopy, "password", repository.Password) 415 updateSecretString(secretCopy, "bearerToken", repository.BearerToken) 416 updateSecretString(secretCopy, "sshPrivateKey", repository.SSHPrivateKey) 417 updateSecretBool(secretCopy, "enableOCI", repository.EnableOCI) 418 updateSecretBool(secretCopy, "insecureOCIForceHttp", repository.InsecureOCIForceHttp) 419 updateSecretString(secretCopy, "tlsClientCertData", repository.TLSClientCertData) 420 updateSecretString(secretCopy, "tlsClientCertKey", repository.TLSClientCertKey) 421 updateSecretString(secretCopy, "type", repository.Type) 422 updateSecretString(secretCopy, "githubAppPrivateKey", repository.GithubAppPrivateKey) 423 updateSecretInt(secretCopy, "githubAppID", repository.GithubAppId) 424 updateSecretInt(secretCopy, "githubAppInstallationID", repository.GithubAppInstallationId) 425 updateSecretString(secretCopy, "githubAppEnterpriseBaseUrl", repository.GitHubAppEnterpriseBaseURL) 426 updateSecretBool(secretCopy, "insecureIgnoreHostKey", repository.InsecureIgnoreHostKey) 427 updateSecretBool(secretCopy, "insecure", repository.Insecure) 428 updateSecretBool(secretCopy, "enableLfs", repository.EnableLFS) 429 updateSecretString(secretCopy, "proxy", repository.Proxy) 430 updateSecretString(secretCopy, "noProxy", repository.NoProxy) 431 updateSecretString(secretCopy, "gcpServiceAccountKey", repository.GCPServiceAccountKey) 432 updateSecretBool(secretCopy, "forceHttpBasicAuth", repository.ForceHttpBasicAuth) 433 updateSecretBool(secretCopy, "useAzureWorkloadIdentity", repository.UseAzureWorkloadIdentity) 434 addSecretMetadata(secretCopy, s.getSecretType()) 435 436 return secretCopy 437 } 438 439 func (s *secretsRepositoryBackend) secretToRepoCred(secret *corev1.Secret) (*appsv1.RepoCreds, error) { 440 secretCopy := secret.DeepCopy() 441 442 repository := &appsv1.RepoCreds{ 443 URL: string(secretCopy.Data["url"]), 444 Username: string(secretCopy.Data["username"]), 445 Password: string(secretCopy.Data["password"]), 446 BearerToken: string(secretCopy.Data["bearerToken"]), 447 SSHPrivateKey: string(secretCopy.Data["sshPrivateKey"]), 448 TLSClientCertData: string(secretCopy.Data["tlsClientCertData"]), 449 TLSClientCertKey: string(secretCopy.Data["tlsClientCertKey"]), 450 Type: string(secretCopy.Data["type"]), 451 GithubAppPrivateKey: string(secretCopy.Data["githubAppPrivateKey"]), 452 GitHubAppEnterpriseBaseURL: string(secretCopy.Data["githubAppEnterpriseBaseUrl"]), 453 GCPServiceAccountKey: string(secretCopy.Data["gcpServiceAccountKey"]), 454 Proxy: string(secretCopy.Data["proxy"]), 455 NoProxy: string(secretCopy.Data["noProxy"]), 456 } 457 458 enableOCI, err := boolOrFalse(secretCopy, "enableOCI") 459 if err != nil { 460 return repository, err 461 } 462 repository.EnableOCI = enableOCI 463 464 insecureOCIForceHTTP, err := boolOrFalse(secretCopy, "insecureOCIForceHttp") 465 if err != nil { 466 return repository, err 467 } 468 repository.InsecureOCIForceHttp = insecureOCIForceHTTP 469 470 githubAppID, err := intOrZero(secretCopy, "githubAppID") 471 if err != nil { 472 return repository, err 473 } 474 repository.GithubAppId = githubAppID 475 476 githubAppInstallationID, err := intOrZero(secretCopy, "githubAppInstallationID") 477 if err != nil { 478 return repository, err 479 } 480 repository.GithubAppInstallationId = githubAppInstallationID 481 482 forceBasicAuth, err := boolOrFalse(secretCopy, "forceHttpBasicAuth") 483 if err != nil { 484 return repository, err 485 } 486 repository.ForceHttpBasicAuth = forceBasicAuth 487 488 useAzureWorkloadIdentity, err := boolOrFalse(secretCopy, "useAzureWorkloadIdentity") 489 if err != nil { 490 return repository, err 491 } 492 repository.UseAzureWorkloadIdentity = useAzureWorkloadIdentity 493 494 return repository, nil 495 } 496 497 func (s *secretsRepositoryBackend) repoCredsToSecret(repoCreds *appsv1.RepoCreds, secret *corev1.Secret) *corev1.Secret { 498 secretCopy := secret.DeepCopy() 499 500 if secretCopy.Data == nil { 501 secretCopy.Data = make(map[string][]byte) 502 } 503 504 updateSecretString(secretCopy, "url", repoCreds.URL) 505 updateSecretString(secretCopy, "username", repoCreds.Username) 506 updateSecretString(secretCopy, "password", repoCreds.Password) 507 updateSecretString(secretCopy, "bearerToken", repoCreds.BearerToken) 508 updateSecretString(secretCopy, "sshPrivateKey", repoCreds.SSHPrivateKey) 509 updateSecretBool(secretCopy, "enableOCI", repoCreds.EnableOCI) 510 updateSecretBool(secretCopy, "insecureOCIForceHttp", repoCreds.InsecureOCIForceHttp) 511 updateSecretString(secretCopy, "tlsClientCertData", repoCreds.TLSClientCertData) 512 updateSecretString(secretCopy, "tlsClientCertKey", repoCreds.TLSClientCertKey) 513 updateSecretString(secretCopy, "type", repoCreds.Type) 514 updateSecretString(secretCopy, "githubAppPrivateKey", repoCreds.GithubAppPrivateKey) 515 updateSecretInt(secretCopy, "githubAppID", repoCreds.GithubAppId) 516 updateSecretInt(secretCopy, "githubAppInstallationID", repoCreds.GithubAppInstallationId) 517 updateSecretString(secretCopy, "githubAppEnterpriseBaseUrl", repoCreds.GitHubAppEnterpriseBaseURL) 518 updateSecretString(secretCopy, "gcpServiceAccountKey", repoCreds.GCPServiceAccountKey) 519 updateSecretString(secretCopy, "proxy", repoCreds.Proxy) 520 updateSecretString(secretCopy, "noProxy", repoCreds.NoProxy) 521 updateSecretBool(secretCopy, "forceHttpBasicAuth", repoCreds.ForceHttpBasicAuth) 522 updateSecretBool(secretCopy, "useAzureWorkloadIdentity", repoCreds.UseAzureWorkloadIdentity) 523 addSecretMetadata(secretCopy, s.getRepoCredSecretType()) 524 525 return secretCopy 526 } 527 528 func (s *secretsRepositoryBackend) getRepositorySecret(repoURL, project string, allowFallback bool) (*corev1.Secret, error) { 529 secrets, err := s.db.listSecretsByType(s.getSecretType()) 530 if err != nil { 531 return nil, fmt.Errorf("failed to list repository secrets: %w", err) 532 } 533 534 var foundSecret *corev1.Secret 535 for _, secret := range secrets { 536 if git.SameURL(string(secret.Data["url"]), repoURL) { 537 projectSecret := string(secret.Data["project"]) 538 if project == projectSecret { 539 if foundSecret != nil { 540 log.Warnf("Found multiple credentials for repoURL: %s", repoURL) 541 } 542 543 return secret, nil 544 } 545 546 if projectSecret == "" && allowFallback { 547 if foundSecret != nil { 548 log.Warnf("Found multiple credentials for repoURL: %s", repoURL) 549 } 550 551 foundSecret = secret 552 } 553 } 554 } 555 556 if foundSecret != nil { 557 return foundSecret, nil 558 } 559 560 return nil, status.Errorf(codes.NotFound, "repository %q not found", repoURL) 561 } 562 563 func (s *secretsRepositoryBackend) getRepoCredsSecret(repoURL string) (*corev1.Secret, error) { 564 secrets, err := s.db.listSecretsByType(s.getRepoCredSecretType()) 565 if err != nil { 566 return nil, err 567 } 568 569 index := s.getRepositoryCredentialIndex(secrets, repoURL) 570 if index < 0 { 571 return nil, status.Errorf(codes.NotFound, "repository credentials %q not found", repoURL) 572 } 573 574 return secrets[index], nil 575 } 576 577 func (s *secretsRepositoryBackend) getRepositoryCredentialIndex(repoCredentials []*corev1.Secret, repoURL string) int { 578 maxLen, idx := 0, -1 579 repoURL = git.NormalizeGitURL(repoURL) 580 for i, cred := range repoCredentials { 581 credURL := git.NormalizeGitURL(string(cred.Data["url"])) 582 if strings.HasPrefix(repoURL, credURL) { 583 if len(credURL) == maxLen { 584 log.Warnf("Found multiple credentials for repoURL: %s", repoURL) 585 } 586 if len(credURL) > maxLen { 587 maxLen = len(credURL) 588 idx = i 589 } 590 } 591 } 592 return idx 593 } 594 595 func (s *secretsRepositoryBackend) getSecretType() string { 596 if s.writeCreds { 597 return common.LabelValueSecretTypeRepositoryWrite 598 } 599 return common.LabelValueSecretTypeRepository 600 } 601 602 func (s *secretsRepositoryBackend) getRepoCredSecretType() string { 603 if s.writeCreds { 604 return common.LabelValueSecretTypeRepoCredsWrite 605 } 606 return common.LabelValueSecretTypeRepoCreds 607 }