Secrets Management
LEVEL 0
The Problem
Your application needs secrets:
- Database passwords
- API keys
- OAuth tokens
- TLS certificates
Where do you put them?
❌ Bad ideas:
# Hardcoded in Dockerfile
ENV DB_PASSWORD=supersecret123
Anyone with the image can see it:
docker image inspect myapp | grep DB_PASSWORD
# In docker-compose.yml committed to git
environment:
API_KEY: sk_live_abc123xyz
Now it’s in version control history forever.
# In environment variable in shell
export SECRET_KEY=my-secret
docker run -e SECRET_KEY myapp
Shows up in docker inspect and process list.
Secrets need special handling.
LEVEL 1
The Concept — The Safe Deposit Box
The Concept
Imagine storing valuables.
Bad approach: Leave them out
- Keys on your desk
- Passwords on sticky notes
- Credit cards in plain sight
Anyone who enters your room can steal them.
Good approach: Safe deposit box
- Store valuables in a secure vault
- Access only when needed
- Retrieve, use, return
- Access logged and audited
- Separate from regular storage
Secrets management systems are safe deposit boxes for credentials.
LEVEL 2
The Mechanics — Secrets Management Approaches
The Mechanics
Approach 1: Environment variables (basic, not recommended for production)
services:
app:
image: myapp
environment:
DB_PASSWORD: ${DB_PASSWORD} # From .env file
.env file (not in git):
DB_PASSWORD=supersecret
Pros: Simple
Cons: Visible in docker inspect, process list, logs
Approach 2: Docker Secrets (Swarm mode)
# Create secret
echo "supersecret" | docker secret create db_password -
# Use in service
docker service create \
--secret db_password \
myapp
In compose (Swarm mode):
version: '3.9'
services:
app:
image: myapp
secrets:
- db_password
secrets:
db_password:
external: true
Inside container, secret is mounted as file:
cat /run/secrets/db_password
# supersecret
Approach 3: External secrets managers
HashiCorp Vault:
# app.py
import hvac
client = hvac.Client(url='http://vault:8200')
client.token = os.environ['VAULT_TOKEN']
secret = client.secrets.kv.v2.read_secret_version(path='db/password')
db_password = secret['data']['data']['password']
AWS Secrets Manager:
import boto3
client = boto3.client('secretsmanager')
secret = client.get_secret_value(SecretId='myapp/db/password')
db_password = secret['SecretString']
Approach 4: Config files mounted as volumes
services:
app:
image: myapp
volumes:
- ./secrets/db-password.txt:/run/secrets/db-password:ro
.gitignore:
secrets/
Better than environment variables, but still stored on disk.
LEVEL 3
Best Practices for Secrets
1. Never hardcode secrets
❌ Don’t:
ENV API_KEY=abc123
✅ Do:
# App reads from file or environment at runtime
2. Never commit secrets to git
Use .gitignore:
.env
.env.local
secrets/
*.key
*.pem
credentials.json
3. Rotate secrets regularly
Change passwords, keys periodically. Automate rotation.
4. Use different secrets per environment
secrets/
development/
db-password.txt
staging/
db-password.txt
production/
db-password.txt
5. Limit secret access
Only containers that need a secret should have access.
services:
web:
# Doesn't need DB password
image: nginx
app:
# Needs DB password
image: myapp
secrets:
- db_password
db:
# Has its own password
image: postgres
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
6. Audit secret access
Log when secrets are retrieved. Monitor for unusual access patterns.
LEVEL 4
Secrets in CI/CD
GitHub Actions:
Store secrets in repository settings:
# .github/workflows/deploy.yml
- name: Deploy
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
API_KEY: ${{ secrets.API_KEY }}
run: |
docker run -e DB_PASSWORD -e API_KEY myapp
GitLab CI:
# .gitlab-ci.yml
deploy:
script:
- docker run -e DB_PASSWORD=$DB_PASSWORD myapp
variables:
DB_PASSWORD:
vault: production/db/password@secret
Encrypted files in git (git-crypt, SOPS)
# Encrypt secrets file
sops -e secrets.yaml > secrets.enc.yaml
# Commit encrypted file
git add secrets.enc.yaml
# Decrypt in CI
sops -d secrets.enc.yaml > secrets.yaml
LEVEL 5
Detecting Exposed Secrets
Scan for secrets in images:
# Trivy can detect secrets
trivy image --security-checks secret myapp:latest
Scan git history:
# TruffleHog
trufflehog git https://github.com/user/repo
# Gitleaks
gitleaks detect --source .
Pre-commit hooks:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/Yelp/detect-secrets
hooks:
- id: detect-secrets
Prevents committing secrets to git.