HashiCorp Vault and External Secrets Operator: Secure Kubernetes Secrets
Master HashiCorp Vault and External Secrets Operator to automate your Kubernetes secrets management. Learn how to sync sensitive data securely in your cluster.
Managing secrets in Kubernetes is a constant pain point. If you’re manually creating Kubernetes Secrets or—heaven forbid—committing base64-encoded strings to Git, you’re doing it wrong. I’ve spent years cleaning up these messes. The industry standard isn't just "encrypting at rest"; it's about centralizing your secrets in a dedicated vault and syncing them into your clusters on demand.
In this post, we’re going to set up HashiCorp Vault as our source of truth and use the External Secrets Operator (ESO) to inject those secrets directly into Kubernetes.
Why External Secrets Operator?
Kubernetes native secrets are just base64-encoded strings stored in etcd. They aren't truly secure. By using HashiCorp Vault, you get audit logs, rotation policies, and fine-grained access control. ESO acts as the bridge; it polls Vault and creates native Kubernetes Secret objects, meaning your applications don't need to change their code to talk to Vault.
Prerequisites
- A running Kubernetes cluster (v1.24+)
- A HashiCorp Vault instance (v1.12+)
- Helm installed
Step 1: Installing the External Secrets Operator
We’ll install ESO using Helm. It’s the most straightforward path.
Bashhelm repo add external-secrets https://charts.external-secrets.io helm repo update helm install external-secrets external-secrets/external-secrets \ -n external-secrets \ --create-namespace
Once installed, verify the pods are running with kubectl get pods -n external-secrets. You should see the controller and the webhook pods up and ready.
Step 2: Configure Vault Authentication
ESO needs a way to talk to Vault. The cleanest way is using Kubernetes ServiceAccount tokens.
First, enable the Kubernetes auth method in Vault:
Bashvault auth enable kubernetes
Configure the auth path to point to your Kubernetes cluster:
Bashvault write auth/kubernetes/config \ kubernetes_host="https://<YOUR_K8S_API_URL>"
Now, create a role in Vault that maps a Kubernetes ServiceAccount to a Vault policy.
Step 3: Create the SecretStore
The SecretStore is a namespaced resource that tells ESO how to connect to Vault.
YAMLapiVersion: external-secrets.io/v1beta1 kind: SecretStore metadata: name: vault-backend spec: provider: vault: server: "https://vault.example.com:8200" path: "secret" version: "v2" auth: kubernetes: mountPath: "kubernetes" role: "eso-role"
Apply this with kubectl apply -f secretstore.yaml. Ensure the status is Ready by running kubectl get secretstore.
Step 4: Syncing Secrets with ExternalSecret
Now for the magic. We define an ExternalSecret resource that tells ESO which keys to fetch from Vault and how to map them to a local Kubernetes Secret.
YAMLapiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: app-db-secret spec: refreshInterval: "1h" secretStoreRef: name: vault-backend kind: SecretStore target: name: db-credentials # The name of the resulting K8s Secret creationPolicy: Owner data: - secretKey: db-password remoteRef: key: secret/data/myapp/db property: password
ESO will now reach out to Vault, pull the value for password from the secret/data/myapp/db path, and create a Kubernetes Secret named db-credentials.
Hard-Won Lessons from Production
I’ve seen teams get this wrong, so keep these three things in mind:
- Refresh Intervals: Don't set
refreshIntervaltoo low (e.g., every 10 seconds). You’ll hammer your Vault instance unnecessarily. 1h or 5m is usually plenty. - RBAC is Key: Ensure the ServiceAccount ESO uses has the absolute minimum permissions in Vault. Use the principle of least privilege.
- Monitoring: Monitor the ESO controller logs. If it fails to sync, it will tell you exactly why (usually a permission issue or a missing secret path).
Final Thoughts
Integrating HashiCorp Vault with External Secrets Operator transforms your secret management from a manual, error-prone task into a robust, automated DevSecOps pipeline. Your developers get the native Kubernetes Secret experience they expect, while your security team gets the centralized control they demand.
It’s a win-win. Stop hardcoding your credentials and start automating today.