Secret Management Guide¶
Securely manage secrets in USL applications across different environments and platforms.
Overview¶
USL supports multiple secret management backends: - EnvVars: Environment variables (development only) - Vault: HashiCorp Vault (recommended for production) - AWS Secrets Manager: AWS-native secret storage - Azure Key Vault: Azure-native secret storage - GCP Secret Manager: Google Cloud-native secret storage
Secret Backends¶
Environment Variables (Development)¶
Use for: Local development, testing
Generates standard Kubernetes Secret:
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
database-url: "postgresql://user:pass@localhost:5432/db"
api-key: "dev-api-key"
⚠️ Warning: Never commit secrets to version control!
HashiCorp Vault (Recommended)¶
Use for: Production, multi-cloud, dynamic secrets
Setup Vault¶
-
Install Vault:
-
Initialize and Unseal:
-
Run Setup Script:
This script: - Enables KV secrets engine - Creates application secrets - Configures Kubernetes auth - Sets up database dynamic secrets - Creates access policies
External Secrets Operator¶
Install External Secrets Operator:
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets \
external-secrets/external-secrets \
--namespace external-secrets-system \
--create-namespace
Create SecretStore:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: production
spec:
provider:
vault:
server: "https://vault.example.com:8200"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "my-app"
serviceAccountRef:
name: "my-app"
Generated ExternalSecret:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
spec:
refreshInterval: 15m
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: database-url
remoteRef:
key: secret/data/app/database
property: url
Vault Policies¶
Create policy (vault-policy.hcl):
# Read application secrets
path "secret/data/app/*" {
capabilities = ["read", "list"]
}
# Get dynamic database credentials
path "database/creds/app" {
capabilities = ["read"]
}
# Token operations
path "auth/token/renew-self" {
capabilities = ["update"]
}
Apply policy:
Kubernetes Auth¶
Configure Kubernetes authentication:
vault write auth/kubernetes/role/my-app \
bound_service_account_names=my-app \
bound_service_account_namespaces=production \
policies=app-policy \
ttl=24h
Dynamic Database Credentials¶
Configure database connection:
vault write database/config/mydb \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@postgres:5432/mydb" \
username="vault_admin" \
password="vault_password"
Create role:
vault write database/roles/my-app \
db_name=mydb \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
AWS Secrets Manager¶
Use for: AWS ECS, EKS deployments
Setup AWS Secrets Manager¶
-
Create secrets:
-
IAM Policy:
-
Attach to ECS Task Role:
ECS Task Definition¶
{
"containerDefinitions": [{
"name": "app",
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:us-east-1:ACCOUNT_ID:secret:app/database-url"
},
{
"name": "API_KEY",
"valueFrom": "arn:aws:secretsmanager:us-east-1:ACCOUNT_ID:secret:app/api-key"
}
]
}]
}
Azure Key Vault¶
Use for: Azure Container Apps, AKS deployments
Setup Azure Key Vault¶
-
Create Key Vault:
-
Store secrets:
-
Enable Managed Identity:
-
Grant Access:
Container App Configuration¶
properties:
configuration:
secrets:
- name: database-url
keyVaultUrl: https://app-keyvault.vault.azure.net/secrets/database-url
- name: api-key
keyVaultUrl: https://app-keyvault.vault.azure.net/secrets/api-key
template:
containers:
- name: app
env:
- name: DATABASE_URL
secretRef: database-url
- name: API_KEY
secretRef: api-key
GCP Secret Manager¶
Use for: Cloud Run, GKE deployments
Setup GCP Secret Manager¶
-
Enable API:
-
Create secrets:
-
Grant Access:
Cloud Run Configuration¶
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: database-url
key: latest
- name: API_KEY
valueFrom:
secretKeyRef:
name: api-key
key: latest
Secret Rotation¶
Automatic Rotation with External Secrets¶
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
spec:
refreshInterval: 1h # Sync every hour
secretStoreRef:
name: vault-backend
target:
name: app-secrets
creationPolicy: Owner
Manual Rotation¶
# Update secret in Vault
vault kv put secret/app/database url="new-connection-string"
# Force sync (if needed)
kubectl annotate externalsecret app-secrets \
force-sync=$(date +%s) \
--overwrite
Rolling Restart on Secret Change¶
Use Reloader to automatically restart pods:
helm repo add stakater https://stakater.github.io/stakater-charts
helm install reloader stakater/reloader
Add annotation to Deployment:
Best Practices¶
1. Never Commit Secrets¶
✅ Good: Use secret management systems ❌ Bad: Hardcode in code or config files
2. Use Separate Secrets per Environment¶
vault kv put secret/app/dev database_url="..."
vault kv put secret/app/staging database_url="..."
vault kv put secret/app/prod database_url="..."
3. Principle of Least Privilege¶
Grant minimal permissions:
4. Short-Lived Credentials¶
Use dynamic secrets:
5. Audit Secret Access¶
Enable Vault audit logging:
6. Encrypt at Rest¶
Ensure secrets are encrypted: - Kubernetes: Enable encryption at rest - Cloud providers: Use KMS encryption
Security Checklist¶
- Secrets not in version control
- Separate secrets per environment
- Minimal IAM permissions
- Encryption at rest enabled
- Encryption in transit (TLS)
- Secret rotation enabled
- Audit logging enabled
- Access reviews regular
- Backup secrets securely
- Incident response plan
Troubleshooting¶
ExternalSecret Not Syncing¶
kubectl describe externalsecret app-secrets -n production
kubectl logs -l app=external-secrets -n external-secrets-system
Vault Connection Issues¶
# Test connection
kubectl run vault-test --rm -it --image=vault:latest -- \
vault kv get -address=http://vault:8200 secret/app/database
# Check auth
kubectl exec -it pod-name -- \
cat /var/run/secrets/kubernetes.io/serviceaccount/token
AWS Secrets Manager Access Denied¶
# Check IAM role
aws sts get-caller-identity
# Test secret access
aws secretsmanager get-secret-value \
--secret-id app/database-url \
--region us-east-1
Next Steps¶
- Kubernetes Guide - K8s deployment
- Helm Guide - Helm charts
- Cloud Providers - Cloud-specific features
- Monitoring - Security monitoring