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
argocdlabelledargocd.argoproj.io/secret-type: repository, withtype,urland a key or token. Arepo-credsSecret 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.templateadds 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.
conditionslists 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 withkubectl create secret. In production it is a vault reached with workload identity; the ExternalSecrets do not change. - The URL must match: the Application's
repoURLmust 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
Unknownplus an authentication error at the next fetch, not at once.--hard-refreshshows it now.
Commands
kubectl -n store-platform create secret generic NAME --from-file=sshPrivateKey=KEY | Put a value in a store, never in Git |
kubectl get clustersecretstore | Is each store Valid and READY True? |
kubectl -n NS get externalsecret,secret NAME | The ExternalSecret from Git (SecretSynced) and the Secret built from it |
kubectl -n NS describe externalsecret NAME | Why a Secret was not built: Conditions and Events |
kubectl -n NS annotate externalsecret NAME force-sync=$(date +%s) --overwrite | Fetch again now, instead of waiting for refreshInterval |
ssh-keygen -q -t ed25519 -N '' -f KEY | A key pair for Argo CD (no passphrase: it runs unattended) |
gh repo deploy-key add KEY.pub -R OWNER/REPO --title argocd | Register the public half, read-only |
argocd repo list | Is 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.