| name | secrets-management |
| description | Implement secrets management with HashiCorp Vault, AWS Secrets Manager, or Kubernetes Secrets for secure credential storage and rotation. |
Secrets Management
Overview
Deploy and configure secure secrets management systems to store, rotate, and audit access to sensitive credentials, API keys, and certificates across your infrastructure.
When to Use
- Database credentials management
- API key and token storage
- Certificate management
- SSH key distribution
- Credential rotation automation
- Audit and compliance logging
- Multi-environment secrets
- Encryption key management
Implementation Examples
1. HashiCorp Vault Setup
# vault-config.hcl
storage "raft" {
path = "/vault/data"
node_id = "node1"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/vault/config/vault.crt"
tls_key_file = "/vault/config/vault.key"
}
api_addr = "https://0.0.0.0:8200"
cluster_addr = "https://0.0.0.0:8201"
ui = true
2. Vault Kubernetes Integration
# vault-kubernetes.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-auth
namespace: vault
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: vault-auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault-auth
namespace: vault
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: vault
namespace: vault
spec:
replicas: 3
serviceName: vault
selector:
matchLabels:
app: vault
template:
metadata:
labels:
app: vault
spec:
serviceAccountName: vault-auth
containers:
- name: vault
image: vault:1.15.0
args:
- "server"
- "-config=/vault/config/vault.hcl"
ports:
- containerPort: 8200
name: api
- containerPort: 8201
name: cluster
securityContext:
runAsNonRoot: true
runAsUser: 100
capabilities:
add:
- IPC_LOCK
env:
- name: VAULT_CLUSTER_ADDR
value: "https://127.0.0.1:8201"
- name: VAULT_API_ADDR
value: "https://127.0.0.1:8200"
- name: VAULT_SKIP_VERIFY
value: "false"
volumeMounts:
- name: vault-config
mountPath: /vault/config
- name: vault-data
mountPath: /vault/data
- name: vault-logs
mountPath: /vault/logs
livenessProbe:
httpGet:
path: /v1/sys/health
port: 8200
scheme: HTTPS
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /v1/sys/health
port: 8200
scheme: HTTPS
initialDelaySeconds: 30
periodSeconds: 5
volumes:
- name: vault-config
configMap:
name: vault-config
- name: vault-logs
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: vault-data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: vault
namespace: vault
spec:
clusterIP: None
ports:
- port: 8200
targetPort: 8200
name: api
- port: 8201
targetPort: 8201
name: cluster
selector:
app: vault
---
apiVersion: v1
kind: ConfigMap
metadata:
name: vault-config
namespace: vault
data:
vault.hcl: |
storage "raft" {
path = "/vault/data"
node_id = "node1"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/vault/config/vault.crt"
tls_key_file = "/vault/config/vault.key"
}
api_addr = "https://vault:8200"
cluster_addr = "https://vault:8201"
ui = true
3. Vault Secret Configuration
#!/bin/bash
# vault-setup.sh - Configure Vault for applications
set -euo pipefail
VAULT_ADDR="https://vault:8200"
VAULT_TOKEN="${VAULT_TOKEN}"
export VAULT_ADDR
export VAULT_TOKEN
echo "Setting up Vault secrets..."
# Enable secret engines
vault secrets enable -version=2 kv
vault secrets enable -path=database database
# Create database credentials
vault write database/config/mydb \
plugin_name=postgresql-database-plugin \
allowed_roles="readonly,readwrite" \
connection_url="postgresql://{{username}}:{{password}}@postgres:5432/mydb" \
username="vault_admin" \
password="vault_password"
# Create database roles
vault write database/roles/readonly \
db_name=mydb \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
revocation_statements="DROP ROLE IF EXISTS \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
# Create API secrets
vault kv put secret/api/keys \
github_token="ghp_xxxxxxxxxxx" \
aws_access_key="AKIAIOSFODNN7EXAMPLE" \
aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
slack_webhook="https://hooks.slack.com/services/..."
# Create TLS certificates
vault write -f pki/root/generate/internal \
common_name="my-root-ca" \
ttl="87600h"
vault write pki/roles/my-domain \
allowed_domains="*.myapp.com,myapp.com" \
allow_subdomains=true \
max_ttl="720h"
# Setup auto-unseal
vault write sys/seal/migrate/start \
migrate_from_seal_type="shamir"
echo "Vault setup completed"
4. AWS Secrets Manager Configuration
# aws-secrets-manager.py
import boto3
import json
from datetime import datetime
class SecretsManager:
def __init__(self, region='us-east-1'):
self.client = boto3.client('secretsmanager', region_name=region)
def create_secret(self, name, secret_value, tags=None):
"""Create a new secret"""
try:
response = self.client.create_secret(
Name=name,
SecretString=json.dumps(secret_value),
Tags=tags or []
)
return response['ARN']
except Exception as e:
print(f"Error creating secret: {e}")
raise
def get_secret(self, name):
"""Retrieve a secret"""
try:
response = self.client.get_secret_value(SecretId=name)
return json.loads(response['SecretString'])
except Exception as e:
print(f"Error retrieving secret: {e}")
raise
def update_secret(self, name, secret_value):
"""Update a secret"""
try:
response = self.client.update_secret(
SecretId=name,
SecretString=json.dumps(secret_value)
)
return response['ARN']
except Exception as e:
print(f"Error updating secret: {e}")
raise
def rotate_secret(self, name, rotation_rules):
"""Enable automatic rotation"""
try:
self.client.rotate_secret(
SecretId=name,
RotationRules=rotation_rules
)
except Exception as e:
print(f"Error rotating secret: {e}")
raise
def list_secrets(self):
"""List all secrets"""
try:
response = self.client.list_secrets()
return response['SecretList']
except Exception as e:
print(f"Error listing secrets: {e}")
raise
def delete_secret(self, name, recovery_days=30):
"""Delete a secret with recovery window"""
try:
response = self.client.delete_secret(
SecretId=name,
RecoveryWindowInDays=recovery_days
)
return response
except Exception as e:
print(f"Error deleting secret: {e}")
raise
# Usage
if __name__ == '__main__':
manager = SecretsManager()
# Create database credentials secret
db_creds = {
'username': 'admin',
'password': 'SecurePassword123!',
'host': 'postgres.example.com',
'port': 5432,
'dbname': 'myapp'
}
secret_arn = manager.create_secret(
'prod/database/credentials',
db_creds,
tags=[
{'Key': 'Environment', 'Value': 'production'},
{'Key': 'Service', 'Value': 'myapp'}
]
)
print(f"Secret created: {secret_arn}")
# Setup rotation
manager.rotate_secret(
'prod/database/credentials',
{'AutomaticallyAfterDays': 30}
)
# Retrieve secret
retrieved = manager.get_secret('prod/database/credentials')
print(f"Retrieved secret: {retrieved}")
5. Kubernetes Secrets
# kubernetes-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-credentials
namespace: production
type: Opaque
stringData:
database_url: "postgresql://user:pass@postgres:5432/myapp"
api_key: "sk_live_xxxxxxxxxxxxxx"
jwt_secret: "your-jwt-secret-key"
---
apiVersion: v1
kind: Secret
metadata:
name: docker-registry
namespace: production
type: kubernetes.io/dockercfg
data:
.dockercfg: <base64-encoded-dockerconfig>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
# Use external secrets operator
serviceAccountName: myapp
containers:
- name: app
image: myapp:latest
env:
# From Kubernetes secret
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-credentials
key: database_url
# From mounted secret
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-credentials
key: api_key
volumeMounts:
- name: secrets
mountPath: /app/secrets
readOnly: true
volumes:
- name: secrets
secret:
secretName: app-credentials
defaultMode: 0400
---
# External Secrets Operator
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secret-store
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secret-store
kind: SecretStore
target:
name: app-external-secret
creationPolicy: Owner
data:
- secretKey: database_url
remoteRef:
key: prod/database/url
- secretKey: api_key
remoteRef:
key: prod/api/key
Best Practices
✅ DO
- Rotate secrets regularly
- Use strong encryption
- Implement access controls
- Audit secret access
- Use managed services
- Implement secret versioning
- Encrypt secrets in transit
- Use separate secrets per environment
❌ DON'T
- Store secrets in code
- Use weak encryption
- Share secrets via email/chat
- Commit secrets to version control
- Use single master password
- Log secret values
- Hardcode credentials
- Disable rotation