Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung E-Mail / Mailserver 07.11.2026 · 13 min Lesezeit Geprüft 07.11.2026

MTA-STS, TLS-RPT und DMARC-Berichte: den eigenen Mailserver absichern und auswerten

Teil 12 der Reihe Server im Netz: MTA-STS mit Caddy ausliefern, TLS-RPT und DMARC-Berichte empfangen und mit einem Skript lesen, dann von testing auf enforce und von p=none auf eine strengere DMARC-Stufe. Mit echten Abrufen durch Microsoft und Google und dem ersten Bericht von Google.

Werbung Mit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Mehr dazu

Symbolbild: Rack-Server mit Netzwerkkabel zu Briefumschlag mit Schloss und Berichtsblatt

Mit SPF, DKIM und DMARC aus Teil 11 weiß ein fremder Mailserver, ob eine Mail wirklich von Ihrer Domain kommt. Ob die Verbindung zu Ihrem Server verschlüsselt sein muss, weiß er damit noch nicht, und ob irgendwo Mails mit Ihrer Domain scheitern, erfahren Sie auch nicht. Dafür gibt es MTA-STS, TLS-RPT und die Berichte von DMARC. Diese Anleitung richtet MTA-STS mit Caddy ein, zeigt, wer die Policy wirklich abruft, und wertet den ersten echten Bericht aus, er kam von Google. Erst danach geht es von „testing“ auf „enforce“ und bei DMARC von „none“ auf eine strengere Stufe.

Dies ist Teil 12 der Reihe „Server im Netz“. Als Beispiel dient der Mailserver Stalwart aus Teil 11 auf einem VPS mit Caddy.

Was MTA-STS, TLS-RPT und DMARC-Berichte leisten

  • MTA-STS sagt anderen Mailservern: Liefere an diese Domain nur über TLS mit gültigem Zertifikat an die genannten MX-Server. Im Modus testing wird nur berichtet, im Modus enforce wird bei Fehlern nicht zugestellt.
  • TLS-RPT nennt die Adresse, an die sendende Server einmal am Tag melden, wie viele Verbindungen mit TLS geklappt haben und welche nicht.
  • DMARC-Aggregatberichte kommen von Empfängern Ihrer Mails. Sie zeigen, von welchen Adressen Mails mit Ihrer Domain kamen und ob SPF und DKIM gepasst haben.

DANE aus DANE für den eigenen Mailserver verfolgt dasselbe Ziel wie MTA-STS, setzt aber DNSSEC voraus. MTA-STS braucht stattdessen HTTPS.

Voraussetzungen

  • Ein Mailserver mit gültigem Zertifikat für den MX-Namen, hier mail.sedv-lab.de, wie in Teil 11
  • Caddy für HTTPS wie in Teil 4, mit einem DNS-Eintrag, der mta-sts.ihre-domain auf den Server zeigt. Im Beispiel deckt ihn ein Wildcard-Eintrag schon ab.
  • Zugriff auf die DNS-Zone
  • Eine zustellbare Adresse für die Berichte, im Beispiel postmaster@sedv-lab.de

Schritt 1: Die Policy über Caddy ausliefern

Die Policy muss unter genau diesem Namen per HTTPS erreichbar sein. Eine eigene Website braucht es dafür nicht, Caddy liefert den Text direkt aus. Ergänzen Sie im Caddyfile:

mta-sts.sedv-lab.de {
	log {
		output file /log/mta-sts.json
		format json
	}
	handle /.well-known/mta-sts.txt {
		header Content-Type "text/plain; charset=utf-8"
		respond <<ENDE
			version: STSv1
			mode: testing
			mx: mail.sedv-lab.de
			max_age: 86400
			ENDE 200
	}
	respond 404
}

Das eigene Log zeigt später, wer die Policy abruft. Auf dem Host liegt es dort, wo Ihr Container /log einbindet, im Beispiel /root/sin/web/log/. Im Beispiel stehen in dem Block außerdem die Schutzregeln aus den vorigen Teilen. Der Modus testing ist der sichere Anfang, max_age legt fest, wie lange Sender die Policy zwischenspeichern dürfen, hier ein Tag.

Wählen Sie für das Heredoc eine Endmarke, die im Text nicht vorkommt. STS scheiterte im Test an STSv1.

$ docker exec caddy caddy validate --config /etc/caddy/Caddyfile && docker exec caddy caddy reload --config /etc/caddy/Caddyfile
$ curl -s https://mta-sts.sedv-lab.de/.well-known/mta-sts.txt

