The Secret Management Problem
Kubernetes Secrets are base64-encoded (not encrypted) and stored in etcd. In production you need:
- Secrets managed in a central vault (Vault, AWS SM, Azure KV)
- Automatic rotation without redeployment
- Audit trail of who accessed what
- No secrets in Git, ever
External Secrets Operator (ESO) syncs secrets from external stores into Kubernetes.
Architecture
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Kubernetes Cluster β
β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β External Secrets Operator β β
β βββββββββββββββββββββ¬βββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββΌβββββββββββββββββββββββ β
β β ExternalSecret CRD β β
β β (defines what to fetch and where) β β
β βββββββββββββββββββββ¬βββββββββββββββββββββββ β
β β sync β
β βββββββββββββββββββββΌβββββββββββββββββββββββ β
β β Kubernetes Secret β β
β β (auto-created and refreshed) β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββββ ββββββββββββ ββββββββββββββ
β HashiCorp β β AWS β β Azure β
β Vault β β Secrets β β Key Vault β
ββββββββββββββββ ββββββββββββ ββββββββββββββInstallation
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespaceProvider Configuration
AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: eu-west-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa # IRSAHashiCorp Vault
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets"Azure Key Vault
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: azure-kv
spec:
provider:
azurekv:
vaultUrl: "https://my-keyvault.vault.azure.net"
authType: WorkloadIdentity
serviceAccountRef:
name: external-secrets-saFetch Secrets
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: database-credentials
namespace: production
spec:
refreshInterval: 1h # Re-sync every hour (catches rotations)
secretStoreRef:
name: aws-secrets
kind: SecretStore
target:
name: db-credentials # K8s Secret name
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: production/database
property: username
- secretKey: password
remoteRef:
key: production/database
property: password
- secretKey: host
remoteRef:
key: production/database
property: hostThis creates a Kubernetes Secret db-credentials with keys username, password, host β automatically refreshed every hour.
Use in Pods
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: app
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials # Created by ESO
key: passwordSecret Rotation
When the source secret rotates (e.g., AWS auto-rotation every 30 days):
- ESO detects change on next
refreshInterval - Updates Kubernetes Secret
- Pods with
envFromneed restart (use Reloader or Stakater)
# Auto-restart pods when secret changes
metadata:
annotations:
secret.reloader.stakater.com/reload: "db-credentials"Supported Providers
| Provider | Auth Methods |
|---|---|
| AWS Secrets Manager | IRSA, Access Key, Web Identity |
| AWS Parameter Store | IRSA, Access Key |
| HashiCorp Vault | Kubernetes, Token, AppRole |
| Azure Key Vault | Workload Identity, Managed Identity |
| GCP Secret Manager | Workload Identity, Service Account |
| 1Password | Connect Server |
| Doppler | Service Token |