The application runs as a non-root user (appuser) for security:
RUN useradd --create-home --shell /bin/bash appuser
USER appuserBenefits:
- If container is compromised, attacker has limited privileges
- Prevents privilege escalation attacks
- Follows security best practices
docker-compose.ymlcontains hardcoded development passwords (acceptable for local development only)postgres_passwordandpgadmin_passwordare placeholder credentials
- Production uses environment variables from
.env.production .env.productionis in.gitignoreto prevent committing secrets- Strong secrets must be generated with:
openssl rand -base64 32
Best Practices:
- Never commit
.env.productionto version control - Use different passwords for development and production
- Rotate secrets regularly
- Use strong, randomly generated secrets (32+ bytes)
- Uses official
python:3.13-slimimage - Slim variants reduce attack surface by excluding unnecessary packages
- Minimal system packages installed (
libpq5,curl) aptcache cleaned after installation- Multi-stage build reduces final image size
Internet → Nginx (port 80/443) → App (port 8000) → PostgreSQL (internal only)
Security Features:
- Nginx acts as reverse proxy, hiding app details
- PostgreSQL only accessible within Docker network
- App port 8000 not exposed publicly
- SSL/TLS encryption for all traffic
- Certificates stored in
./ssl/directory .gitignoreprevents committing certificates- Obtain certificates using Certbot:
certbot certonly --standalone -d yourdomain.com
Required Files:
./ssl/fullchain.pem- Certificate chain./ssl/privkey.pem- Private key
Nginx Configuration:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...';
ssl_prefer_server_ciphers off;Nginx implements security best practice headers:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;Purpose:
- HSTS: Forces HTTPS connections
- X-Frame-Options: Prevents clickjacking
- X-Content-Type-Options: Prevents MIME sniffing
- X-XSS-Protection: XSS protection
- Referrer-Policy: Controls referrer information
API endpoints are rate-limited to prevent abuse:
limit_req_zone $binary_remote_addr zone=api:10m;
limit_req zone=api burst=20 nodelay;Configuration:
- 10MB shared memory for tracking
- Burst of 20 requests allowed
- Rate limits defined per endpoint
All services have health checks:
App Container:
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1Database Container:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U comicpile"]
interval: 10s
timeout: 5s
retries: 5Nginx Container:
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3✅ Implemented:
- Non-root user
- Minimal base image (slim)
- Multi-stage build
- Health checks on all containers
- Secrets in environment variables (not code)
- .env.production in .gitignore
- SSL/TLS for all traffic
- Security headers in Nginx
- Rate limiting on API
- No direct database exposure
- Read-only file system where possible
Run vulnerability scans before deployment:
# Scan Docker image
docker scan comic-pile:latest
# Scan base image
docker scan python:3.13-slim
# Check for exposed secrets
git-secrets --scan
# Check for dependencies with known vulnerabilities
# (Requires setup of tools like Snyk or Dependabot)Before deploying to production:
- Generate strong secrets with
openssl rand -base64 32 - Update
.env.productionwith production values - Obtain SSL certificates for domain
- Place certificates in
./ssl/directory - Update
nginx.confserver_name if needed - Run vulnerability scan on Docker image
- Test health checks locally
- Verify no secrets in git history
- Review .gitignore includes all sensitive files
- Backup database before migration
- Test rollback procedure
If secrets are leaked:
- Immediately rotate all secrets
- Revoke and regenerate SSL certificates
- Check audit logs for unauthorized access
- Update
.env.productionwith new secrets - Restart containers with new secrets
If container is compromised:
- Stop affected containers immediately
- Review container logs for indicators of compromise
- Rotate all secrets
- Update to latest base images with security patches
- Rebuild and redeploy containers
- Monitor container logs for suspicious activity
- Set up alerts for health check failures
- Track rate limit violations
- Monitor SSL certificate expiration
- Regular security audits (quarterly)