Configuration

Security

Cleanmails implements multiple layers of security to protect your data and infrastructure.

Encryption

  • Credentials at rest: All SMTP/IMAP passwords, integration tokens, and API keys are encrypted with AES-256-GCM using your ENCRYPTION_KEY (64-char hex = 32 bytes)
  • User passwords: Hashed with bcrypt
  • API keys: Hashed with bcrypt before storage; only the cm_live_ prefix is displayed in the UI
  • JWT tokens: Signed with JWT_SECRET (HMAC), 7-day expiry
Never lose your ENCRYPTION_KEY

If you lose the ENCRYPTION_KEY, all stored SMTP passwords, IMAP credentials, and integration tokens become unrecoverable. Back up your .env file separately.

Authentication

  • JWT-based auth: Login returns a JWT token set as an HttpOnly cookie (auth_token)
  • API key fallback: Endpoints also accept Authorization: Bearer cm_live_xxx for programmatic access
  • Workspace access middleware: Every workspace-scoped request validates the user has access to that workspace
  • Role-based access: Admin endpoints require super_admin or admin role

Rate Limiting

ScopeLimitWindow
Login / Register (per IP)5 requests1 minute
Authenticated endpoints (per user)60 requests1 minute

HTTP Security Headers

The security headers middleware adds to every response:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Strict-Transport-Security: max-age=31536000 (via Caddy)

CORS

CORS is restricted to origins defined in the ALLOWED_ORIGINS environment variable (comma-separated). In production, this should only contain your domain (e.g., https://app.yourdomain.com).

License Guard

A middleware checks license status on every request. If the license is revoked, all API routes are blocked with a 403 response — protecting against unauthorized usage after license expiration.

Input Validation

  • Request body limit: 10 MB max (MaxMultipartMemory)
  • Binding validation: Gin struct binding with required field annotations
  • UUID parsing: All resource IDs are validated as UUIDs
  • Workspace isolation: Queries always filter by workspace_id at the database layer

Webhook Security

  • Outgoing webhooks: Signed with HMAC-SHA256 using per-subscription secrets
  • Incoming webhooks (SES/Resend): Public endpoints for bounce/complaint processing
  • Unsubscribe links: HMAC-signed tokens prevent enumeration attacks
  • Tracking links: Unique per-delivery tracking IDs

Data Isolation

  • All data queries are scoped by workspace_id — enforced by middleware
  • Employees only see assigned workspaces (controlled via admin assignments)
  • Clients only see their own workspace
  • Campaign start validates step count and mailbox assignment
  • Lead deduplication enforced by database unique index per workspace

Infrastructure Security

  • .env file: Set to mode 640 (readable by root and docker group only)
  • Docker socket: Mounted read-only for self-update capability
  • No exposed ports: Only Caddy exposes 80/443; all services communicate internally
  • Health checks: Internal only (not exposed through Caddy except /health)
  • Redis: No authentication required (only accessible within Docker network)
  • PostgreSQL: Password-protected, only accessible within Docker network

Roles & Permissions

RoleAccess
super_adminEverything + system updates + impersonation
adminAdmin panel + all workspaces + employee/client management
employeeOnly assigned workspaces (campaigns, leads, mailboxes, threads)
clientRead-only view of their workspace