Sawabona - Secrets Management Guide¶
Version: 2.0 Last Updated: 2026-01-28 Architecture: Single-Tenant Rust
Introduction¶
Secrets management is critical for production security. Sawabona implements a tiered approach supporting three deployment scenarios:
- Development: Environment variables with
.envfiles - Self-Hosted: Docker secrets with encrypted files
- Production: AWS Secrets Manager or Azure Key Vault with automatic rotation
Compliance Considerations¶
- SOC 2: Requires encryption at rest and audit logging
- PCI-DSS: Requires secure storage of payment provider credentials
- GDPR: Requires encryption of sensitive data
Secrets Inventory¶
Core Secrets¶
| Secret | Purpose | Rotation | Required For |
|---|---|---|---|
| JWT_SECRET | Authentication tokens | 90 days | All deployments |
| ADMIN_API_KEY | Admin operations | 90 days | All deployments |
| SAWABONA_ENCRYPTION_KEY | Data encryption (AES-256-GCM) | 90 days | All deployments |
| DATABASE_URL | PostgreSQL connection | 90 days | All deployments |
Note: SAWABONA_ENCRYPTION_KEY accepts both standard and URL-safe base64 encoding. It must decode to exactly 32 bytes.
Payment Provider Secrets¶
| Secret | Provider | Purpose | Rotation | Required For |
|---|---|---|---|---|
| SAWABONA_STRIPE_SECRET_KEY | Stripe | Payment processing | As needed | Stripe integration |
| SAWABONA_STRIPE_WEBHOOK_SECRET | Stripe | Webhook verification | As needed | Stripe webhooks |
| ADYEN_API_KEY | Adyen | Payment processing | As needed | Adyen integration |
| ADYEN_WEBHOOK_SECRET | Adyen | Webhook verification | As needed | Adyen webhooks |
| FLUTTERWAVE_SECRET_KEY | Flutterwave | Payment processing | As needed | Flutterwave integration |
| PAYSTACK_SECRET_KEY | PayStack | Payment processing | As needed | PayStack integration |
| MERCADOPAGO_ACCESS_TOKEN | MercadoPago | Payment processing | As needed | MercadoPago integration |
| PAGSEGURO_TOKEN | PagSeguro | Payment processing | As needed | PagSeguro integration |
| BRAINTREE_PRIVATE_KEY | Braintree | Payment processing | As needed | Braintree integration |
Important: Payment provider API keys are encrypted at rest using AES-256-GCM before storage in the database. For Stripe, the API key is auto-seeded from SAWABONA_STRIPE_SECRET_KEY on startup (encrypted with SAWABONA_ENCRYPTION_KEY).
Backend Comparison¶
| Feature | Env Vars | Docker Secrets | AWS Secrets Manager | Azure Key Vault |
|---|---|---|---|---|
| Cost | Free | Free | $0.40/secret/month | $0.03/10k ops |
| Rotation | Manual | Manual | Automatic | Automatic |
| Audit Logs | No | No | Yes | Yes |
| Encryption at Rest | No | No | Yes (KMS) | Yes (HSM) |
| Complexity | Low | Medium | High | High |
Setup Instructions¶
Environment Variables (Development)¶
# Copy template
cp .env.example .env
# Edit with your values
nano .env
# Validate
make validate-secrets
Docker Secrets (VPS)¶
# Create secrets directory
mkdir -p secrets
# Generate secrets
openssl rand -base64 64 > secrets/jwt_secret.txt
openssl rand -base64 32 > secrets/admin_api_key.txt
openssl rand -base64 32 > secrets/postgres_encryption_key.txt
# Set permissions
chmod 600 secrets/*
# Deploy
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
AWS Secrets Manager (Production)¶
# Create secret
aws secretsmanager create-secret \
--name sawabona/production/secrets \
--secret-string '{
"jwt_secret": "...",
"admin_api_key": "...",
"stripe_secret_key": "..."
}' \
--region us-east-1
# Configure IAM role for ECS/EC2
# Attach policy: SecretsManagerReadSecretPolicy
# Set environment variables
export SAWABONA_SECRETS_BACKEND=aws_secrets_manager
export AWS_SECRETS_MANAGER_SECRET_NAME=sawabona/production/secrets
export AWS_SECRETS_MANAGER_REGION=us-east-1
Azure Key Vault (Production)¶
# Create Key Vault
az keyvault create \
--name sawabona-vault \
--resource-group sawabona-rg
# Add secrets
az keyvault secret set \
--vault-name sawabona-vault \
--name jwt-secret \
--value "..."
# Configure managed identity
# Assign role: Key Vault Secrets User
# Set environment variables
export SAWABONA_SECRETS_BACKEND=azure_keyvault
export AZURE_KEYVAULT_URL=https://sawabona-vault.vault.azure.net/
Rotation Procedures¶
JWT Secret Rotation¶
# Generate new secret
NEW_SECRET=$(openssl rand -base64 64)
# Rotate (with grace period)
sawabona rotate-jwt-secret --new-secret $NEW_SECRET
# Verify rotation
sawabona verify-jwt-rotation
Payment Provider Key Rotation¶
Coordinate with payment provider:
- Stripe: Rotate in Stripe Dashboard
- PayStack: Rotate in PayStack Dashboard
- Others: Follow provider's rotation procedure
Database Encryption Key Rotation¶
Complex operation requiring downtime:
# 1. Backup database
pg_dump sawabona > backup.sql
# 2. Rotate key (requires application restart)
# 3. Re-encrypt data
# 4. Verify encryption
# 5. Restore if needed
psql sawabona < backup.sql
Security Best Practices¶
- Never log secrets - Sanitize logs to remove sensitive data
- Use separate secrets per environment - Dev, staging, production
- Implement least-privilege access - Restrict who can access secrets
- Monitor secret access - Enable audit logging
- Rotate regularly - Every 90 days for long-lived secrets
- Incident response - Have a plan for leaked secrets
Troubleshooting¶
Secrets not loading¶
# Check environment variables
env | grep SAWABONA
# Validate secrets file
python scripts/validate_secrets.py --env-file .env
# Check Docker secrets
docker exec sawabona-server cat /run/secrets/jwt_secret
Encryption errors¶
# Verify pgcrypto is enabled
psql -c "CREATE EXTENSION pgcrypto;"
# Check encryption key
echo $POSTGRES_ENCRYPTION_KEY | wc -c # Should be 44+ chars