PlatformEngr® Argo CD course

Cheat notes · Module 08 · Real-world delivery

Private repositories and secrets

A five-minute review for when you have done this module and want it back in your head. Open the full module for the steps and the lab, or the full text to print.

The idea

Credentials are the one thing that must not sit in Git. Argo CD reads a private repo with a read-only deploy key stored as a labelled Secret. Values live in a secret store; Git holds only ExternalSecrets, references that say which key to fetch from which store, and External Secrets builds the Secrets in the cluster.

Remember

  • Repository credential: a Secret in argocd labelled argocd.argoproj.io/secret-type: repository, with type, url and a key or token. A repo-creds Secret does the same for every repo under a URL prefix.
  • Deploy key: an SSH key that can read one repository and belongs to no person. The narrowest credential GitHub offers, and no outage when someone leaves.
  • ExternalSecret: names a store (secretStoreRef), the key in it (remoteRef) and the Secret to build (target). target.template adds labels, such as the repository label Argo CD looks for. Safe to commit: it holds no value.
  • ClusterSecretStore: how External Secrets reaches a store and logs in. conditions lists the only namespaces that may read from it. One store per team.
  • On kind: each store is a namespace (store-platform, store-checkout) you fill with kubectl create secret. In production it is a vault reached with workload identity; the ExternalSecrets do not change.
  • The URL must match: the Application's repoURL must be the SSH URL the credential was made for.
  • The options: External Secrets with a store (this course), SOPS (you have a cloud KMS), encrypt-for-one-cluster controllers (avoid: a rebuild means re-encrypting everything). Avoid the Vault plugin: secrets end up in the repo-server cache.

Pitfalls

  • Pointing an ExternalSecret at another team's store: denied by spec.condition, Degraded, and no Secret. Use your own store.
  • Forgetting the value: an ExternalSecret whose key is not in the store stays SecretSyncedError. Git says what; someone still has to put the value in the store.
  • A changed value reaches the Secret at the next refresh, but a pod reads environment variables only at start. Restart the Deployment after a rotation.
  • A repo made private, or a deleted deploy key, shows up as Unknown plus an authentication error at the next fetch, not at once. --hard-refresh shows it now.

Commands

kubectl -n store-platform create secret generic NAME --from-file=sshPrivateKey=KEYPut a value in a store, never in Git
kubectl get clustersecretstoreIs each store Valid and READY True?
kubectl -n NS get externalsecret,secret NAMEThe ExternalSecret from Git (SecretSynced) and the Secret built from it
kubectl -n NS describe externalsecret NAMEWhy a Secret was not built: Conditions and Events
kubectl -n NS annotate externalsecret NAME force-sync=$(date +%s) --overwriteFetch again now, instead of waiting for refreshInterval
ssh-keygen -q -t ed25519 -N '' -f KEYA key pair for Argo CD (no passphrase: it runs unattended)
gh repo deploy-key add KEY.pub -R OWNER/REPO --title argocdRegister the public half, read-only
argocd repo listIs each repository connection Successful?

Test yourself

Answer in your head first, then open the answer.

What makes a Secret a repository credential for Argo CD?

The label argocd.argoproj.io/secret-type: repository, plus url and credentials. Here the ExternalSecret's template sets the label.

Why a deploy key instead of a personal access token?

It reads one repository only and belongs to no person.

Someone reads your ExternalSecret on GitHub. What can they do with it?

Nothing useful. It names a store and a key; only identities you granted can read the store.

What keeps team-checkout from reading the platform's deploy keys?

The conditions on the platform ClusterSecretStore: only argocd may use it.

Where module 08 leaves you: checkout-config is private and Argo CD reads it over SSH with a deploy key from the platform store. Each environment has a checkout-db Secret built by an ExternalSecret, and the checkout pod gets DB_PASSWORD. Git holds only references.