trails/docs/server-hardening.md
Ullrich Schäfer 0a8dd0b766
Add transactional emails (SMTP) and planner features (no-go areas, notes, crash recovery)
Transactional emails:
- Add nodemailer SMTP email module with dev-mode console logging
- Magic link template and welcome template with HTML + plain text
- Wire sendMagicLink into login flow, sendWelcome into registration
- Update privacy page and deploy docs for SMTP configuration

Planner features:
- No-go areas: draw polygons on map (leaflet-geoman), synced via Yjs,
  passed to BRouter as nogos parameter, route recomputes on change
- Session notes: collaborative Y.Text textarea in sidebar tab
- Crash recovery: periodic localStorage save of Yjs state, restore on reconnect
- Rate limit session creation (10/IP/hour) in /new route

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 01:00:42 +01:00

1.7 KiB

Server Hardening

Steps to harden the Hetzner CX21 production server.

UFW Firewall

# Install and enable
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp    # SSH
ufw allow 80/tcp    # HTTP (Caddy redirect)
ufw allow 443/tcp   # HTTPS
ufw enable
ufw status

Fail2ban (SSH protection)

# Install
apt install -y fail2ban

# Create config
cat > /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
bantime = 3600
findtime = 600
EOF

# Start
systemctl enable fail2ban
systemctl start fail2ban

# Check status
fail2ban-client status sshd

Email (SMTP)

Transactional emails (magic link login, welcome) require an SMTP server. Set these env vars on the server (used by docker-compose):

# SMTP connection URL (any provider: Mailgun, SES, Postfix relay, etc.)
export SMTP_URL="smtp://user:pass@smtp.example.com:587"

# Optional: override sender address (defaults to noreply@trails.cool)
export SMTP_FROM="trails.cool <noreply@trails.cool>"

DNS records for deliverability (add to your domain's DNS):

  • SPF: v=spf1 include:_spf.your-smtp-provider.com ~all
  • DKIM: Provider-specific TXT record for email signing
  • DMARC: v=DMARC1; p=quarantine; rua=mailto:dmarc@trails.cool

In dev mode, emails are logged to console instead of sent (no SMTP needed).

SSH Hardening

Already in place:

  • Key-based auth only (password auth disabled by Hetzner cloud-init)
  • Root login via SSH key only

Optional improvements:

  • Change SSH port (security through obscurity, mild benefit)
  • Add AllowUsers root to /etc/ssh/sshd_config to restrict SSH users