-
-
Notifications
You must be signed in to change notification settings - Fork 23
Security Configuration
How to secure your Caddy Proxy Manager deployment in production.
- Production Security Requirements
- Password Security
- Session Secret Management
- Rate Limiting
- Container Security
- Network Security
- Certificate Security
- Multi-Instance Deployments
- Production Deployment Checklist
The app refuses to start in production if you haven't set strong credentials. No "admin/admin" in production.
-
SESSION_SECRET
- Minimum 32 characters
- Must be cryptographically random
- Cannot use placeholder values
-
ADMIN_PASSWORD
- Minimum 12 characters
- Must include uppercase letters (A-Z)
- Must include lowercase letters (a-z)
- Must include numbers (0-9)
- Must include special characters (!@#$%^&* etc.)
- Cannot be default "admin"
# Generate secure session secret
export SESSION_SECRET=$(openssl rand -base64 32)
# Set secure admin credentials
export ADMIN_USERNAME="admin"
export ADMIN_PASSWORD="your-strong-password"
# Start containers
docker compose up -dProduction passwords must:
- Be at least 12 characters long
- Contain at least one uppercase letter (A-Z)
- Contain at least one lowercase letter (a-z)
- Contain at least one number (0-9)
- Contain at least one special character (!@#$%^&*)
- Not be the default value "admin"
Validation occurs:
- At application startup (environment variable)
- When changing password via UI
- During user creation (future multi-user feature)
- Passwords are hashed with bcrypt (cost 12)
- Hashes stored in SQLite database
- Original passwords never stored in plain text
- Bcrypt provides salt and key derivation
- Use a password manager to generate and store passwords
- Use unique passwords for each deployment
- Rotate passwords regularly (every 90 days recommended)
- Never share passwords via insecure channels (email, chat, etc.)
- Change default password immediately after first login
The session secret is used for:
- Encrypting session cookies
- Signing JWT tokens
- CSRF protection
- OAuth state parameter signing
- Minimum length: 32 characters
- Uniqueness: Different for each deployment
- Randomness: Cryptographically secure random data
- Persistence: Never change after initial deployment (invalidates all sessions)
Recommended method (OpenSSL):
openssl rand -base64 32Alternative methods:
# Using /dev/urandom
head -c 32 /dev/urandom | base64
# Using Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
# Using Python
python3 -c "import os; import base64; print(base64.b64encode(os.urandom(32)).decode())"When to rotate:
- Suspected compromise
- Security incident
- Employee offboarding
- Compliance requirements
How to rotate:
- Generate new secret
- Update
SESSION_SECRETin.env - Restart containers:
docker compose restart web - All users will be logged out (this is expected)
Warning: Rotating the secret logs out all users immediately.
Prevents brute force attacks by limiting login attempts. Powered by Better Auth's built-in rate limiter.
Default configuration:
- Maximum attempts: 5
- Time window: 60 seconds
How it works:
- All auth requests count against the rate limit
- After 5 requests in 60 seconds, further requests are rejected with HTTP 429
- Counter resets after the window expires
Configure via environment variables:
# Disable rate limiting entirely (not recommended)
AUTH_RATE_LIMIT_ENABLED=false
# Allow more attempts per window
AUTH_RATE_LIMIT_MAX=10
# Longer observation window (120 seconds)
AUTH_RATE_LIMIT_WINDOW=120See Environment Variables Reference for details.
Limitations:
- In-memory storage (not persistent across restarts)
- Not suitable for multi-instance deployments
- Per-instance tracking only
Recommendations:
- Use OAuth for better security
- Monitor failed login attempts in audit logs
- Consider external firewall/WAF for additional protection
Run containers as non-root users for improved security.
Default UIDs/GIDs:
- Web service: 10001:10001
- Caddy service: 10000:10000
Custom user (recommended for development):
# Match your host user
PUID=1000 # id -u
PGID=1000 # id -g
# Rebuild containers
docker compose up --build -dSee Rootless Docker Operation for complete guide.
- Minimal base images (Alpine Linux)
- Regular security updates
- SBOM (Software Bill of Materials) generated
- Build provenance attestation
- Automated vulnerability scanning
Secure data directories:
chmod 700 data caddy-data caddy-configRestrict .env file:
chmod 600 .env
chown root:root .env # If running as root- Containers run in isolated bridge network
- Caddy admin API (port 2019) not exposed externally
- Only web UI port (3000) and HTTP/HTTPS (80/443) exposed
- Secrets not accessible from Caddy container
Exposed ports:
- 80 (HTTP) - Public
- 443 (HTTPS) - Public
- 3000 (Web UI) - Should be behind reverse proxy in production
Internal ports (not exposed):
- 2019 (Caddy Admin API) - Internal only
Recommended: Put web UI behind reverse proxy
Example Caddy configuration:
proxy-ui.example.com {
reverse_proxy localhost:3000
}
Benefits:
- HTTPS for web UI
- Access control
- Rate limiting
- Logging
Recommended firewall rules:
# Allow HTTP/HTTPS
ufw allow 80/tcp
ufw allow 443/tcp
# Block direct access to web UI from internet
# Only allow from local network
ufw allow from 192.168.0.0/16 to any port 3000
# Or allow from specific IPs
ufw allow from 1.2.3.4 to any port 3000- Use Docker network isolation
- Don't expose internal services
- Use VPN for remote access to UI
- Consider Cloudflare Tunnel for secure access
Security Warning: Certificate private keys are stored unencrypted in the SQLite database.
Implications:
- Anyone with database access can extract private keys
- Backup security is critical
- Suitable for internal/development use
- Consider HSM for high-security requirements
Mitigation:
- Encrypt database backups
- Restrict database file permissions (chmod 600)
- Use short-lived certificates
- Rotate certificates regularly
- Use Let's Encrypt for automatic renewal
- Enable Cloudflare DNS-01 for wildcard certificates
- Monitor expiration dates (Caddy handles this automatically)
- Use strong key sizes (2048-bit RSA minimum, 256-bit ECDSA recommended)
- Limit certificate scope (don't use one cert for everything)
- Stored by Caddy in
/datavolume - Encrypted by Caddy
- Backup Caddy data volume for continuity
Caddy Proxy Manager supports a master/slave configuration via Instance Sync.
The master pushes proxy hosts, certificates, access lists, and settings to each slave on every configuration change. User accounts are not synced.
# Master
INSTANCE_MODE=master
INSTANCE_SLAVES='[{"name":"replica","url":"https://replica.example.com","token":"<32-char-token>"}]'
# Slave
INSTANCE_MODE=slave
INSTANCE_SYNC_TOKEN=<32-char-token>See Environment Variables Reference for the full list of INSTANCE_* variables.
Even with instance sync, some limitations apply:
- Rate limiting — In-memory only, not shared across instances
- Session storage — Per-instance; logging into master does not log you into slaves
- Database — SQLite; only one writer at a time per instance
- Caddy API — Each instance manages its own Caddy process
Active-active deployments (load-balanced identical nodes sharing state) are not supported. The master/slave model is one-way push: only the master manages configuration.
For simpler deployments, run one instance with Docker restart policies:
services:
web:
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "..."]
interval: 30s
timeout: 10s
retries: 3- Generate unique SESSION_SECRET (32+ chars)
- Set strong ADMIN_PASSWORD (12+ chars, mixed)
- Set correct BASE_URL for your domain
- Configure ACME email in Settings
- Review and customize rate limiting
- Plan backup strategy
- Document secrets storage location
- Enable HTTPS for web UI (reverse proxy)
- Restrict web UI access (firewall/VPN)
- Set up a DNS provider for DNS-01 (wildcard certs) — see DNS Provider Configuration
- Configure OAuth if using SSO
- Enable audit logging
- Restrict .env file permissions (chmod 600)
- Secure data directory permissions (chmod 700)
- Use rootless containers (PUID/PGID)
- Keep images updated regularly
- Enable automatic security updates
- Monitor security advisories
- Review Docker Compose configuration
- Set up monitoring and alerting
- Configure automated backups
- Test backup restoration
- Document incident response plan
- Plan secret rotation schedule
- Review access logs regularly
- Change admin password from profile
- Test all functionality
- Verify HTTPS certificates
- Test backup and restore
- Monitor logs for errors
- Set up health check monitoring
-
Immediate actions:
- Rotate SESSION_SECRET
- Change ADMIN_PASSWORD
- Review audit logs
- Check for unauthorized changes
-
Investigation:
- Review access logs
- Check database for unauthorized users (future)
- Verify proxy host configurations
- Check certificate changes
-
Recovery:
- Restore from known-good backup
- Re-deploy with new secrets
- Force logout all users
- Notify relevant parties
CPM enforces a three-tier role system:
- Viewer — Dashboard login and forward-auth access only
- User — Same as Viewer (intended for forward auth users)
- Admin — Full access to all features, settings, API, and user management
New users (including OAuth sign-ups) default to user. Only admins can promote users.
Non-admin users see only the welcome page — no stats, traffic data, analytics, audit log, or configuration.
See Feature Guide User Management for details.
- Environment Variables Reference - Security-related variables
- Installation Guide - Secure installation steps
- Rootless Docker Operation - Non-root container security
- OAuth Authentication Setup - SSO security
- Feature Guide User Management - User roles and account management
- Feature Guide Forward Auth - Built-in forward auth portal
Need help? See Troubleshooting or open an issue.