Skip to content

Security Hardening

Before going live, verify every item:

  • All secrets generated with openssl rand -hex 32 (not default values)
  • .env.prod file permissions set to 600 (owner-only)
  • .env.prod is in .gitignore and never committed
  • JWT_SECRET is at least 32 characters
  • Separate encryption keys for APP_ENCRYPTION_KEY, MFA_ENCRYPTION_KEY
  • AGENT_ENROLLMENT_SECRET rotated after initial enrollment batch
  • Only ports 80/443 (and optionally 3478 for TURN) exposed publicly
  • PostgreSQL bound to 127.0.0.1 (not 0.0.0.0)
  • Redis bound to 127.0.0.1 (not 0.0.0.0)
  • Grafana/Prometheus accessible only via localhost or VPN
  • SSH key-only authentication (no password auth)
  • UFW or iptables configured
  • Caddy auto-TLS configured with valid domain and ACME email
  • HSTS header enabled with includeSubDomains; preload
  • No self-signed certificates in production
  • no-new-privileges: true on all containers (default in prod compose)
  • cap_drop: ALL on all containers
  • API and Web containers run with read_only: true rootfs
  • Resource limits (cpus, mem_limit, pids_limit) set
  • Non-root container users (UID 1001)
  • MFA (TOTP) enabled for all admin accounts
  • Registration disabled in production (ENABLE_REGISTRATION=false) after initial setup
  • Rate limiting active on login endpoints
  • Session timeout configured (SESSION_MAX_AGE)
  • Agent tokens stored as SHA-256 hashes (automatic for new enrollments)
  • Config file permissions: 0750 for /etc/breeze/, 0640 for agent.yaml, 0600 for secrets.yaml
  • Agent rate limiting enabled (120 req/60s per agent via Redis)
  • Enrollment keys set with expiry and usage limits
  • Consider enabling Cloudflare mTLS for zero-trust agent auth
  • Prometheus metrics endpoint protected with bearer token
  • Alert rules configured for error rates and infrastructure
  • Audit logging enabled (automatic for all mutating operations)
  • Log aggregation configured (Loki)
Terminal window
# UFW example
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Only if using TURN for WebRTC:
# sudo ufw allow 3478/tcp
# sudo ufw allow 3478/udp
sudo ufw enable

All mutating operations are automatically logged with:

FieldDescription
actorTypeuser, api_key, agent, or system
actorIdUser ID or device ID
actionOperation performed
resourceTarget resource type
resourceIdTarget resource ID
detailsJSON payload of changes
ipAddressClient IP address
timestampISO 8601 timestamp

Query audit logs via the API:

Terminal window
curl -H "Authorization: Bearer $TOKEN" \
"https://breeze.yourdomain.com/api/v1/audit?resource=devices&action=delete"

Breeze implements Redis-backed sliding window rate limiting:

EndpointLimitWindow
Login5 attempts5 minutes
API (per user)100 requests60 seconds
Agent (per device)120 requests60 seconds
Enrollment10 attempts60 seconds