Der Abruf liefert die vier Zeilen mit HTTP 200. Das Zertifikat für den neuen Namen holte Caddy im Test innerhalb von Sekunden.

Schritt 2: Die DNS-Einträge setzen

Zwei TXT-Einträge gehören dazu. Der erste kündigt die Policy an. Seine id ändern Sie bei jeder Änderung der Policy, sonst holen Sender die neue Fassung erst nach Ablauf von max_age. Ein Zeitstempel eignet sich gut:

_mta-sts.sedv-lab.de.   3600  IN TXT  "v=STSv1; id=20261106T1338"
_smtp._tls.sedv-lab.de. 3600  IN TXT  "v=TLSRPTv1; rua=mailto:postmaster@sedv-lab.de"

Der DMARC-Eintrag aus Teil 11 nennt dieselbe Adresse:

_dmarc.sedv-lab.de.     3600  IN TXT  "v=DMARC1; p=none; rua=mailto:postmaster@sedv-lab.de"

Fragen Sie nach dem Speichern zuerst den autoritativen Nameserver. Im Test war der Eintrag dort nach knapp fünf Minuten sichtbar. Eine Abfrage bei öffentlichen Resolvern davor rächte sich: Zwei von drei merkten sich die leere Antwort für die Dauer aus dem SOA-Eintrag, bei uns bis zu 3600 Sekunden.

$ dig +short TXT _mta-sts.sedv-lab.de @$(dig +short NS sedv-lab.de | head -1)

Der autoritative Server liefert den Eintrag mit Ihrer id.

Schritt 3: Von außen prüfen

Der MTA-STS-Test von MXToolbox prüft Eintrag, Abruf, Zertifikat, Syntax der Policy und den MX-Server. Im Test waren alle acht Prüfungen grün.

MXToolbox MTA-STS-Test für sedv-lab.de mit acht bestandenen PrüfungenMXToolbox, MTA-STS-Test für sedv-lab.de: alle acht Prüfungen bestanden, vom DNS-Eintrag über den Abruf der Policy bis zum TLS des MX-Servers.

Alle Prüfungen sind grün. Meldet der Test einen Fehler beim Abruf, prüfen Sie Zertifikat und Content-Type der Policy.

Schritt 4: Wer die Policy wirklich abruft

Das Log aus Schritt 1 zeigt es. Im Test schickten wir Mails aus Microsoft 365 und von outlook.de an den Server. In beiden Fällen holte Microsoft die Policy in derselben Sekunde, in der die SMTP-Verbindung kam, über IPv6 und ohne User-Agent. Die Mail kam dann mit TLS 1.3 an. Das zweite Microsoft-System holte die Policy erneut, obwohl das erste sie anderthalb Minuten vorher schon geladen hatte. Google holte die Policy bei einer Testmail aus Gmail ebenfalls während der Zustellung, aber über IPv4, die Mail selbst kam über IPv6.

$ sudo jq -r 'select(.request.uri=="/.well-known/mta-sts.txt") | [.ts, .request.remote_ip, .status] | @tsv' /root/sin/web/log/mta-sts.json

In den ersten 18 Stunden kamen auf den neuen Namen außerdem 1.244 andere Anfragen, von Scannern auf die Startseite und auf Pfade wie /.env. Die ersten im Log trafen etwa zehn Minuten nach dem Zertifikat ein. Der Block antwortet darauf mit 404, die Schutzregeln sperren einen Teil mit 403.

Nach einer Testmail von einem großen Anbieter steht ein Abruf mit Status 200 im Log.

Schritt 5: Sofort prüfen, statt auf Berichte zu warten

Die Berichte kommen meist einmal am Tag, für den Vortag. Anfordern lassen sie sich nicht. Ob SPF, DKIM und DMARC für eine einzelne Mail stimmen, zeigt ein Prüfdienst sofort. Auf learndmarc.com erscheint eine Empfangsadresse, an die Sie eine Mail von Ihrem Server schicken. Die Auswertung erscheint danach auf der Seite.

learndmarc.com zeigt SPF, DKIM RSA, DKIM Ed25519 und DMARC als bestandenlearndmarc.com, Auswertung einer Mail vom Testserver: SPF und beide DKIM-Signaturen (RSA und Ed25519) bestanden, jeweils passend zur Absenderdomain. Darunter meldet die Seite DMARC PASS.

Im Test bestanden SPF, die RSA-Signatur und die Ed25519-Signatur, DMARC damit auch. Die ältere Prüfadresse check-auth@verifier.port25.com taugte nicht: Ihr Zertifikat war abgelaufen, Stalwart verweigerte die Zustellung mit certificate expired und behielt die Mail zum erneuten Versuch in der Warteschlange. Löschen Sie solche Mails dort, sonst versucht der Server es weiter.

