infra/docs/plans/2026-02-23-mailserver-hardening-design.md
Viktor Barzin 1c300a14cf mailserver: overhaul inbound delivery, monitoring, CrowdSec, and migrate to Brevo relay
Inbound:
- Direct MX to mail.viktorbarzin.me (ForwardEmail relay attempted and abandoned)
- Dedicated MetalLB IP 10.0.20.202 with ETP: Local for CrowdSec real-IP detection
- Removed Cloudflare Email Routing (can't store-and-forward)
- Fixed dual SPF violation, hardened to -all
- Added MTA-STS, TLSRPT, imported Rspamd DKIM into Terraform
- Removed dead BIND zones from config.tfvars (199 lines)

Outbound:
- Migrated from Mailgun (100/day) to Brevo (300/day free)
- Added Brevo DKIM CNAMEs and verification TXT

Monitoring:
- Probe frequency: 30m → 20m, alert thresholds adjusted to 60m
- Enabled Dovecot exporter scraping (port 9166)
- Added external SMTP monitor on public IP

Documentation:
- New docs/architecture/mailserver.md with full architecture
- New docs/architecture/mailserver-visual.html visualization
- Updated monitoring.md, CLAUDE.md, historical plan docs
2026-04-12 22:24:38 +01:00

2.3 KiB

Mail Server Lightweight Hardening Design

Date: 2026-02-23 Scope: Security, reliability, and hygiene improvements to the docker-mailserver stack Status: Completed. ForwardEmail relay removed 2026-04-12 — MX now direct to mail.viktorbarzin.me on dedicated MetalLB IP with CrowdSec protection.

Current State

  • docker-mailserver 15.0.0 on K8s (single replica, Recreate strategy)
  • Roundcubemail webmail (MySQL-backed, debug logging on, unpinned :latest tag)
  • Outbound relay via Mailgun, inbound MX via ForwardEmail
  • OpenDKIM for DKIM signing, no spam filtering (SpamAssassin/ClamAV/Amavis disabled)
  • DMARC policy none (monitoring only)
  • No brute-force protection, no mailserver-down alert
  • Dovecot exporter sidecar (unpinned), stale SendGrid DNS records

Changes

1. Enable Rspamd (replace OpenDKIM as DKIM signer)

Add to mailserver_env_config:

  • ENABLE_RSPAMD = "1" (spam filtering, DKIM verification, phishing detection, Oletools)
  • ENABLE_OPENDKIM = "0" (Rspamd handles DKIM signing natively)
  • RSPAMD_LEARN = "1" (learn from Junk folder movements)

Existing OpenDKIM key mounts stay — Rspamd reads them from the same paths. Resource impact: ~150-200MB additional RAM.

2. DMARC DNS enforcement

Update _dmarc TXT record: p=none -> p=quarantine. Can tighten to p=reject after validation.

3. Postfix rate limiting

Add to postfix_cf:

smtpd_client_connection_rate_limit = 10
smtpd_client_message_rate_limit = 30
anvil_rate_time_unit = 60s

Service already uses externalTrafficPolicy: Local, so real client IPs are visible to Postfix. ForwardEmail IPs on port 25 are subject to same limits but 10 conn/min is generous.

4. Prometheus alert

Uncomment the existing mailserver-down alert in prometheus_chart_values.tpl.

5. Roundcubemail cleanup

  • Pin image: roundcube/roundcubemail:latest -> roundcube/roundcubemail:1.6-apache
  • Disable debug: ROUNDCUBEMAIL_SMTP_DEBUG = "false", ROUNDCUBEMAIL_DEBUG_LEVEL = "1"

6. SendGrid DNS cleanup

Remove stale CNAME records: em7107, s1._domainkey, s2._domainkey.

Not Changing

  • Roundcubemail stays (user preference)
  • ForwardEmail/Mailgun relay stays (practical dependency)
  • ClamAV stays disabled (Rspamd Oletools covers malicious attachments)
  • Single replica (HA email requires significant additional complexity)