Zentrales SMTP-Relay für Docker Container
Zentrales SMTP-Relay für Docker Container: Sichere E-Mail-Zustellung in isolierten Netzwerken
Inhaltsverzeichnis
1. Das Problem: E-Mail aus Docker Containern
In einer modernen Docker-basierten Infrastruktur laufen zahlreiche Services, die E-Mail-Benachrichtigungen versenden müssen: Authentifizierungs-Systeme für Passwort-Resets, CMS-Plattformen für Workflow-Notifications, Backup-Systeme für Status-Reports, und Monitoring-Tools für Alerts.
Doch was in einer klassischen Server-Umgebung trivial erscheint – eine E-Mail über einen SMTP-Server zu versenden – wird in containerisierten Umgebungen schnell zur Herausforderung.
Die Herausforderungen
- Netzwerk-Isolation: Container in internen Docker Networks haben keinen direkten Zugang zu externen Mail-Servern
- DNS-Auflösung: Interne Container können oft externe Hostnamen nicht auflösen
- Cross-Network-Kommunikation: Services in verschiedenen Docker Networks können nicht direkt kommunizieren
- Dezentrale Konfiguration: Jeder Service hat eigene SMTP-Einstellungen – Wartungsalptraum
- Security: Credentials müssen in jedem Container konfiguriert werden
Konkrete Symptome
In der Praxis manifestieren sich diese Probleme durch:
- E-Mails, die nie ankommen
- Timeout-Fehler beim Verbindungsaufbau
- "Connection refused" oder "Host not found" Meldungen
- Inkonsistente E-Mail-Zustellung (manchmal funktioniert es, manchmal nicht)
# Typischer Fehler in Container-Logs
SMTP Error: Could not connect to mail.example.com:587
Connection timed out (110)
# Oder
sendmail: fatal: Host not found: mail.example.com
2. Die Lösung: Zentrales SMTP-Relay
Die elegante Lösung für dieses Problem ist ein zentrales SMTP-Relay – ein spezialisierter Container, der als Gateway zwischen internen Services und dem externen Mail-Server fungiert.
Konzept
┌─────────────────────────────────────────────────────────────┐
│ Internes Docker Network │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Service A│ │Service B│ │Service C│ │
│ │ (Auth) │ │ (CMS) │ │ (Backup)│ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ SMTP-Relay │◄──── Einziger Ausgangspunkt
│ │ (Postfix) │ │
│ └────────┬─────────┘ │
│ │ │
└───────────────────────┼──────────────────────────────────────┘
│
│ SMTP Relay
▼
┌──────────────────┐
│ Mail-Server │
│ (Postfix/ │
│ Mailcow) │
└──────────────────┘
Vorteile dieser Architektur
- ✅ Zentrale Konfiguration: Ein Punkt für alle SMTP-Einstellungen
- ✅ Netzwerk-Brücke: Verbindet isolierte Networks mit dem Mail-Server
- ✅ Security-Gateway: Zentrale Kontrolle über ausgehende E-Mails
- ✅ Einfache Integration: Services senden an localhost-ähnlichen Host
- ✅ Debugging: Ein Ort für Logs und Fehlersuche
- ✅ Rate-Limiting: Schutz vor E-Mail-Flooding
3. Architektur & Design
Komponenten-Übersicht
| Komponente | Funktion | Details |
|---|---|---|
| SMTP-Relay Container | Gateway für E-Mail-Versand | Postfix-basiert, leichtgewichtig |
| Internes Network | Isoliertes Netzwerk für Services | Eigenes Subnet, keine externe Erreichbarkeit |
| Mail-Server Network | Verbindung zum Mail-Server | Externes oder Mail-spezifisches Network |
| Client Services | E-Mail sendende Anwendungen | Auth, CMS, Backup, Monitoring, etc. |
Netzwerk-Design
Der SMTP-Relay Container ist das einzige Element, das in beiden Netzwerken gleichzeitig existiert – dem internen Service-Network und dem Mail-Server-Network.
# Docker Network Topologie
smtp-internal (Bridge, isolated)
├── smtp-relay ──┐
├── authentik ──┤
├── directus ──┼── Alle senden an smtp-relay:25
├── nextcloud ──┤
├── n8n ──┤
├── gitea ──┤
└── backup ──┘
mail-network (Bridge)
├── smtp-relay ◄── Einzige Verbindung nach außen
├── postfix
├── dovecot
└── ...
Message Flow
- Service (z.B. Authentik) generiert E-Mail
- Service sendet an
smtp-relay:25(internes Network) - SMTP-Relay empfängt, validiert, loggt
- SMTP-Relay leitet an Mail-Server weiter (Mail-Network)
- Mail-Server versendet an Internet
4. Security-Konzept
Ein SMTP-Relay ohne angemessene Sicherheitsmaßnahmen kann schnell zum Open Relay werden – ein Albtraum für jeden Administrator. Die folgende Security-Architektur verhindert Missbrauch.
Defense in Depth: Mehrschichtige Sicherheit
┌─────────────────────────────────────────────────────────────┐
│ Schicht 1: Netzwerk-Isolation │
│ └── Nur Container im internen Network können verbinden │
├─────────────────────────────────────────────────────────────┤
│ Schicht 2: IP-basierte Zugriffskontrolle │
│ └── Nur definiertes Subnet darf senden │
├─────────────────────────────────────────────────────────────┤
│ Schicht 3: Domain-Whitelist │
│ └── Nur eigene Domain als Absender erlaubt │
├─────────────────────────────────────────────────────────────┤
│ Schicht 4: Sender-Validierung │
│ └── FQDN-Prüfung, Domain-Existenz │
├─────────────────────────────────────────────────────────────┤
│ Schicht 5: Rate-Limiting │
│ └── Maximale E-Mails pro Zeiteinheit │
└─────────────────────────────────────────────────────────────┘
Schicht 1: Netzwerk-Isolation
Das SMTP-Relay ist nur im internen Network erreichbar. Container außerhalb dieses Networks können keine Verbindung aufbauen.
# Docker Compose Network Definition
networks:
smtp-internal:
driver: bridge
internal: false # Bridge für Relay-Funktion
ipam:
config:
- subnet: 172.30.0.0/24 # Dediziertes Subnet
Schicht 2: IP-basierte Zugriffskontrolle
Postfix akzeptiert nur Verbindungen aus dem definierten Subnet:
# Postfix MYNETWORKS Konfiguration
MYNETWORKS: "127.0.0.0/8 [INTERNES_SUBNET]"
# Nur localhost und das interne Subnet dürfen senden
# Alle anderen werden abgelehnt
Schicht 3: Domain-Whitelist
Nur E-Mails mit der eigenen Domain als Absender werden akzeptiert:
# Erlaubte Sender-Domains
ALLOWED_SENDER_DOMAINS: example.com
# Schützt vor:
# - Spoofing fremder Domains
# - Verwendung als Open Relay
# - Reputation-Schäden
Schicht 4: Sender-Validierung
# Postfix Sender Restrictions
smtpd_sender_restrictions:
- permit_mynetworks # Erlaube interne Clients
- reject_unknown_sender_domain # Lehne unbekannte Domains ab
- reject_non_fqdn_sender # Erfordere vollqualifizierte Adressen
Schicht 5: Rate-Limiting
Schutz vor Flooding, kompromittierten Containern und fehlerhaften Workflows:
# Postfix Rate-Limiting
smtpd_client_message_rate_limit: [MAX_MESSAGES_PER_HOUR]
smtpd_client_connection_rate_limit: [MAX_CONNECTIONS_PER_HOUR]
anvil_rate_time_unit: 3600s # Zeitfenster: 1 Stunde
Da das SMTP-Relay nur im internen Network erreichbar ist (Schicht 1 & 2), ist keine zusätzliche Authentifizierung erforderlich. Die Netzwerk-Isolation ist der primäre Security-Mechanismus.
Dies vereinfacht die Konfiguration der Client-Services erheblich – sie benötigen keine Credentials, nur Host und Port.
5. Implementierung
Docker Compose Setup
Die Implementierung basiert auf dem boky/postfix Image – einem
schlanken, produktionsreifen Postfix-Container:
version: '3.9'
services:
smtp-relay:
image: boky/postfix:latest
container_name: smtp-relay
hostname: smtp-relay
restart: unless-stopped
networks:
- smtp-internal # Internes Service-Network
- mail-network # Verbindung zum Mail-Server
environment:
# Relay-Ziel
RELAYHOST: [MAIL_SERVER_HOST]:25
# Security: Nur eigene Domain
ALLOWED_SENDER_DOMAINS: example.com
# Security: Nur internes Subnet
MYNETWORKS: "127.0.0.0/8 [INTERNES_SUBNET]"
# Rate-Limiting
POSTFIX_smtpd_client_message_rate_limit: "[LIMIT]"
POSTFIX_smtpd_client_connection_rate_limit: "[LIMIT]"
POSTFIX_anvil_rate_time_unit: "3600s"
# Sender-Validierung
POSTFIX_smtpd_sender_restrictions: >-
permit_mynetworks,
reject_unknown_sender_domain,
reject_non_fqdn_sender
healthcheck:
test: ["CMD", "postfix", "status"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 128m
cpus: '0.25'
networks:
smtp-internal:
driver: bridge
ipam:
config:
- subnet: 172.30.0.0/24
mail-network:
external: true
Warum boky/postfix?
| Eigenschaft | Vorteil |
|---|---|
| Minimal | Nur Postfix, keine unnötigen Komponenten |
| Environment-basiert | Konfiguration via ENV-Variablen |
| Relay-optimiert | Speziell für Relay-Szenarien designed |
| Aktiv maintained | Regelmäßige Security-Updates |
| Klein | ~50MB Image-Größe |
6. Service-Integration
Die Integration in bestehende Services ist denkbar einfach: Anstatt externe SMTP-Server mit Credentials zu konfigurieren, verweisen alle Services auf den internen Relay.
Allgemeines Muster
# Vorher (externe SMTP-Konfiguration)
SMTP_HOST: mail.example.com
SMTP_PORT: 587
SMTP_USER: service@example.com
SMTP_PASSWORD: [GEHEIM]
SMTP_TLS: true
# Nachher (internes Relay)
SMTP_HOST: smtp-relay
SMTP_PORT: 25
SMTP_USER: (nicht benötigt)
SMTP_PASSWORD: (nicht benötigt)
SMTP_TLS: false
Beispiel: Authentik (SSO)
authentik-server:
networks:
- frontend
- authentik-backend
- smtp-internal # Hinzufügen
environment:
AUTHENTIK_EMAIL__HOST: smtp-relay
AUTHENTIK_EMAIL__PORT: 25
AUTHENTIK_EMAIL__USE_TLS: "false"
AUTHENTIK_EMAIL__USE_SSL: "false"
AUTHENTIK_EMAIL__FROM: auth@example.com
Beispiel: Directus (CMS)
directus:
networks:
- frontend
- webapp-backend
- smtp-internal # Hinzufügen
environment:
EMAIL_TRANSPORT: smtp
EMAIL_SMTP_HOST: smtp-relay
EMAIL_SMTP_PORT: 25
EMAIL_FROM: cms@example.com
Beispiel: n8n (Workflow Automation)
n8n:
networks:
- frontend
- n8n-backend
- smtp-internal # Hinzufügen
environment:
N8N_EMAIL_MODE: smtp
N8N_SMTP_HOST: smtp-relay
N8N_SMTP_PORT: 25
N8N_SMTP_SENDER: workflow@example.com
N8N_SMTP_SSL: "false"
Beispiel: Nextcloud (File Sharing)
Nextcloud benötigt zusätzliche Konfiguration via OCC-Kommando:
# Docker Compose: Network hinzufügen
nextcloud:
networks:
- frontend
- cloud
- smtp-internal # Hinzufügen
# OCC-Konfiguration
docker exec -u www-data nextcloud php occ config:system:set \
mail_smtpmode --value="smtp"
docker exec -u www-data nextcloud php occ config:system:set \
mail_smtphost --value="smtp-relay"
docker exec -u www-data nextcloud php occ config:system:set \
mail_smtpport --value="25"
docker exec -u www-data nextcloud php occ config:system:set \
mail_from_address --value="cloud"
docker exec -u www-data nextcloud php occ config:system:set \
mail_domain --value="example.com"
Beispiel: Gitea (Git Service)
gitea:
networks:
- frontend
- gitea-backend
- smtp-internal # Hinzufügen
environment:
GITEA__mailer__ENABLED: "true"
GITEA__mailer__FROM: git@example.com
GITEA__mailer__PROTOCOL: smtp
GITEA__mailer__SMTP_ADDR: smtp-relay
GITEA__mailer__SMTP_PORT: 25
Beispiel: Backup-Script (Shell)
#!/bin/sh
# backup-notify.sh
SMTP_HOST="smtp-relay"
SMTP_PORT="25"
FROM_EMAIL="backup@example.com"
TO_EMAIL="admin@example.com"
# E-Mail senden mit sendmail oder msmtp
echo "Subject: Backup Status
From: $FROM_EMAIL
To: $TO_EMAIL
Backup completed successfully at $(date)" | \
nc $SMTP_HOST $SMTP_PORT
7. Monitoring & Troubleshooting
Health Checks
# Container-Status prüfen
docker ps | grep smtp-relay
# Postfix-Status
docker exec smtp-relay postfix status
# Mail-Queue prüfen
docker exec smtp-relay mailq
Logging
# Live-Logs anzeigen
docker logs -f smtp-relay
# Letzte 50 Zeilen
docker logs --tail 50 smtp-relay
# Nur Fehler
docker logs smtp-relay 2>&1 | grep -E "(error|reject|warning)"
Häufige Probleme & Lösungen
| Problem | Ursache | Lösung |
|---|---|---|
| Connection refused | Service nicht im smtp-internal Network | docker network connect smtp-internal [container] |
| Sender address rejected | Falsche Sender-Domain | Sender auf @example.com ändern |
| Rate limit exceeded | Zu viele E-Mails | Queue prüfen, Rate-Limit anpassen |
| Relay access denied | IP nicht in MYNETWORKS | Subnet-Konfiguration prüfen |
| Messages stuck in queue | Mail-Server nicht erreichbar | Relay-Verbindung prüfen |
Test-E-Mail senden
# Aus einem verbundenen Container
docker exec [service-container] sh -c '
echo "Subject: Test
From: test@example.com
To: admin@example.com
Test email from SMTP relay" | nc smtp-relay 25
'
# Oder mit msmtp/sendmail
docker exec [service-container] sh -c '
echo "Test message" | \
mail -s "Test" -S smtp=smtp-relay:25 admin@example.com
'
Queue-Management
# Queue anzeigen
docker exec smtp-relay mailq
# Alle E-Mails erneut versuchen
docker exec smtp-relay postqueue -f
# Spezifische E-Mail löschen
docker exec smtp-relay postsuper -d [QUEUE_ID]
# Alle E-Mails löschen (Vorsicht!)
docker exec smtp-relay postsuper -d ALL
8. Fazit & Best Practices
Zusammenfassung
Ein zentrales SMTP-Relay löst das fundamentale Problem der E-Mail-Zustellung aus Docker-Containern elegant und sicher. Durch die Kombination aus Netzwerk-Isolation, Domain-Whitelisting und Rate-Limiting entsteht eine robuste Lösung für produktive Umgebungen.
- ✅ Zentrale Verwaltung: Ein Konfigurationspunkt statt dutzender
- ✅ Erhöhte Sicherheit: Mehrschichtiges Security-Konzept
- ✅ Vereinfachte Integration: Keine Credentials in Services nötig
- ✅ Besseres Debugging: Ein Ort für alle E-Mail-Logs
- ✅ Skalierbarkeit: Einfach neue Services hinzufügen
- ✅ Ressourcen-effizient: ~128MB RAM, minimal CPU
Best Practices
- Eigenes Subnet: Verwenden Sie ein dediziertes Subnet für das SMTP-Network
- Rate-Limiting anpassen: Starten Sie konservativ und erhöhen Sie bei Bedarf
- Monitoring einrichten: Überwachen Sie Queue-Länge und Zustellungszeiten
- Log-Rotation: Konfigurieren Sie Docker Log-Rotation
- Health Checks: Nutzen Sie Docker Health Checks für Alerting
- Regelmäßige Updates: Halten Sie das Postfix-Image aktuell
Erweiterungsmöglichkeiten
- DKIM-Signierung: E-Mails im Relay signieren für bessere Zustellbarkeit
- Prometheus Metrics: Postfix-Exporter für detailliertes Monitoring
- Multiple Relays: Load-Balancing für High-Availability
- TLS zum Mail-Server: Verschlüsselung der Relay-Verbindung
- Outbound-Queue-Retention: Längere Speicherung bei Ausfall
Wann ist diese Lösung geeignet?
| Geeignet für | Weniger geeignet für |
|---|---|
| Self-hosted Infrastrukturen | Externe Cloud-Services (nutzen deren SMTP) |
| Multiple Docker-Services mit E-Mail-Bedarf | Einzelne Container mit einfacher Konfiguration |
| Umgebungen mit Network-Isolation | Flache Netzwerke ohne Segmentierung |
| Eigener Mail-Server (Mailcow, Postfix) | Ausschließlich externe SMTP-Provider |