Skip to content

Security Configuration

fuomag9 edited this page Jul 23, 2026 · 7 revisions

Security Configuration

How to secure your Caddy Proxy Manager deployment in production.

Table of Contents

  1. Production Security Requirements
  2. Password Security
  3. Session Secret Management
  4. Rate Limiting
  5. Container Security
  6. Network Security
  7. Certificate Security
  8. Multi-Instance Deployments
  9. Production Deployment Checklist

Production Security Requirements

The app refuses to start in production if you haven't set strong credentials. No "admin/admin" in production.

Required Variables

  1. SESSION_SECRET

    • Minimum 32 characters
    • Must be cryptographically random
    • Cannot use placeholder values
  2. 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"

Quick Production Setup

# 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 -d

Password Security

Password Requirements

Production 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"

Password Validation

Validation occurs:

  • At application startup (environment variable)
  • When changing password via UI
  • During user creation (future multi-user feature)

Password Storage

  • 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

Password Best Practices

  1. Use a password manager to generate and store passwords
  2. Use unique passwords for each deployment
  3. Rotate passwords regularly (every 90 days recommended)
  4. Never share passwords via insecure channels (email, chat, etc.)
  5. Change default password immediately after first login

Session Secret Management

What is SESSION_SECRET?

The session secret is used for:

  • Encrypting session cookies
  • Signing JWT tokens
  • CSRF protection
  • OAuth state parameter signing

Requirements

  • Minimum length: 32 characters
  • Uniqueness: Different for each deployment
  • Randomness: Cryptographically secure random data
  • Persistence: Never change after initial deployment (invalidates all sessions)

Generating Secure Secrets

Recommended method (OpenSSL):

openssl rand -base64 32

Alternative 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())"

Secret Rotation

When to rotate:

  • Suspected compromise
  • Security incident
  • Employee offboarding
  • Compliance requirements

How to rotate:

  1. Generate new secret
  2. Update SESSION_SECRET in .env
  3. Restart containers: docker compose restart web
  4. All users will be logged out (this is expected)

Warning: Rotating the secret logs out all users immediately.


Rate Limiting

Login Rate Limiting

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:

  1. All auth requests count against the rate limit
  2. After 5 requests in 60 seconds, further requests are rejected with HTTP 429
  3. Counter resets after the window expires

Custom Rate Limiting

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=120

See Environment Variables Reference for details.

Rate Limiting Considerations

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

Container Security

Rootless Containers

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 -d

See Rootless Docker Operation for complete guide.

Image Security

  • Minimal base images (Alpine Linux)
  • Regular security updates
  • SBOM (Software Bill of Materials) generated
  • Build provenance attestation
  • Automated vulnerability scanning

Volume Permissions

Secure data directories:

chmod 700 data caddy-data caddy-config

Restrict .env file:

chmod 600 .env
chown root:root .env  # If running as root

Container Isolation

  • 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

Network Security

Port Exposure

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

Reverse Proxy for Web UI

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

Firewall Configuration

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

Network Isolation

  • Use Docker network isolation
  • Don't expose internal services
  • Use VPN for remote access to UI
  • Consider Cloudflare Tunnel for secure access

Certificate Security

Private Key Storage

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

Certificate Best Practices

  1. Use Let's Encrypt for automatic renewal
  2. Enable Cloudflare DNS-01 for wildcard certificates
  3. Monitor expiration dates (Caddy handles this automatically)
  4. Use strong key sizes (2048-bit RSA minimum, 256-bit ECDSA recommended)
  5. Limit certificate scope (don't use one cert for everything)

ACME Account Key

  • Stored by Caddy in /data volume
  • Encrypted by Caddy
  • Backup Caddy data volume for continuity

Multi-Instance Deployments

Instance Sync (Master/Slave)

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.

Remaining Limitations

Even with instance sync, some limitations apply:

  1. Rate limiting — In-memory only, not shared across instances
  2. Session storage — Per-instance; logging into master does not log you into slaves
  3. Database — SQLite; only one writer at a time per instance
  4. 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.

Single Instance HA

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

Production Deployment Checklist

Before Deployment

  • 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

Security Configuration

  • 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)

Container Security

  • Use rootless containers (PUID/PGID)
  • Keep images updated regularly
  • Enable automatic security updates
  • Monitor security advisories
  • Review Docker Compose configuration

Operational Security

  • Set up monitoring and alerting
  • Configure automated backups
  • Test backup restoration
  • Document incident response plan
  • Plan secret rotation schedule
  • Review access logs regularly

Post-Deployment

  • Change admin password from profile
  • Test all functionality
  • Verify HTTPS certificates
  • Test backup and restore
  • Monitor logs for errors
  • Set up health check monitoring

Security Incident Response

Suspected Compromise

  1. Immediate actions:

    • Rotate SESSION_SECRET
    • Change ADMIN_PASSWORD
    • Review audit logs
    • Check for unauthorized changes
  2. Investigation:

    • Review access logs
    • Check database for unauthorized users (future)
    • Verify proxy host configurations
    • Check certificate changes
  3. Recovery:

    • Restore from known-good backup
    • Re-deploy with new secrets
    • Force logout all users
    • Notify relevant parties

User Roles

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.


Related Documentation


Need help? See Troubleshooting or open an issue.

Clone this wiki locally