Secrets Management Guide
Overview
This guide covers two main approaches for managing secrets in Kubernetes:
- External Secrets Operator (ESO)
- Secrets Store CSI Driver
Prerequisites
Kubernetes Cluster Requirements
- Kubernetes 1.19+
- RBAC enabled
cert-manager
installed (required for ESO webhook)- Secrets Store CSI Driver installed
Cloud Provider Requirements
AWS
- AWS CLI installed and configured
- IAM roles with necessary permissions:
secretsmanager:GetSecretValue
secretsmanager:DescribeSecret
secretsmanager:ListSecrets
eksctl
for EKS clusters
Azure
- Azure CLI installed and configured
- Azure subscription with Key Vault access
- Service Principal with required permissions:
- Key Vault Secrets User
- Key Vault Secrets Officer
GCP
- Google Cloud SDK installed and configured
- Service account with Secret Manager access
- Workload Identity configured
Required Tools
bash
# Helm 3.0+
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
External Secrets Operator (ESO)
Installation
- Add the Helm repository:
bash
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
- Install ESO:
bash
helm install external-secrets \
external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace \
--set installCRDs=true
Cloud Provider Setup
AWS Secrets Manager
- Create IAM policy:
bash
aws iam create-policy \
--policy-name external-secrets-policy \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "*"
}
]
}'
- Create SecretStore:
yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-backend
spec:
provider:
aws:
service: SecretsManager
region: us-west-2
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
Azure Key Vault
- Create Key Vault:
bash
az keyvault create \
--name prime-edm-kv \
--resource-group prime-edm-rg \
--location eastus
- Create SecretStore:
yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: azure-backend
spec:
provider:
azurekv:
tenantId: "your-tenant-id"
vaultUrl: "https://prime-edm-kv.vault.azure.net"
authSecretRef:
clientId:
name: azure-secret-creds
key: client-id
clientSecret:
name: azure-secret-creds
key: client-secret
Google Secret Manager
- Enable Secret Manager API:
bash
gcloud services enable secretmanager.googleapis.com
- Create SecretStore:
yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: gcp-backend
spec:
provider:
gcpsm:
projectID: your-project-id
auth:
workloadIdentity:
serviceAccountRef:
name: external-secrets-sa
namespace: default
Creating External Secrets
- Create an ExternalSecret:
yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: database-secret
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-backend
kind: SecretStore
target:
name: db-credentials
data:
- secretKey: username
remoteRef:
key: prod/db/credentials
property: username
- secretKey: password
remoteRef:
key: prod/db/credentials
property: password
Secrets Store CSI Driver
Installation
- Add the Secrets Store CSI Driver Helm repository:
bash
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm repo update
- Install the driver:
bash
helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver \
--namespace kube-system \
--set syncSecret.enabled=true \
--set enableSecretRotation=true
Cloud Provider Setup
AWS Provider
- Install the AWS provider:
bash
helm repo add aws-secrets-manager https://aws.github.io/secrets-store-csi-driver-provider-aws
helm repo update
helm install secrets-provider-aws aws-secrets-manager/secrets-store-csi-driver-provider-aws \
--namespace kube-system
- Create SecretProviderClass:
yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: aws-secrets
spec:
provider: aws
parameters:
objects: |
- objectName: "prod/db/credentials"
objectType: "secretsmanager"
objectAlias: "DB_CREDENTIALS"
secretObjects:
- secretName: db-credentials
type: Opaque
data:
- objectName: DB_CREDENTIALS
key: username
- objectName: DB_CREDENTIALS
key: password
Azure Provider
- Install the Azure provider:
bash
helm repo add csi-secrets-store-provider-azure https://azure.github.io/secrets-store-csi-driver-provider-azure/charts
helm repo update
helm install azure-csi-provider csi-secrets-store-provider-azure/csi-secrets-store-provider-azure \
--namespace kube-system
- Create SecretProviderClass:
yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: azure-secrets
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true"
userAssignedIdentityID: "<client-id>"
keyvaultName: "<key-vault-name>"
objects: |
array:
- |
objectName: dbUsername
objectType: secret
- |
objectName: dbPassword
objectType: secret
tenantId: "<tenant-id>"
GCP Provider
- Install the GCP provider:
bash
helm repo add secrets-store-csi-driver-provider-gcp https://kubernetes-sigs.github.io/secrets-store-csi-driver-provider-gcp/charts
helm repo update
helm install secrets-provider-gcp secrets-store-csi-driver-provider-gcp/secrets-store-csi-driver-provider-gcp \
--namespace kube-system
- Create SecretProviderClass:
yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: gcp-secrets
spec:
provider: gcp
parameters:
secrets: |
- resourceName: "projects/$PROJECT_ID/secrets/db-username/versions/latest"
path: "username"
- resourceName: "projects/$PROJECT_ID/secrets/db-password/versions/latest"
path: "password"
Using Secrets in Pods
- Mount secrets as volumes:
yaml
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
volumes:
- name: secrets-store-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "aws-secrets" # or azure-secrets, gcp-secrets
containers:
- name: app
image: app:latest
volumeMounts:
- name: secrets-store-inline
mountPath: "/mnt/secrets-store"
readOnly: true
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
Best Practices
- Enable Secret Auto-Rotation:
yaml
spec:
volumes:
- name: secrets-store-inline
csi:
driver: secrets-store.csi.k8s.io
volumeAttributes:
secretProviderClass: "aws-secrets"
refreshInterval: "1h" # Adjust as needed
Use Pod Identity:
- AWS: Use IAM Roles for Service Accounts (IRSA)
- Azure: Use Managed Identities
- GCP: Use Workload Identity
Implement Health Checks:
yaml
spec:
containers:
- name: app
livenessProbe:
exec:
command:
- cat
- /mnt/secrets-store/username
initialDelaySeconds: 5
periodSeconds: 30
Troubleshooting
- Check provider pods:
bash
kubectl get pods -n kube-system -l app=secrets-store-csi-driver
- View provider logs:
bash
kubectl logs -n kube-system -l app=secrets-store-csi-driver
- Verify SecretProviderClass:
bash
kubectl describe secretproviderclass aws-secrets
- Check mounted secrets:
bash
kubectl exec -it app -- ls -l /mnt/secrets-store
Comparison: ESO vs CSI Driver
External Secrets Operator
- Pros:
- Kubernetes-native secret management
- Automatic secret rotation
- Support for multiple providers
- No pod restarts needed for updates
- Cons:
- Additional operator to maintain
- Requires CRDs
Secrets Store CSI Driver
- Pros:
- Direct volume mounting
- Native integration with cloud providers
- Support for certificates
- Cons:
- Requires pod restarts for updates
- More complex setup
- Limited to volume mounts
Security Best Practices
Secret Rotation
- Enable automatic rotation
- Set appropriate refresh intervals
- Monitor rotation status
Access Control
- Use RBAC strictly
- Implement least privilege
- Regular access reviews
Monitoring
- Set up alerts for failures
- Monitor secret access
- Audit logging
Backup and Recovery
- Regular backups of configurations
- Document recovery procedures
- Test restoration process