Wie ein großer Anbieter die Mail sieht, zeigt Gmail unter „Original anzeigen“. Bei unserer Antwort auf eine Gmail-Mail stand dort:

dkim=neutral (no key) header.s=v1-ed25519-20261105
dkim=pass header.s=v1-rsa-20261105
spf=pass (google.com: domain of lab@sedv-lab.de designates 62.83.99.6 as permitted sender)
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=sedv-lab.de

Trotz bestandener Prüfungen landete die Mail im Spam, auch eine zweite Antwort, nachdem die erste als „Kein Spam“ markiert war. Die IP stand auf keiner der fünf geprüften Sperrlisten. Ob sie im Posteingang landet, entscheidet der Empfänger nach seinem eigenen Urteil über Domain und Server.

Der Prüfdienst zeigt für SPF, DKIM und DMARC jeweils PASS.

Schritt 6: Berichte empfangen und lesen

Prüfen Sie zuerst, ob die Berichtsadresse Mails annimmt. Im Beispiel ist postmaster@ ein Alias des Postfachs, eine Testmail landete sofort im Posteingang. Fehlt die Adresse, kommen nie Berichte an, und niemand merkt es.

Die Berichte kommen als Mail mit Anhang: DMARC als XML, gepackt als .zip oder .xml.gz, TLS-RPT als JSON in .json.gz. Im Test traf der erste Bericht gegen Mittag des Folgetags ein. Absender war Google, Betreff Report domain: sedv-lab.de Submitter: google.com. Er deckte den Vortag von 0 bis 24 Uhr UTC ab. Microsoft und TLS-RPT-Berichte waren bis dahin nicht dabei.

Lesen lässt sich das XML direkt, übersichtlicher wird es mit einem kleinen Skript. Es entpackt den Anhang selbst und gibt je Absender-IP eine Zeile aus. Speichern Sie es als auswert.py und übergeben Sie die gespeicherten Anhänge:

#!/usr/bin/env python3
"""DMARC- (XML) und TLS-RPT-Berichte (JSON) lesen, auch gepackt. Aufruf: auswert.py DATEI..."""
import gzip, io, json, sys, zipfile
import xml.etree.ElementTree as ET
from datetime import datetime, timezone


def roh(p):
    b = open(p, "rb").read()
    if b[:2] == b"\x1f\x8b": return gzip.decompress(b)
    if b[:2] == b"PK":
        z = zipfile.ZipFile(io.BytesIO(b)); return z.read(z.namelist()[0])
    return b


def zeit(s): return datetime.fromtimestamp(int(s), timezone.utc).strftime("%d.%m. %H:%M")


def dmarc(x):
    r = ET.fromstring(x)
    md = r.find("report_metadata"); pol = r.find("policy_published")
    print(f"DMARC von {md.findtext('org_name')}, Zeitraum {zeit(md.findtext('date_range/begin'))} bis {zeit(md.findtext('date_range/end'))} UTC, Policy p={pol.findtext('p')}")
    for rec in r.findall("record"):
        row = rec.find("row"); pe = row.find("policy_evaluated"); au = rec.find("auth_results")
        dk = ",".join(f"{d.findtext('domain')}/{d.findtext('selector')}={d.findtext('result')}" for d in au.findall("dkim")) or "-"
        sp = ",".join(f"{d.findtext('domain')}={d.findtext('result')}" for d in au.findall("spf")) or "-"
        print(f"  Quelle {row.findtext('source_ip')}  Mails {row.findtext('count')}  Ergebnis dkim={pe.findtext('dkim')} spf={pe.findtext('spf')} disposition={pe.findtext('disposition')}  Header-From {rec.findtext('identifiers/header_from')}  DKIM {dk}  SPF {sp}")


def tlsrpt(x):
    d = json.loads(x)
    dr = d.get("date-range", {})
    print(f"TLS-RPT von {d.get('organization-name')}, Zeitraum {dr.get('start-datetime')} bis {dr.get('end-datetime')}")
    for p in d.get("policies", []):
        pol = p.get("policy", {}); s = p.get("summary", {})
        print(f"  Policy {pol.get('policy-type')} {pol.get('policy-domain')}  erfolgreich {s.get('total-successful-session-count')}  fehlgeschlagen {s.get('total-failure-session-count')}")
        for f in p.get("failure-details", []) or []:
            print(f"    Fehler {f.get('result-type')} von {f.get('sending-mta-ip')} an {f.get('receiving-mx-hostname')}: {f.get('failed-session-count')}")


