01Einleitung & Zielbild
Ziel dieses Beitrags ist ein produktionstauglicher öffentlicher Mail-Relay auf Debian mit dem Hostnamen mail.example.com. Der Relay übernimmt zwei klar getrennte Aufgaben:
- Ausgehend (Smarthost): Anwendungen und interne Systeme authentifizieren sich per SASL auf Port 587 (Submission, STARTTLS) und der Relay stellt die Nachrichten an die Ziel-MX im Internet zu.
- Eingehend (Gateway): Der Relay ist der MX für
example.com, nimmt Mails auf Port 25 an und reicht sie über einen WireGuard-Tunnel an den internen Mailserver (10.10.0.2) weiter.
Der interne Mailserver bleibt so vollständig hinter dem Tunnel verborgen; nach außen ist ausschließlich der Relay mit sauberer IP-Reputation, TLS und DKIM sichtbar.
Voraussetzungen
- Ein Debian-12-Server mit statischer öffentlicher IPv4 (idealerweise auch IPv6) und
root-Zugang. - Zugriff auf die öffentliche DNS-Zone von
example.comsowie die Möglichkeit, den PTR/Reverse-DNS-Eintrag der IP beim Hoster zu setzen. - Freigeschaltete Ports:
25,587,465(optional),80(nur für ACME-Standalone),51820/udp(WireGuard) und22.
Alle Beispiele verwenden die Domain example.com, den Relay mail.example.com, das Tunnel-Netz 10.10.0.0/24 (Relay 10.10.0.1, interner MTA 10.10.0.2). Ersetzen Sie diese Werte durch Ihre realen Angaben.
02Grundinstallation & Härtung
Zunächst das System aktualisieren, den vollqualifizierten Hostnamen setzen und die Zeitsynchronisation sicherstellen — ein korrekter FQDN und eine korrekte Uhrzeit sind für TLS, DKIM und Logauswertung essenziell.
apt update && apt -y full-upgrade
# FQDN setzen
hostnamectl set-hostname mail.example.com
echo "127.0.1.1 mail.example.com mail" >> /etc/hosts
# Zeitsynchronisation
apt -y install systemd-timesyncd
timedatectl set-ntp trueFirewall
Wir nutzen ufw als schlanke Front für nftables. Nur die tatsächlich benötigten Ports werden geöffnet.
apt -y install ufw
ufw allow 22/tcp # SSH
ufw allow 25/tcp # SMTP (inbound MX)
ufw allow 587/tcp # Submission (SASL)
ufw allow 465/tcp # SMTPS (optional)
ufw allow 51820/udp # WireGuard
ufw --force enable
# Port 80 bleibt geschlossen. Nur falls ACME per HTTP-01/Standalone (Abschnitt 8)
# genutzt wird, temporär öffnen und danach wieder schließen:
# ufw allow 80/tcp && acme.sh --issue --standalone ... && ufw delete allow 80/tcpSSH härten
Der Server ist über Port 22 aus dem Internet erreichbar. fail2ban (Abschnitt 10) ist nur eine zusätzliche Absicherung — die eigentliche Härtung passiert im SSH-Daemon: Schlüssel statt Passwort, kein direkter Root-Login.
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AllowUsers admin
X11Forwarding no
MaxAuthTries 3Erst einen SSH-Schlüssel hinterlegen und die Anmeldung testen, dann PasswordAuthentication no aktivieren und systemctl reload ssh — sonst sperren Sie sich aus.
Grundhärtung des Hosts
Firewallregeln begrenzen die Netzwerk-Exposition, ersetzen aber keine Anwendungs- und Systemhärtung. Für einen dauerhaft exponierten Server empfehlenswert:
# Automatische Sicherheitsupdates
apt -y install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
# AppArmor aktiv halten (unter Debian standardmäßig vorhanden)
apt -y install apparmor apparmor-utils
aa-status
# nur benötigte Dienste; installierte Pakete regelmäßig prüfen
systemctl list-unit-files --state=enabledFür produktive Relays zusätzlich erwägen: sysctl-Härtung (Netzwerk-Stack), Ressourcenlimits, ein Monitoring/Alerting (Abschnitt 12) sowie regelmäßige Paketinventur. Diese Maßnahmen sind Host-Standard und in dieser Doku nur als Checkliste vermerkt.
Postfix installieren
# Nicht-interaktive Installation (Konfiguration folgt manuell)
DEBIAN_FRONTEND=noninteractive apt -y install postfix postfix-pcre
# Bei der Nachfrage "Internet Site" wählen; wir überschreiben ohnehin main.cf
systemctl enable postfixVor Änderungen die Auslieferungsdateien sichern: cp /etc/postfix/main.cf{,.orig} und cp /etc/postfix/master.cf{,.orig}.
03Postfix-Konfiguration: main.cf
Die main.cf definiert das globale Verhalten des Servers: Identität, Netzwerke, Relay-Ziele, TLS, SASL und die Milter-Anbindung für DKIM. Die folgende Datei ist vollständig und kommentiert.
# ===== Identität =====
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
compatibility_level = 3.6
# ===== Netzwerk / lokale Zustellung deaktivieren =====
inet_interfaces = all
# IPv6 nur aktivieren, wenn es vollständig mitgehärtet ist (Firewall, PTR,
# Monitoring, Reputation). Wird IPv6 nicht benötigt: inet_protocols = ipv4
inet_protocols = all
mydestination =
local_recipient_maps =
local_transport = error:local delivery is disabled
# Nur localhost ist implizit vertraut. Der interne MTA authentifiziert sich
# stattdessen per SASL (siehe Abschnitt 5) — kein IP-Vertrauen über das VPN.
mynetworks = 127.0.0.0/8 [::1]/128
# ===== Eingehend: example.com an den internen MTA weiterreichen =====
relay_domains = example.com
transport_maps = hash:/etc/postfix/transport
relay_recipient_maps = hash:/etc/postfix/relay_recipients
# ===== Restrictions =====
smtpd_helo_required = yes
smtpd_relay_restrictions =
permit_mynetworks
permit_sasl_authenticated
reject_unauth_destination
smtpd_recipient_restrictions =
permit_mynetworks
permit_sasl_authenticated
reject_unauth_destination
reject_unknown_recipient_domain
reject_non_fqdn_recipient
smtpd_helo_restrictions =
permit_mynetworks
reject_invalid_helo_hostname
reject_non_fqdn_helo_hostname
# ===== SASL (Details in Abschnitt 5) =====
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = cyrus
smtpd_sasl_path = smtpd
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes
# ===== TLS (Zertifikate via acme.sh, Abschnitt 8) =====
smtpd_tls_cert_file = /etc/postfix/tls/mail.example.com/fullchain.cer
smtpd_tls_key_file = /etc/postfix/tls/mail.example.com/private.key
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtpd_tls_mandatory_protocols = >=TLSv1.2
smtpd_tls_protocols = >=TLSv1.2
smtp_tls_security_level = may
smtp_tls_mandatory_protocols = >=TLSv1.2
smtp_tls_protocols = >=TLSv1.2
tls_preempt_cipherlist = yes
smtpd_tls_loglevel = 1
# ===== DKIM / Milter (OpenDKIM, Abschnitt 7) =====
# tempfail statt accept: Fällt OpenDKIM aus, wird Mail zurückgestellt (4xx)
# statt UNsigniert ausgeliefert. Bewusste Sicherheit-vor-Verfügbarkeit-Wahl.
milter_default_action = tempfail
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = inet:127.0.0.1:8891
# ===== Hygiene =====
disable_vrfy_command = yes
smtpd_delay_reject = yes
message_size_limit = 52428800
biff = no
append_dot_mydomain = no
# ===== Rate-Limits (anvil, pro Client-IP) =====
smtpd_client_connection_count_limit = 20
smtpd_client_connection_rate_limit = 30
smtpd_client_message_rate_limit = 100
smtpd_client_recipient_rate_limit = 200
smtpd_recipient_limit = 100
anvil_rate_time_unit = 60s
# mynetworks (localhost) von den Limits ausnehmen
smtpd_client_event_limit_exceptions = $mynetworksDiese Limits greifen pro Client-IP. Für authentifizierte Submission über eine gemeinsame IP (z. B. mehrere Anwendungen hinter dem Tunnel) sind sie deshalb nur grob. Echtes Pro-Konto-Rate-Limiting kann Stock-Postfix nicht — dafür postfwd oder policyd (cluebringer) vorschalten. fail2ban ist ausdrücklich kein Ersatz für Rate-Limiting.
smtp_tls_security_level = may verschlüsselt zu fremden MX nur, wenn möglich — es ist kein garantierter Schutz und anfällig für STARTTLS-Downgrade. Für erzwungene Transportsicherheit siehe MTA-STS/DANE in Abschnitt 6. Der Wert may auf Port 25 ist für Internet-Mail dennoch korrekt, da sich TLS zu beliebigen Fremdservern nicht erzwingen lässt, ohne Zustellung zu riskieren.
Transport- und Empfänger-Maps
Die transport-Map lenkt alle Mails für example.com durch den Tunnel an den internen MTA. Die eckigen Klammern [ ] unterdrücken die MX-Suche und adressieren den Host direkt.
example.com smtp:[10.10.0.2]:25
.example.com smtp:[10.10.0.2]:25Die relay_recipients-Map verhindert Backscatter, indem nur real existierende Postfächer angenommen werden (sonst nimmt der Relay alles an und erzeugt später Unzustellbarkeits-Bounces).
info@example.com OK
kontakt@example.com OK
# Alternativ pauschal (weniger streng):
# @example.com OKpostmap /etc/postfix/transport
postmap /etc/postfix/relay_recipients
postfix check && systemctl reload postfix04Postfix-Konfiguration: master.cf
Während die main.cf globale Vorgaben macht, definiert die master.cf die einzelnen Dienste (Ports) und erlaubt pro Dienst gezielte Overrides mit -o. Wir aktivieren Submission (587) mit erzwungenem TLS und SASL sowie optional SMTPS (465).
Die Override-Zeilen (-o …) müssen mit mindestens einem Leerzeichen eingerückt sein und dürfen keine Leerzeichen um das = enthalten.
# service type private unpriv chroot wakeup maxproc command
smtp inet n - y - - smtpd
-o smtpd_sasl_auth_enable=no
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_tls_auth_only=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_sasl_type=cyrus
-o smtpd_sasl_path=smtpd
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_tls_auth_only=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_sasl_type=cyrus
-o smtpd_sasl_path=smtpd
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATINGWichtig: Auf Port 25 (erste Zeile) bleibt smtpd_sasl_auth_enable=no — dort sollen sich fremde MTAs nicht anmelden. Authentifizierte Einlieferung läuft ausschließlich über 587/465. Auf 587 erzwingt smtpd_tls_security_level=encrypt, auf 465 der wrappermode — kombiniert mit smtpd_tls_auth_only=yes ist damit auf beiden Diensten sichergestellt, dass SASL nur über eine bereits bestehende TLS-Verbindung stattfindet.
postfix check && systemctl restart postfix
ss -tlnp | grep -E ':25|:465|:587'05SASL-Authentifizierung (Cyrus, sasldb)
Für einen Relay mit wenigen Absende-Konten ist die Cyrus-SASL-auxprop-Datenbank (sasldb2) die schlankste Lösung — kein zusätzlicher Daemon, Verwaltung per saslpasswd2. (Für größere Umgebungen mit vielen Postfächern ist Dovecot-SASL die bessere Wahl; siehe Hinweis am Ende.)
apt -y install libsasl2-modules sasl2-binSASL-Mechanismen für Postfix festlegen
Postfix (chroot) sucht die SASL-Konfiguration unter /etc/postfix/sasl/smtpd.conf.
pwcheck_method: auxprop
auxprop_plugin: sasldb
mech_list: PLAIN LOGINDie Mechanismen PLAIN und LOGIN übertragen das Passwort im Klartext (base64) und dürfen ausschließlich über eine bereits etablierte TLS-Verbindung zum Einsatz kommen. Genau das erzwingen 587 (encrypt) und 465 (wrappermode) plus smtpd_tls_auth_only=yes. Dieselbe SASL-Konfiguration niemals an einem Dienst ohne TLS-Zwang verwenden.
Konto anlegen
Der Realm muss zu smtpd_sasl_local_domain passen (hier mail.example.com). Der Client meldet sich dann mit dem reinen Benutzernamen an.
# Konto "relay-app" im Realm mail.example.com anlegen
saslpasswd2 -c -u mail.example.com relay-app
# Postfix muss die DB lesen dürfen
chown root:postfix /etc/sasldb2
chmod 640 /etc/sasldb2
# Kontrolle
sasldblistusers2Postfix läuft unter Debian per Default im chroot (/var/spool/postfix). Cyrus SASL sucht sasldb2 relativ dazu. Statt die Datei manuell zu kopieren (was auseinanderlaufen kann), binden wir sie per Bind-Mount ein — so ist die im chroot sichtbare DB stets identisch mit /etc/sasldb2, auch nach jeder Kontoänderung.
install -d /var/spool/postfix/etc
touch /var/spool/postfix/etc/sasldb2
# dauerhafter Bind-Mount via fstab
echo '/etc/sasldb2 /var/spool/postfix/etc/sasldb2 none bind 0 0' >> /etc/fstab
mount -a
chown root:postfix /etc/sasldb2
chmod 640 /etc/sasldb2
systemctl restart postfixBei vielen Nutzern oder wenn ohnehin IMAP betrieben wird, ersetzen Sie die drei smtpd_sasl_*-Zeilen durch smtpd_sasl_type=dovecot und smtpd_sasl_path=private/auth und binden den Dovecot-Auth-Socket ein. Der Rest der Konfiguration bleibt identisch.
Internen MTA per SASL anbinden (kein IP-Vertrauen über das VPN)
Standardmäßig würde man das Tunnel-Netz in mynetworks aufnehmen und dem internen MTA damit den Versand allein aufgrund seiner IP erlauben. Sicherer ist es, auch über den WireGuard-Tunnel SASL zu verlangen: Das Relay-Recht hängt dann an Zugangsdaten statt an der Netzwerkposition. Deshalb steht in der main.cf (Abschnitt 3) 10.10.0.0/24 bewusst nicht mehr in mynetworks.
Ohne IP-Vertrauen kann ein kompromittierter Host im Tunnel-Netz den Relay nicht mehr als offenen Versandweg missbrauchen. Ausgehende Zustellung ist nur noch nach erfolgreicher Anmeldung auf Port 587 möglich; Port 25 nimmt vom Tunnel nur noch Mail für example.com (eingehend) an.
Eigenes Relay-Konto für den internen MTA
saslpasswd2 -c -u mail.example.com internal-mta
# Dank Bind-Mount ist das Konto sofort auch im chroot verfügbar — kein Kopieren nötig.
sasldblistusers2Interner MTA sendet authentifiziert über Port 587
Der interne Postfix wird zum Smarthost-Client: Er relayt ausgehende Mail über den Tunnel an [10.10.0.1]:587 und meldet sich dabei mit dem Konto internal-mta an.
relayhost = [10.10.0.1]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
# Tunnel-IP: Zertifikatsname weicht ab -> verschlüsseln ohne Namensprüfung
smtp_tls_security_level = encrypt[10.10.0.1]:587 internal-mta:GEHEIMpostmap /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
systemctl reload postfixBei einem Exchange als internem MTA denselben Effekt über einen Sendeconnector erreichen: Smarthost [10.10.0.1]:587, Authentifizierung „Basic" über TLS mit den internal-mta-Zugangsdaten.
Credential-Missbrauch begrenzen
Das größte praktische Risiko eines Relays ist nicht ein Postfix-Bug, sondern gestohlene SASL-Zugangsdaten: Damit lässt sich legitim authentifiziert Massenmail versenden. Deshalb gehören die folgenden Maßnahmen zur Standard-Härtung, nicht ins „Optional".
- Pro Anwendung ein eigenes Konto — keine geteilten Zugangsdaten, damit Sperren/Rotation gezielt möglich sind.
- Absender an das Konto binden (siehe unten) — ein Konto darf nur seine freigegebenen Absenderadressen nutzen.
- Rate-Limits (Abschnitt 3) und Monitoring ungewöhnlicher Versandmengen (Abschnitt 12).
- Kurze Credential-Lebensdauer und regelmäßige Rotation; Zugangsdaten nur in geschützten Konfigdateien (
chmod 600).
Absenderbindung (smtpd_sender_login_maps)
Verknüpft jede erlaubte Absenderadresse mit dem berechtigten SASL-Login und lehnt Abweichungen ab.
smtpd_sender_login_maps = hash:/etc/postfix/sender_loginnoreply@example.com internal-mta
info@example.com internal-mta
app1@example.com relay-appIm Submission-Dienst (master.cf, Port 587/465) die Prüfung ergänzen — vor permit_sasl_authenticated:
-o smtpd_sender_restrictions=reject_sender_login_mismatch
-o smtpd_sender_login_maps=hash:/etc/postfix/sender_loginpostmap /etc/postfix/sender_login && systemctl reload postfix06Öffentliche DNS-Einträge
Die Zustellbarkeit steht und fällt mit den DNS-Einträgen. Reihenfolge und Zweck im Überblick:
| Typ | Name | Wert (Beispiel) | Zweck |
|---|---|---|---|
A | mail.example.com | 203.0.113.10 | Adresse des Relays |
AAAA | mail.example.com | 2001:db8::10 | optional, IPv6 |
MX | example.com | 10 mail.example.com. | Eingang für die Domain |
PTR | (IP-Rückwärtszone) | mail.example.com | Reverse-DNS — beim Hoster setzen |
TXT | example.com | v=spf1 a:mail.example.com -all | SPF |
TXT | default._domainkey | v=DKIM1; k=rsa; p=… | DKIM (aus Abschnitt 7) |
TXT | _dmarc.example.com | v=DMARC1; p=quarantine; … | DMARC |
SPF, DMARC & optionale Härtung
# SPF: nur mail.example.com darf für example.com senden
example.com. IN TXT "v=spf1 a:mail.example.com -all"
# DMARC: Verstöße unter Quarantäne, Reports einsammeln
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; fo=1; adkim=s; aspf=s"
# optional: CAA schränkt CAs ein (Let's Encrypt)
example.com. IN CAA 0 issue "letsencrypt.org"Ein fehlender oder unpassender PTR (Reverse-DNS) ist der häufigste Grund für Spam-Einstufung. Der PTR der öffentlichen IP muss exakt mail.example.com ergeben und wird beim IP-Provider gesetzt, nicht in der Domain-Zone.
dig +short MX example.com
dig +short TXT example.com
dig +short TXT default._domainkey.example.com
dig +short -x 203.0.113.10 # PTRErzwungene Transportsicherheit: MTA-STS, DANE, TLS-RPT
Das opportunistische may (Abschnitt 3) verschlüsselt nur, wenn die Gegenstelle mitspielt, und ist gegen STARTTLS-Downgrade anfällig. Wer TLS für ein-/ausgehende Zustellung erzwingen will, ergänzt eine der folgenden Mechaniken — für schützenswerte Kommunikation ausdrücklich empfohlen:
| Mechanismus | Voraussetzung | Wirkung |
|---|---|---|
| MTA-STS | HTTPS-Policy unter mta-sts.example.com + TXT _mta-sts | Sendende MTAs erzwingen TLS zu Ihrem MX (Policy enforce) |
| DANE (TLSA) | DNSSEC für die Zone + TLSA-Record | Bindet das MX-Zertifikat an DNS; stärkster Schutz, aber DNSSEC-Pflicht |
| TLS-RPT | TXT _smtp._tls | Berichte über fehlgeschlagene TLS-Zustellungen |
_mta-sts.example.com. IN TXT "v=STSv1; id=20260101T000000;"
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"version: STSv1
mode: enforce
mx: mail.example.com
max_age: 604800Damit Ihr Relay die Policies fremder Domains ebenfalls durchsetzt (statt nur opportunistisch zu verschlüsseln), lässt sich postfix-mta-sts-resolver als smtp_tls_policy_maps einbinden. DANE-Ausgangsprüfung aktiviert smtp_tls_security_level = dane — setzt aber einen DNSSEC-validierenden Resolver voraus.
07DKIM mit OpenDKIM
DKIM signiert ausgehende Mails kryptografisch. Wir betreiben OpenDKIM als Milter, den Postfix über inet:127.0.0.1:8891 anspricht (bereits in der main.cf hinterlegt).
apt -y install opendkim opendkim-tools
mkdir -p /etc/opendkim/keys/example.com
# 2048-bit-Schlüssel, Selector "default"
opendkim-genkey -b 2048 -d example.com -D /etc/opendkim/keys/example.com -s default -v
chown -R opendkim:opendkim /etc/opendkim
chmod 600 /etc/opendkim/keys/example.com/default.privateSyslog yes
UMask 007
Mode sv
Canonicalization relaxed/simple
Socket inet:8891@127.0.0.1
OversignHeaders From
KeyTable /etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
InternalHosts /etc/opendkim/TrustedHosts
ExternalIgnoreList /etc/opendkim/TrustedHosts# KeyTable
default._domainkey.example.com example.com:default:/etc/opendkim/keys/example.com/default.private
# SigningTable
*@example.com default._domainkey.example.com
# TrustedHosts: nur diese Clients erzeugen SIGNIERTE Mail (Sign-Modus).
# Bewusst NICHT das ganze /24 — konsistent zu "kein IP-Vertrauen über das VPN".
127.0.0.1
::1
10.10.0.2Zwei Mechanismen wirken zusammen, damit nur die richtige Mail signiert wird: InternalHosts setzt OpenDKIM nur für 127.0.0.1/::1 (relay-lokal) und den internen MTA 10.10.0.2 in den Sign-Modus — Mail von fremden Internet-MX (eingehend auf Port 25) wird dagegen nur verifiziert, nie neu signiert. Zusätzlich signiert die SigningTable ausschließlich Mail mit Absender @example.com. So wird fremde eingehende Mail niemals versehentlich mit Ihrer Domain-Signatur versehen.
systemctl enable --now opendkim
systemctl restart postfix
# Diesen TXT-Inhalt in die DNS-Zone übernehmen (Selector default):
cat /etc/opendkim/keys/example.com/default.txtNach DNS-Propagation: opendkim-testkey -d example.com -s default -vvv sollte „key OK" melden.
08TLS-Zertifikate mit acme.sh
acme.sh ist ein schlanker ACME-Client ohne Python-Abhängigkeiten. Für einen Mailserver ohne Webserver ist die DNS-API-Validierung ideal (kein offener Port 80 nötig); alternativ funktioniert der Standalone-Modus.
apt -y install curl socat
curl https://get.acme.sh | sh -s email=admin@example.com
source ~/.bashrc
acme.sh --set-default-ca --server letsencryptexport CF_Token="cf-api-token"
acme.sh --issue --dns dns_cf -d mail.example.com
# Alternative ohne DNS-API (Port 80 muss frei/erreichbar sein):
# acme.sh --issue --standalone -d mail.example.comAnschließend die Zertifikate an den von Postfix erwarteten Ort installieren (nicht direkt aus dem acme.sh-Verzeichnis referenzieren) und einen Reload-Hook setzen. So werden bei jeder Erneuerung automatisch die neuen Dateien eingespielt.
mkdir -p /etc/postfix/tls/mail.example.com
acme.sh --install-cert -d mail.example.com \
--key-file /etc/postfix/tls/mail.example.com/private.key \
--fullchain-file /etc/postfix/tls/mail.example.com/fullchain.cer \
--reloadcmd "systemctl reload postfix"
chmod 600 /etc/postfix/tls/mail.example.com/private.keyacme.sh legt bei der Installation einen cron-Eintrag an, der Zertifikate rechtzeitig erneuert und dank --reloadcmd Postfix neu lädt. Kontrolle: acme.sh --list und crontab -l.
09WireGuard-Tunnel zum internen Mailserver
Der WireGuard-Tunnel verbindet den öffentlichen Relay mit dem internen MTA — verschlüsselt, ohne offenes Relay und ohne den internen Server im Internet zu exponieren. Der Relay hat die öffentliche IP und ist damit der „Endpoint"; der interne Server baut die Verbindung aktiv auf.
apt -y install wireguard
umask 077
wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey
cat /etc/wireguard/publickey # öffentlichen Schlüssel notieren[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <RELAY_PRIVATE_KEY>
[Peer]
# interner Mailserver
PublicKey = <INTERN_PUBLIC_KEY>
AllowedIPs = 10.10.0.2/32[Interface]
Address = 10.10.0.2/24
PrivateKey = <INTERN_PRIVATE_KEY>
[Peer]
# öffentlicher Relay
PublicKey = <RELAY_PUBLIC_KEY>
Endpoint = mail.example.com:51820
AllowedIPs = 10.10.0.1/32
# hält den Tunnel durch NAT/Firewall offen
PersistentKeepalive = 25systemctl enable --now wg-quick@wg0
wg show
ping -c 2 10.10.0.2 # vom Relay ausEingehend nutzt die transport-Map aus Abschnitt 3 die Tunnel-Adresse [10.10.0.2]:25 — hier ist das Relay der Client. Ausgehend ist das Tunnel-Netz bewusst nicht in mynetworks: Der interne MTA meldet sich per SASL auf Port 587 an (siehe Abschnitt 5), sodass auch über den verschlüsselten Tunnel eine Authentifizierung erforderlich ist.
10fail2ban — Schutz gegen Brute-Force
fail2ban wertet die Mail- und SSH-Logs aus und sperrt IPs, die wiederholt fehlschlagende Anmeldungen oder auffälliges SMTP-Verhalten zeigen.
apt -y install fail2ban rsyslog[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
# eigene Netze und den Tunnel niemals sperren
ignoreip = 127.0.0.1/8 ::1 10.10.0.0/24
backend = systemd
[sshd]
enabled = true
[postfix]
enabled = true
mode = aggressive
[postfix-sasl]
enabled = truesystemctl enable --now fail2ban
fail2ban-client status
fail2ban-client status postfix-saslDebian 12 protokolliert standardmäßig ins systemd-journal — daher backend = systemd. Wenn Sie klassische Dateien (/var/log/mail.log) bevorzugen, installieren Sie rsyslog (siehe nächster Abschnitt) und setzen die Jails auf backend = auto.
11logrotate — Logmanagement
Damit die Mail-Logs nicht unbegrenzt wachsen, rotiert logrotate sie täglich, komprimiert ältere Stände und begrenzt die Aufbewahrung — was zugleich der datenschutzrechtlichen Datenminimierung dient.
/var/log/mail.log /var/log/mail.err /var/log/mail.warn {
daily
rotate 30
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate 2>/dev/null || systemctl kill -s HUP rsyslog.service
endscript
}Debian bringt in /etc/logrotate.d/rsyslog bereits Regeln für die Mail-Logs mit. Entfernen Sie dort die /var/log/mail.*-Zeilen, damit nicht zwei Konfigurationen dieselben Dateien rotieren.
acme.sh schreibt sein Log nach ~/.acme.sh/acme.sh.log und begrenzt es selbst; für fail2ban liefert Debian bereits /etc/logrotate.d/fail2ban. Ein manueller Trockenlauf zeigt, was passieren würde:
logrotate -d /etc/logrotate.d/postfix-relay12Inbetriebnahme & Tests
1 · TLS auf Port 587 prüfen
openssl s_client -starttls smtp -crlf -connect mail.example.com:587 -servername mail.example.com2 · Authentifizierten Versand testen (swaks)
apt -y install swaks
swaks --server mail.example.com --port 587 -tls \
--auth LOGIN --auth-user relay-app --auth-password 'GEHEIM' \
--from noreply@example.com --to check-auth@verifier.port25.com3 · SPF/DKIM/DMARC im Ergebnis prüfen
Senden Sie eine Mail an einen Dienst wie mail-tester.com oder an ein Gmail-Postfach und prüfen Sie im Header Authentication-Results: auf spf=pass, dkim=pass und dmarc=pass.
4 · Queue & Betrieb
postqueue -p # Queue anzeigen
postqueue -f # Zustellung erzwingen
postsuper -d ALL deferred # zurückgestellte Mails löschen (mit Bedacht)
journalctl -u postfix -f # Live-LogMonitoring & Alerting
Ein Relay braucht Beobachtung — gerade um kompromittierte Konten (plötzlicher Versand-Spike) und Betriebsprobleme früh zu erkennen. Mindestens diese Signale sollten überwacht und alarmiert werden:
- Queue-Länge (
postqueue -p | tail -1) — Anstieg deutet auf Zustell- oder Missbrauchsprobleme. - Authentifizierungsfehler & Versandmengen — ungewöhnliche Spitzen pro Konto/Client (Hinweis auf gestohlene Credentials).
- Zertifikatsablauf (
openssl x509 -enddate) und erfolgreiche acme.sh-Erneuerung. - Blacklist-Status der ausgehenden IP sowie DMARC-/TLS-RPT-Reports.
- Dienststatus von
postfix,opendkim,wg-quick@wg0,fail2ban.
# Queue-Größe
mailq | grep -c '^[A-F0-9]'
# SASL-Fehlversuche der letzten Stunde
journalctl -u postfix --since "1 hour ago" | grep -c 'SASL .* authentication failed'
# Tage bis Zertifikatsablauf
openssl x509 -enddate -noout -in /etc/postfix/tls/mail.example.com/fullchain.cerFür dauerhaftes Monitoring bieten sich prometheus-postfix-exporter + Alertmanager oder eine schlanke Cron-Lösung mit Schwellwert-Mail an. Wichtig ist vor allem ein Alarm bei anomaler Versandmenge — das ist die wirksamste Erkennung von Credential-Missbrauch.
13Troubleshooting
| Symptom | Wahrscheinliche Ursache | Ansatz |
|---|---|---|
relay access denied | Absender weder in mynetworks noch SASL-authentifiziert | Auf 587 senden und Auth prüfen; mynetworks kontrollieren |
SASL authentication failed | sasldb2 im chroot nicht lesbar / falscher Realm | Bind-Mount/Kopie nach /var/spool/postfix/etc; Realm = myhostname |
| TLS-Fehler beim Start | Zertifikatspfad falsch oder Key nicht lesbar | postfix check; Pfade & chmod 600 auf den Key |
| Mails landen im Spam | PTR-Mismatch, fehlendes DKIM/DMARC | dig -x, opendkim-testkey, Header prüfen |
| Interner MTA unerreichbar | Tunnel unten / AllowedIPs falsch | wg show, ping 10.10.0.2, Firewall 51820/udp |
journalctl -u postfix --since "10 min ago" | grep -Ei 'warning|error|reject|sasl|tls'14Datenschutz & Betrieb (DSGVO)
- Datenminimierung: Mail-Logs enthalten Absender-, Empfänger- und IP-Daten. Die Aufbewahrung über logrotate (hier 30 Tage) sollte dem tatsächlichen Betriebsbedarf entsprechen und dokumentiert sein.
- Auftragsverarbeitung: Läuft der Relay bei einem Hoster, gehört dieser als Auftragsverarbeiter mit AVV und — bei Standort außerhalb der EU — passendem Übermittlungsmechanismus ins Verzeichnis der Verarbeitungstätigkeiten.
- Transportverschlüsselung: STARTTLS ist konfiguriert; für sensible Kommunikation kann
smtp_tls_security_levelselektiv aufencryptgehoben oder MTA-STS ergänzt werden. - Zugriff: SASL-Konten und WireGuard-Schlüssel sind Zugangsdaten — restriktive Dateirechte, getrennte Konten je Anwendung und regelmäßige Rotation.
Diese Dokumentation beschreibt einen technischen Aufbau und ersetzt keine rechtliche Beratung. Die konkrete DSGVO-Bewertung ist im Einzelfall zu treffen.