Ferrite takes security seriously. This document outlines security best practices and configuration requirements.
All environment variables MUST be explicitly set. The application will NOT start with default or hardcoded credentials.
Required environment variables:
POSTGRES_USER- Database usernamePOSTGRES_PASSWORD- Database password (strong password required)POSTGRES_DB- Database nameJWT_SECRET- JWT signing secret (minimum 32 characters)
-
Never commit
.envfiles to version control- The
.gitignoreis configured to block.envfiles - Only
.env.example(with no real credentials) should be committed
- The
-
Generate strong secrets:
# Generate JWT secret (Linux/Mac) openssl rand -base64 32 # Generate strong password (Linux) pwgen -s 32 1 # Generate password (Windows PowerShell) [System.Web.Security.Membership]::GeneratePassword(32,10)
-
Set restrictive file permissions:
# Linux/Mac chmod 600 .env # Ensure only owner can read/write ls -la .env # Should show: -rw-------
-
Use different credentials per environment:
- Development:
.env.dev - Staging:
.env.staging - Production:
.env.production - Never reuse credentials across environments
- Development:
✅ Non-root user: All containers run as non-root (UID 65532) ✅ Distroless base: Minimal attack surface, no shell or package manager ✅ No new privileges: Containers cannot escalate privileges ✅ Health checks: Automatic monitoring and restart ✅ Network isolation: Services communicate on isolated Docker network
- Set strong, unique
POSTGRES_PASSWORD(32+ characters) - Set strong, unique
JWT_SECRET(32+ characters minimum) - Set strong, unique
PGADMIN_PASSWORDif using PgAdmin - Use different credentials than development
- Enable TLS/SSL for database connections
- Configure firewall rules to restrict access
- Use Docker secrets for sensitive data (Swarm/Kubernetes)
- Enable Docker Content Trust (image signing)
- Regularly update base images and dependencies
- Monitor logs for suspicious activity
- Set up automated backups
- Implement rate limiting at reverse proxy level
- Minimum length: 32 characters (256 bits)
- Recommended length: 64 characters (512 bits)
- Character set: Use base64 or random alphanumeric
- Rotation: Rotate JWT secrets regularly (e.g., every 90 days)
# Good: 32 bytes = 256 bits
openssl rand -base64 32
# Better: 64 bytes = 512 bits
openssl rand -base64 64- Minimum 16 characters
- Mix of uppercase, lowercase, numbers, and special characters
- No dictionary words or common patterns
- Unique per environment
For production, enable SSL/TLS in PostgreSQL:
# docker-compose.yml - Production database
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_INITDB_ARGS: "--ssl=on"Update connection string:
DATABASE_URL=postgres://user:pass@host:5432/db?sslmode=require
Do NOT use .env files in production. Instead, use:
-
Docker Secrets (Docker Swarm):
echo "my_secret" | docker secret create jwt_secret -
-
Kubernetes Secrets:
kubectl create secret generic ferrite-secrets \ --from-literal=jwt-secret='your_secret' \ --from-literal=db-password='your_password'
-
Cloud Provider Secret Managers:
- AWS Secrets Manager
- Azure Key Vault
- Google Cloud Secret Manager
- HashiCorp Vault
-
Environment Variables via Orchestrator:
- Injected at runtime
- Never stored in images or version control
# Check for Rust security advisories
cargo audit
# Update dependencies
cargo update
# Rebuild Docker images regularly
docker-compose build --no-cacheThe project uses:
rust:1.83-bookworm(builder)gcr.io/distroless/cc-debian12:nonroot(runtime)
Update regularly for security patches.
If you discover a security vulnerability, please email the maintainers directly rather than opening a public issue.
This project follows:
- OWASP Application Security Verification Standard (ASVS)
- CIS Docker Benchmark
- Rust Security Working Group guidelines