for p in sys.argv[1:]:
    x = roh(p)
    print("==", p.split("/")[-1])
    (tlsrpt if x.lstrip()[:1] == b"{" else dmarc)(x)
$ python3 auswert.py ~/Downloads/*sedv-lab.de*.zip
== 2026-11-07T104516Z_google.com!sedv-lab.de!1793923200!1794009599.zip
DMARC von google.com, Zeitraum 06.11. 00:00 bis 06.11. 23:59 UTC, Policy p=none
  Quelle 62.83.99.6  Mails 2  Ergebnis dkim=pass spf=pass disposition=none  Header-From sedv-lab.de  DKIM sedv-lab.de/v1-ed25519-20261105=fail,sedv-lab.de/v1-rsa-20261105=pass  SPF sedv-lab.de=pass

So lesen Sie die Zeile:

  • Quelle ist die IP, von der die Mails kamen. Steht hier eine Adresse, die Sie nicht kennen, verschickt jemand Mails mit Ihrer Domain, oder ein Dienst wie ein Newsletter-Anbieter fehlt noch im SPF-Eintrag. Auch weitergeleitete Mails, etwa über eine Mailingliste, tauchen so auf.
  • Ergebnis ist die Bewertung für DMARC. Für pass genügt eine der beiden Prüfungen, wenn ihre Domain zur Absenderdomain passt.
  • DKIM listet jede Signatur einzeln mit ihrem Selektor.

Hier steht eine Überraschung: Google meldete die Ed25519-Signatur als fail. Im Header der späteren Antwort an Gmail stand für denselben Selektor neutral (no key). learndmarc hatte dieselbe Signatur als gültig bestätigt. Google prüft Ed25519 also nicht. Das Ergebnis für DMARC blieb pass, weil die RSA-Signatur trug. Signieren Sie deshalb nie nur mit Ed25519.

Im Bericht stehen nur Ihre eigenen Server-IPs, und bei jeder steht unter Ergebnis mindestens einmal pass.

Schritt 7: Von testing auf enforce

Mit enforce liefert ein Sender, der MTA-STS beachtet, nur noch zu, wenn TLS und Zertifikat passen. Stellen Sie erst um, wenn die Prüfung bei MXToolbox grün ist und die TLS-RPT-Berichte Ihrer wichtigsten Absender keine Fehler zeigen. Im Test haben wir nach einem Tag umgestellt, obwohl noch kein TLS-RPT-Bericht da war, weil die Domain nur Testmails bekommt und alle Abrufe sauber waren. Ändern Sie im Caddyfile die Zeilen mode und max_age:

			mode: enforce
			max_age: 604800

Sieben Tage für max_age sind ein Mittelweg. So lange halten Sender die Policy fest, auch wenn sie die Datei zwischendurch nicht laden können. Einen neuen MX-Namen tragen Sie in die Policy ein und ändern die id, dann holen Sender sie neu. Wollen Sie MTA-STS abschalten, veröffentlichen Sie mode: none mit neuer id. Den TXT-Eintrag nur zu löschen genügt nicht. Halten Sie den Wert klein, solange sich am Mailserver noch etwas ändert.

$ docker exec caddy caddy validate --config /etc/caddy/Caddyfile && docker exec caddy caddy reload --config /etc/caddy/Caddyfile
$ docker exec caddy grep -n "mode:" /etc/caddy/Caddyfile
$ curl -s https://mta-sts.sedv-lab.de/.well-known/mta-sts.txt

Im Test lieferte Caddy nach dem Reload noch mode: testing: sed -i legt eine neue Datei an, der Container sah über den Einzeldatei-Mount weiter die alte. Die zweite Zeile oben zeigt das. Abhilfe ist docker restart caddy, die Website war dafür rund 14 Sekunden weg. Binden Sie besser den Ordner statt der Datei ein.

Dann bekommt der Eintrag _mta-sts eine neue id, sonst holen Sender die Policy erst nach Ablauf des alten max_age neu:

_mta-sts.sedv-lab.de.   3600  IN TXT  "v=STSv1; id=20261107T1147"

In der CloudDNS-API von netcup gibt es kein Ändern, nur Löschen und Anlegen. Beides geht in einem Changeset mit delete und create, beides wird mit einem Aufruf übernommen.

Die Policy zeigt mode: enforce, der autoritative Nameserver die neue id, und der Test bei MXToolbox bleibt grün.

Schritt 8: DMARC strenger stellen

Mit p=none berichten Empfänger nur. Mit p=quarantine bitten Sie, Mails, die DMARC nicht bestehen, als verdächtig zu behandeln, meist landen sie im Spam. Mit p=reject sollen Empfänger sie ablehnen. Subdomains ohne eigenes sp= erben die Stufe. Voraussetzung für den Schritt: In den Berichten stehen nur eigene Quellen, und alle bestehen. Im Test lag ein Bericht vor, von Google, und darin stand nur der Server selbst.

_dmarc.sedv-lab.de.     3600  IN TXT  "v=DMARC1; p=quarantine; rua=mailto:postmaster@sedv-lab.de"

Den Eintrag haben wir im selben Changeset wie die neue id getauscht. Bleiben die Berichte danach einige Wochen sauber, folgt p=reject. Verschickt ein weiterer Dienst Mails mit Ihrer Domain, etwa ein Shop oder ein Newsletter, richten Sie vorher SPF oder besser DKIM mit Ihrer Domain für ihn ein. Sonst behandeln Empfänger genau diese Mails als verdächtig.

$ dig +short TXT _dmarc.sedv-lab.de @$(dig +short NS sedv-lab.de | head -1)

Der autoritative Server liefert p=quarantine. Im nächsten Bericht steht unter Policy p=quarantine.

Typische Fehler

Das Heredoc mit der Endmarke STS scheitert

Die Endmarke steht schon in STSv1. Wählen Sie eine Endmarke, die im Text nicht vorkommt, im Beispiel ENDE.

Öffentliche Resolver liefern den neuen TXT-Eintrag nicht

Sie haben dort gefragt, bevor der Eintrag beim autoritativen Nameserver sichtbar war. Zwei von drei Resolvern merkten sich die leere Antwort für die Dauer aus dem SOA-Eintrag, im Test bis zu 3600 Sekunden. Fragen Sie zuerst den autoritativen Nameserver.

MXToolbox meldet einen Fehler beim Abruf

Prüfen Sie Zertifikat und Content-Type der Policy.

Nach dem Reload liefert Caddy noch mode: testing

sed -i legt eine neue Datei an, der Container sieht über den Einzeldatei-Mount weiter die alte. Abhilfe ist docker restart caddy. Binden Sie besser den Ordner statt der Datei ein.

Sender holen die geänderte Policy nicht sofort

Die id im Eintrag _mta-sts ist gleich geblieben. Sender holen die neue Fassung dann erst nach Ablauf von max_age. Ändern Sie die id bei jeder Änderung der Policy.

certificate expired

Die Prüfadresse check-auth@verifier.port25.com hatte ein abgelaufenes Zertifikat, Stalwart behielt die Mail in der Warteschlange. Löschen Sie die Mail dort und nehmen Sie einen anderen Prüfdienst wie learndmarc.com.

dkim=neutral (no key) für die Ed25519-Signatur

Google prüft Ed25519 nicht und meldete die Signatur im DMARC-Bericht als fail. DMARC blieb im Test pass, weil die RSA-Signatur trug. Signieren Sie nie nur mit Ed25519.

Häufige Fragen

Wie lange sollte MTA-STS auf testing bleiben?

So lange, bis die TLS-RPT-Berichte Ihrer wichtigsten Absender einige Tage ohne Fehler kommen, oft ein bis zwei Wochen.

Brauche ich MTA-STS, wenn DANE schon läuft?

Ein Sender nutzt, was er unterstützt. Im Test holte Microsoft die MTA-STS-Policy vor jeder Zustellung, obwohl für den Server auch DANE eingerichtet war. Sender, die DANE nicht prüfen, haben ohne MTA-STS keine Vorgabe für TLS.

Wer bekommt die DMARC-Berichte über meine Mails?

Die Adresse aus rua im eigenen DMARC-Eintrag. Ihr eigener Server verschickt umgekehrt Berichte an andere: Stalwart bereitete im Test kurz nach Mitternacht einen Bericht für die Domain vor, von der am Vortag eine Mail eingegangen war.

Fazit

MTA-STS ist schnell eingerichtet: eine Textdatei über Caddy und zwei TXT-Einträge. Microsoft holte die Policy im Test bei jeder Zustellung, Google bei der einen Testmail ebenfalls. Die Berichte brauchen Geduld, für einzelne Mails hilft ein Prüfdienst sofort. Der erste Bericht von Google zeigte, dass erst die zweite Signatur mit RSA DKIM bestehen ließ. Bestandene Prüfungen belegen, dass eine Mail echt ist. Ob sie im Posteingang landet, entscheidet der Empfänger.

Weiterführende Anleitungen und Quellen

MTA-STSTLS-RPTDMARCMailserverStalwartCaddyServer im Netz