428 lines
11 KiB
Markdown
428 lines
11 KiB
Markdown
# Secret Management Guide
|
|||
|
|
|
||
|
|
This document describes best practices for managing secrets and sensitive configuration in the CAATSM application.
|
||
|
|
|
||
|
|
## Current Approach
|
||
|
|
|
||
|
|
The application currently supports secrets via environment variables with the `CAATSM_` prefix:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
export CAATSM_POSTGRES_URL="postgres://user:password@localhost:5432/aviation"
|
||
|
|
export CAATSM_NATS_AUTH_TOKEN="your-token"
|
||
|
|
export CAATSM_NATS_AUTH_PASSWORD="secure-password"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Security considerations:**
|
||
|
|
- Environment variables are visible to all processes on the system
|
||
|
|
- Secrets may be logged in process lists or shell history
|
||
|
|
- No automatic rotation or expiration
|
||
|
|
- Manual management required
|
||
|
|
|
||
|
|
## Recommended Secret Management Systems
|
||
|
|
|
||
|
|
For production deployments, use a dedicated secret management system:
|
||
|
|
|
||
|
|
### Option 1: HashiCorp Vault (Recommended)
|
||
|
|
|
||
|
|
[Vault](https://www.vaultproject.io/) provides secure secret storage with dynamic secrets, encryption, and access control.
|
||
|
|
|
||
|
|
#### Setup
|
||
|
|
|
||
|
|
1. **Install Vault:**
|
||
|
|
```bash
|
||
|
|
# Download and install Vault
|
||
|
|
wget https://releases.hashicorp.com/vault/1.15.0/vault_1.15.0_linux_amd64.zip
|
||
|
|
unzip vault_1.15.0_linux_amd64.zip
|
||
|
|
sudo mv vault /usr/local/bin/
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Start Vault (dev mode for testing):**
|
||
|
|
```bash
|
||
|
|
vault server -dev
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Store secrets:**
|
||
|
|
```bash
|
||
|
|
export VAULT_ADDR='http://127.0.0.1:8200'
|
||
|
|
vault kv put secret/caatsm \
|
||
|
|
postgres_url="postgres://user:pass@db:5432/aviation" \
|
||
|
|
nats_token="your-token" \
|
||
|
|
nats_password="secure-password"
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Integration
|
||
|
|
|
||
|
|
Create a wrapper script or init container to fetch secrets from Vault:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
#!/bin/bash
|
||
|
|
# fetch-secrets.sh
|
||
|
|
|
||
|
|
export VAULT_ADDR="${VAULT_ADDR:-http://vault:8200}"
|
||
|
|
export VAULT_TOKEN="${VAULT_TOKEN}"
|
||
|
|
|
||
|
|
# Fetch secrets from Vault
|
||
|
|
vault kv get -format=json secret/caatsm | jq -r '.data.data | to_entries | .[] | "export CAATSM_\(.key | ascii_upcase | gsub("-"; "_"))=\(.value)"' > /tmp/secrets.env
|
||
|
|
|
||
|
|
# Source secrets
|
||
|
|
source /tmp/secrets.env
|
||
|
|
|
||
|
|
# Start application
|
||
|
|
exec ./bin/receiver listen
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Kubernetes Integration
|
||
|
|
|
||
|
|
Use Vault Agent Sidecar or Vault Secrets Operator:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
apiVersion: v1
|
||
|
|
kind: Pod
|
||
|
|
metadata:
|
||
|
|
name: caatsm-receiver
|
||
|
|
spec:
|
||
|
|
containers:
|
||
|
|
- name: vault-agent
|
||
|
|
image: vault:latest
|
||
|
|
command: ["/bin/sh", "-c"]
|
||
|
|
args:
|
||
|
|
- |
|
||
|
|
vault agent -config=/vault/config/agent.hcl
|
||
|
|
- name: caatsm-receiver
|
||
|
|
image: caatsm/receiver:latest
|
||
|
|
envFrom:
|
||
|
|
- secretRef:
|
||
|
|
name: caatsm-secrets
|
||
|
|
```
|
||
|
|
|
||
|
|
### Option 2: AWS Secrets Manager
|
||
|
|
|
||
|
|
For AWS deployments, use [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) for centralized secret management.
|
||
|
|
|
||
|
|
#### Setup
|
||
|
|
|
||
|
|
1. **Store secrets:**
|
||
|
|
```bash
|
||
|
|
aws secretsmanager create-secret \
|
||
|
|
--name caatsm/production \
|
||
|
|
--secret-string '{
|
||
|
|
"postgres_url": "postgres://user:pass@db:5432/aviation",
|
||
|
|
"nats_token": "your-token",
|
||
|
|
"nats_password": "secure-password"
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Retrieve secrets:**
|
||
|
|
```bash
|
||
|
|
aws secretsmanager get-secret-value \
|
||
|
|
--secret-id caatsm/production \
|
||
|
|
--query SecretString \
|
||
|
|
--output text | jq -r 'to_entries | .[] | "export CAATSM_\(.key | ascii_upcase | gsub("-"; "_"))=\(.value)"'
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Integration
|
||
|
|
|
||
|
|
Use AWS SDK or CLI in init container:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
#!/bin/bash
|
||
|
|
# fetch-aws-secrets.sh
|
||
|
|
|
||
|
|
SECRET_JSON=$(aws secretsmanager get-secret-value \
|
||
|
|
--secret-id caatsm/production \
|
||
|
|
--query SecretString \
|
||
|
|
--output text)
|
||
|
|
|
||
|
|
echo "$SECRET_JSON" | jq -r 'to_entries | .[] | "export CAATSM_\(.key | ascii_upcase | gsub("-"; "_"))=\(.value)"' > /tmp/secrets.env
|
||
|
|
|
||
|
|
source /tmp/secrets.env
|
||
|
|
exec ./bin/receiver listen
|
||
|
|
```
|
||
|
|
|
||
|
|
### Option 3: Kubernetes Secrets
|
||
|
|
|
||
|
|
For Kubernetes deployments, use [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/).
|
||
|
|
|
||
|
|
#### Setup
|
||
|
|
|
||
|
|
1. **Create secret:**
|
||
|
|
```bash
|
||
|
|
kubectl create secret generic caatsm-secrets \
|
||
|
|
--from-literal=postgres-url="postgres://user:pass@db:5432/aviation" \
|
||
|
|
--from-literal=nats-token="your-token" \
|
||
|
|
--from-literal=nats-password="secure-password"
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Use in deployment:**
|
||
|
|
```yaml
|
||
|
|
apiVersion: apps/v1
|
||
|
|
kind: Deployment
|
||
|
|
metadata:
|
||
|
|
name: caatsm-receiver
|
||
|
|
spec:
|
||
|
|
template:
|
||
|
|
spec:
|
||
|
|
containers:
|
||
|
|
- name: receiver
|
||
|
|
image: caatsm/receiver:latest
|
||
|
|
env:
|
||
|
|
- name: CAATSM_POSTGRES_URL
|
||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: caatsm-secrets
|
||
|
|
key: postgres-url
|
||
|
|
- name: CAATSM_NATS_AUTH_TOKEN
|
||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: caatsm-secrets
|
||
|
|
key: nats-token
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Best Practices
|
||
|
|
|
||
|
|
- **Encrypt at rest**: Enable encryption for etcd (Kubernetes backend)
|
||
|
|
- **RBAC**: Restrict access to secrets using Role-Based Access Control
|
||
|
|
- **External Secrets Operator**: Use [External Secrets Operator](https://external-secrets.io/) for integration with external secret stores
|
||
|
|
|
||
|
|
### Option 4: Docker Secrets
|
||
|
|
|
||
|
|
For Docker Swarm deployments, use [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/).
|
||
|
|
|
||
|
|
#### Setup
|
||
|
|
|
||
|
|
1. **Create secret:**
|
||
|
|
```bash
|
||
|
|
echo "your-secret-value" | docker secret create caatsm_nats_token -
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Use in service:**
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
receiver:
|
||
|
|
image: caatsm/receiver:latest
|
||
|
|
secrets:
|
||
|
|
- caatsm_nats_token
|
||
|
|
environment:
|
||
|
|
- CAATSM_NATS_AUTH_TOKEN_FILE=/run/secrets/caatsm_nats_token
|
||
|
|
```
|
||
|
|
|
||
|
|
## Secret Rotation
|
||
|
|
|
||
|
|
### Manual Rotation
|
||
|
|
|
||
|
|
1. **Update secret** in secret management system
|
||
|
|
2. **Restart application** to pick up new secret
|
||
|
|
3. **Verify** application is working correctly
|
||
|
|
4. **Remove old secret** after verification
|
||
|
|
|
||
|
|
### Automated Rotation
|
||
|
|
|
||
|
|
For AWS Secrets Manager, enable automatic rotation:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
aws secretsmanager rotate-secret \
|
||
|
|
--secret-id caatsm/production \
|
||
|
|
--rotation-lambda-arn arn:aws:lambda:region:account:function:rotate-secret
|
||
|
|
```
|
||
|
|
|
||
|
|
For Vault, use dynamic secrets or scheduled rotation policies.
|
||
|
|
|
||
|
|
## Security Best Practices
|
||
|
|
|
||
|
|
### 1. Principle of Least Privilege
|
||
|
|
|
||
|
|
- **Minimal access**: Grant only necessary permissions
|
||
|
|
- **Service accounts**: Use dedicated service accounts for applications
|
||
|
|
- **Secret scoping**: Limit secrets to specific services/environments
|
||
|
|
|
||
|
|
### 2. Encryption
|
||
|
|
|
||
|
|
- **Encryption at rest**: Ensure secrets are encrypted in storage
|
||
|
|
- **Encryption in transit**: Use TLS for secret retrieval
|
||
|
|
- **Key management**: Use proper key management (HSM, KMS, etc.)
|
||
|
|
|
||
|
|
### 3. Audit and Monitoring
|
||
|
|
|
||
|
|
- **Audit logs**: Enable audit logging for secret access
|
||
|
|
- **Monitoring**: Monitor secret access patterns
|
||
|
|
- **Alerts**: Set up alerts for unusual access patterns
|
||
|
|
|
||
|
|
### 4. Secret Lifecycle
|
||
|
|
|
||
|
|
- **Rotation**: Rotate secrets regularly (e.g., every 90 days)
|
||
|
|
- **Expiration**: Set expiration dates for secrets
|
||
|
|
- **Revocation**: Have a process for revoking compromised secrets
|
||
|
|
|
||
|
|
### 5. Development vs Production
|
||
|
|
|
||
|
|
- **Separate stores**: Use different secret stores for dev/staging/prod
|
||
|
|
- **No production secrets in code**: Never commit production secrets
|
||
|
|
- **Local development**: Use local secret files or dev vault instance
|
||
|
|
|
||
|
|
## Configuration Examples
|
||
|
|
|
||
|
|
### Environment Variables (Current)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Development
|
||
|
|
export CAATSM_POSTGRES_URL="postgres://user:pass@localhost:5432/aviation?sslmode=disable"
|
||
|
|
export CAATSM_NATS_URL="nats://localhost:4222"
|
||
|
|
export CAATSM_NATS_AUTH_TOKEN="dev-token"
|
||
|
|
|
||
|
|
# Production (via secret management)
|
||
|
|
# Secrets loaded from Vault/AWS/K8s before application start
|
||
|
|
```
|
||
|
|
|
||
|
|
### Configuration File (Not Recommended for Secrets)
|
||
|
|
|
||
|
|
```toml
|
||
|
|
# config.prod.toml
|
||
|
|
# DO NOT store secrets in config files
|
||
|
|
# Use environment variables or secret management instead
|
||
|
|
|
||
|
|
[postgres]
|
||
|
|
# URL should come from CAATSM_POSTGRES_URL env var
|
||
|
|
url = "" # Empty, will be overridden by env var
|
||
|
|
|
||
|
|
[nats.auth]
|
||
|
|
# Token should come from CAATSM_NATS_AUTH_TOKEN env var
|
||
|
|
token = "" # Empty, will be overridden by env var
|
||
|
|
```
|
||
|
|
|
||
|
|
## Secret Injection Patterns
|
||
|
|
|
||
|
|
### Pattern 1: Init Container (Kubernetes)
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
apiVersion: v1
|
||
|
|
kind: Pod
|
||
|
|
metadata:
|
||
|
|
name: caatsm-receiver
|
||
|
|
spec:
|
||
|
|
initContainers:
|
||
|
|
- name: fetch-secrets
|
||
|
|
image: vault:latest
|
||
|
|
command: ["/bin/sh", "-c"]
|
||
|
|
args:
|
||
|
|
- |
|
||
|
|
vault kv get -format=json secret/caatsm | \
|
||
|
|
jq -r '.data.data | to_entries | .[] | "\(.key | ascii_upcase | gsub("-"; "_"))=\(.value)"' > \
|
||
|
|
/shared/secrets.env
|
||
|
|
volumeMounts:
|
||
|
|
- name: shared-secrets
|
||
|
|
mountPath: /shared
|
||
|
|
containers:
|
||
|
|
- name: receiver
|
||
|
|
image: caatsm/receiver:latest
|
||
|
|
envFrom:
|
||
|
|
- configMapRef:
|
||
|
|
name: caatsm-config
|
||
|
|
env:
|
||
|
|
- name: CAATSM_SECRETS_FILE
|
||
|
|
value: /shared/secrets.env
|
||
|
|
volumeMounts:
|
||
|
|
- name: shared-secrets
|
||
|
|
mountPath: /shared
|
||
|
|
volumes:
|
||
|
|
- name: shared-secrets
|
||
|
|
emptyDir: {}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Pattern 2: Sidecar Container
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
apiVersion: v1
|
||
|
|
kind: Pod
|
||
|
|
metadata:
|
||
|
|
name: caatsm-receiver
|
||
|
|
spec:
|
||
|
|
containers:
|
||
|
|
- name: vault-agent
|
||
|
|
image: vault:latest
|
||
|
|
command: ["vault", "agent", "-config=/vault/config/agent.hcl"]
|
||
|
|
volumeMounts:
|
||
|
|
- name: vault-config
|
||
|
|
mountPath: /vault/config
|
||
|
|
- name: receiver
|
||
|
|
image: caatsm/receiver:latest
|
||
|
|
envFrom:
|
||
|
|
- secretRef:
|
||
|
|
name: caatsm-secrets
|
||
|
|
volumes:
|
||
|
|
- name: vault-config
|
||
|
|
configMap:
|
||
|
|
name: vault-agent-config
|
||
|
|
```
|
||
|
|
|
||
|
|
### Pattern 3: Application-Level Integration
|
||
|
|
|
||
|
|
For applications that need to fetch secrets at runtime:
|
||
|
|
|
||
|
|
```go
|
||
|
|
// Example: Fetch secrets from Vault at startup
|
||
|
|
func loadSecretsFromVault() error {
|
||
|
|
client, err := vault.NewClient(vault.DefaultConfig())
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
secret, err := client.Logical().Read("secret/data/caatsm")
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set environment variables
|
||
|
|
for k, v := range secret.Data["data"].(map[string]interface{}) {
|
||
|
|
os.Setenv("CAATSM_"+strings.ToUpper(k), v.(string))
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Secret Not Found
|
||
|
|
|
||
|
|
**Symptoms:**
|
||
|
|
- Application fails to start
|
||
|
|
- Connection errors to database/NATS
|
||
|
|
|
||
|
|
**Solutions:**
|
||
|
|
- Verify secret exists in secret store
|
||
|
|
- Check secret name/path is correct
|
||
|
|
- Verify application has permissions to access secret
|
||
|
|
- Check secret format (JSON, plain text, etc.)
|
||
|
|
|
||
|
|
### Secret Access Denied
|
||
|
|
|
||
|
|
**Symptoms:**
|
||
|
|
- Authentication errors when fetching secrets
|
||
|
|
- Permission denied errors
|
||
|
|
|
||
|
|
**Solutions:**
|
||
|
|
- Verify IAM roles/service accounts have correct permissions
|
||
|
|
- Check Vault policies or AWS IAM policies
|
||
|
|
- Verify authentication tokens/credentials are valid
|
||
|
|
|
||
|
|
### Secret Rotation Issues
|
||
|
|
|
||
|
|
**Symptoms:**
|
||
|
|
- Application fails after secret rotation
|
||
|
|
- Connection errors after rotation
|
||
|
|
|
||
|
|
**Solutions:**
|
||
|
|
- Implement graceful secret reloading
|
||
|
|
- Use connection pooling with automatic reconnection
|
||
|
|
- Test rotation process in staging first
|
||
|
|
|
||
|
|
## References
|
||
|
|
|
||
|
|
- [HashiCorp Vault Documentation](https://www.vaultproject.io/docs)
|
||
|
|
- [AWS Secrets Manager Documentation](https://docs.aws.amazon.com/secretsmanager/)
|
||
|
|
- [Kubernetes Secrets Documentation](https://kubernetes.io/docs/concepts/configuration/secret/)
|
||
|
|
- [External Secrets Operator](https://external-secrets.io/)
|
||
|
|
- [12-Factor App: Config](https://12factor.net/config)
|
||
|
|
|