Skip to content

Latest commit

 

History

History
171 lines (127 loc) · 4.68 KB

File metadata and controls

171 lines (127 loc) · 4.68 KB

Argo CD with Vault and the External secret operator demo

Example of using External Secrets with Vault and Argo CD. The demo application is a Go web server that reads database credentials from a mounted Kubernetes secret and automatically reloads them when they change—no pod restart required.

Read the full blog at https://codefresh.io/blog/gitops-secrets-with-argo-cd-hashicorp-vault-and-the-external-secret-operator/


How it works

HashiCorp Vault  →  External Secrets Operator  →  Kubernetes Secret  →  Application

ESO polls Vault every 15 seconds and writes values into a native Kubernetes Secret. The app mounts that secret as a file and uses fsnotify to reload it without restarting.


1. Prerequisites

  • A Kubernetes cluster with cluster-admin permissions
  • kubectl and Argo CD installed in the argocd namespace
kubectl create namespace argocd
kubectl apply -n argocd --server-side --force-conflicts -f https://raw.githubusercontent
.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
kubectl get all -n argocd
argocd admin initial-password -n argocd
argocd login localhost:80

2. Set up Vault

Install Vault in dev mode (root token = root, pre-unsealed) via Argo CD:

argocd app create vault \
--project default \
--repo https://helm.releases.hashicorp.com \
--helm-chart vault \
--revision 0.28.0 \
--sync-policy auto \
--sync-option CreateNamespace=true \
--parameter server.dev.enabled=true \
--dest-namespace vault \
--dest-server https://kubernetes.default.svc

Exec into the pod and configure it:

kubectl exec -it vault-0 -n vault -- /bin/sh
vault login root

# Write the demo credentials
vault kv put secret/mysql_credentials \
  url="mysql.example.com:3306" \
  username="my_demo_user" \
  password="my_demo_password"

# Enable Kubernetes auth
vault auth enable kubernetes
vault write auth/kubernetes/config \
  kubernetes_host="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT"

# Policy and role for ESO
vault policy write eso-read-policy - <<EOF
path "secret/*" {
  capabilities = [ "read", "list" ]
}
EOF

vault write auth/kubernetes/role/demo \
  bound_service_account_names=* \
  bound_service_account_namespaces=* \
  policies=eso-read-policy \
  ttl=24h

exit

3. Set up the External Secrets Operator

argocd app create eso \
--project default \
--repo https://charts.external-secrets.io \
--helm-chart external-secrets \
--revision 0.9.19 \
--sync-policy auto \
--sync-option CreateNamespace=true \
--dest-namespace external-secrets \
--dest-server https://kubernetes.default.svc

Apply the ClusterSecretStore (connects ESO to Vault using the demo Kubernetes auth role):

argocd app create vault-secret-store \
--project default \
--repo https://github.com/kostis-codefresh/external-secrets-gitops-example.git \
--path "./manifests/vault-integration" \
--sync-policy auto \
--dest-namespace external-secrets \
--dest-server https://kubernetes.default.svc


kubectl get clustersecretstore vault-backend  # should show READY: True

4. Deploy the app with Argo CD

Point an Argo CD application at manifests/app in this repo. That directory contains the Deployment, Service, and the ExternalSecret that creates the mysql-credentials Kubernetes secret.

argocd app create my-secret-app \
--project default \
--repo https://github.com/kostis-codefresh/external-secrets-gitops-example.git \
--path "./manifests/app" \
--sync-policy auto \
--dest-namespace default \
--dest-server https://kubernetes.default.svc

Once synced, verify and access the app:

kubectl get externalsecret my-db-credentials  # STATUS: SecretSynced
kubectl port-forward svc/gitops-secrets-service 8080:8080

Open http://localhost:8080 to see the current credentials.


5. Rotate a secret and watch the app update itself

Update the secret in Vault:

kubectl exec -it vault-0 -n vault -- vault kv put secret/mysql_credentials \
  url="mysql.example.com:3306" \
  username="rotated_user" \
  password="new_super_secret_password"

You can also use the Vault UI but at http://localhost:8200/ui/vault/secrets/secret/kv/mysql_credentials/metadata/versions. Port forward with kubectl port-forward -n vault vault-0 8200:8200 Then login using root as token.

Within ~15 seconds the app reloads automatically—no restart, no Argo CD sync:

kubectl logs -f -l app=gitops-secrets-app
# Config file changed: /secrets/credentials
# Username is rotated_user
# Password is new_super_secret_password

Refresh http://localhost:8080 to confirm.