Schematische Darstellung eines Postfix-Mailservers mit TLS-Absicherung über DANE, MTA-STS und Zertifikatsprüfung. Ein unsicherer Klartext-Fallback durch STARTTLS-Downgrade wird blockiert.

Mein Mailserver hat seit Jahren DANE mit DNSSEC, MTA-STS und seit diesem Jahr auch einen Post-Quantum-Schlüsselaustausch. Trotzdem hat er heute eine Testmail im Klartext ausgeliefert. Das war kein Bug, sondern genau das Verhalten, das in der Postfix-Doku steht. In diesem Beitrag geht es darum, warum Postfix die Policy „DANE wenn vorhanden, sonst ein geprüftes Zertifikat“ nicht von sich aus kann, warum ich dafür unter FreeBSD nichts Fertiges gefunden habe und wie mein kleiner Dienst postfix-dane-policy diese Lücke schließt.

Der Auslöser: ein Downgrade-Test

Der Mail-Server-Test von comcrypto simuliert unter anderem einen STARTTLS-Downgrade, den eigenen Server kannst du dort also selbst prüfen. Man schickt eine Mail an eine Testadresse, und der Empfängerserver lässt den TLS-Handshake absichtlich scheitern. So würde es auch ein aktiver Angreifer zwischen zwei Mailservern machen: STARTTLS stören und schauen, ob der Absender danach ohne Verschlüsselung weitermacht. Mein Postfix hat genau das getan:

postfix/smtp[5497]: 2BD2E39781: to=<xxxx@evaluation.comcrypto.de>, relay=smtp-in-nbg1pr01loearatl.mxg.cloud[37.120.186.126]:25, delay=1.2, tls=may?, dsn=4.7.5, status=deferred (Cannot start TLS: handshake failure)
postfix/smtp[6005]: 2BD2E39781: to=<xxxx@evaluation.comcrypto.de>, relay=smtp-in-nbg1pr01loearatl.mxg.cloud[37.120.186.126]:25, delay=490, tls=may:none, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C7DC6E00C5)

Erster Versuch: Handshake scheitert, Mail wird zurückgestellt. Gut acht Minuten später der zweite Versuch, und da steht tls=may:none mit status=sent. Die Mail ist im Klartext beim Empfänger angekommen. Ein Angreifer, der STARTTLS stört, hätte sie also nur ein paar Minuten lang aufhalten müssen.

Das ärgert mich vor allem, weil ich dachte das Thema wäre bei mir erledigt. Ist es aber nur für Empfänger, die selbst DANE oder MTA-STS veröffentlichen. Für alle anderen gilt opportunistisches TLS nach RFC 7435: verschlüsseln, wenn es geht, und wenn nicht, dann eben nicht. Und das ist immer noch ein großer Teil der Mailserver da draußen.

Warum Postfix das allein nicht kann

Die ausgehende TLS-Stufe stellt man in Postfix mit smtp_tls_security_level ein. Die Manpage postconf(5) von Postfix 3.11.7 ist bei den einzelnen Stufen sehr deutlich. Zu may steht dort:

after a failed TLS handshake or TLS session, fall back to plaintext if the message has spent minimal_backoff_time in the mail queue

Genau das zeigt mein Log oben. Bei mir stand allerdings gar nicht may in der main.cf, sondern dane. Dazu sagt die Manpage:

When DNSSEC-validated TLSA records are not found the effective tls security level is „may“.

Mit dane bekommt also jeder Empfänger ohne TLSA-Record wieder may, inklusive Klartext-Fallback. Die naheliegende Idee wäre jetzt, global auf verify oder secure zu gehen. Beide Stufen ignorieren TLSA-Records aber komplett. Das hätte zwei unschöne Folgen:

  • Empfänger mit DANE-EE und einem selbst signierten Zertifikat wären plötzlich nicht mehr erreichbar. Das ist bei kleinen Selbsthostern gar nicht so selten und mit DANE völlig legitim, das Vertrauen kommt dort ja aus dem DNSSEC-gesicherten TLSA-Record und nicht von einer CA.
  • DANE-Empfänger würden nur noch gegen die CA geprüft und nicht mehr gegen ihr TLSA. Damit verschenkt man genau den Schutz, den DANE bietet.

Was ich eigentlich will, ist eine Stufe „DANE, wenn der Empfänger es veröffentlicht, sonst verify“. Die gibt es nicht. Viktor Dukhovni, der DANE in Postfix gebaut hat, schrieb dazu schon 2021 auf der exim-users-Liste: „It isn’t currently possible to do ‚dane‘ else ‚encrypt‘. Need a dane-fallback level“. Auch Postfix 3.11 und die 3.12-Snapshots bis August 2026 haben so etwas nicht.

Gibt es das schon fertig?

Postfix kann die Entscheidung aber pro Ziel treffen lassen. Über smtp_tls_policy_maps fragt Postfix vor jeder Zustellung eine Tabelle, welche TLS-Stufe für die Zieldomain gelten soll. Diese Tabelle darf auch ein Dienst sein, der live im DNS nachschaut. Bevor ich selbst etwas baue, habe ich also gesucht:

  • postfix-mta-sts-resolver von Snawoot kann nur MTA-STS, DANE ist dort bewusst außen vor (Issue #67). Den nutze ich seit 2019, damals habe ich das Logfile und ein rc.d-Skript für FreeBSD beigesteuert. Inzwischen gibt es ihn als FreeBSD-Port und er bleibt bei mir im Einsatz, jetzt als zweite Map.
  • postfix-tlspol von Zuplu kommt am nächsten: DANE und MTA-STS in einem Go-Daemon. Ein gutes Projekt, nur eben nicht für FreeBSD gedacht. Es gibt keinen Port, Release-Binaries habe ich nur für Linux gefunden, der Build braucht ein sehr aktuelles Go, und die Installationswege sind auf systemd oder Docker ausgelegt. Dazu liefert tlspol bei gemischten MX-Sets nach meinem Stand dane statt dane-only, womit dort wieder may möglich ist.
  • Mailu nutzt intern eine ähnliche Kette, setzt global aber dane, ohne DANE oder MTA-STS landet man also wieder bei may. Und es ist Docker. Bei mailcow, docker-mailserver, Mail-in-a-Box, iRedMail und Modoboa habe ich nichts Vergleichbares gefunden.
  • Im FreeBSD-Paket-Repository steckt DANE natürlich in Postfix selbst, dazu gibt es Werkzeuge wie letsdns. Das verwaltet aber die eigenen TLSA-Records, kümmert sich also um die andere Seite.

Für Postfix unter FreeBSD habe ich Stand jetzt also nichts Fertiges gefunden. Dann eben selbst bauen.

postfix-dane-policy

postfix-dane-policy ist ein kleiner Python-Dienst (Python 3.10 oder neuer, dazu dnspython). Er spricht das socketmap-Protokoll von Postfix, das sind simple Netstrings über einen Unix-Socket, und wird in smtp_tls_policy_maps eingehängt. Er beantwortet genau eine Frage: Hat diese Zieldomain nutzbares DANE? Alles andere bleibt bei Postfix und beim MTA-STS-Resolver.

Für jede Next-Hop-Domain holt der Dienst den MX-RRset und für jeden MX-Host den TLSA-RRset unter _25._tcp. Ist ein MX-Host ein per DNSSEC validierter CNAME, macht er es wie Postfix und RFC 7672: Er sucht zuerst am Zielnamen, dann am ursprünglichen Namen, der erste gesicherte TLSA-RRset entscheidet, und ein Fehler beim Lookup bricht die Suche ab. Gezählt werden nur brauchbare Records, also Usage 2 oder 3, Selector 0 oder 1 und Matching-Type 0, 1 oder 2 mit passender Länge. Dann entscheidet er so:

SituationAntwortWirkung in Postfix
MX per DNSSEC validiert und mindestens ein MX-Host hat einen validierten TLSA-RRset mit brauchbaren RecordsOK dane-onlyDANE ist Pflicht, kein Fallback
MX nicht validiert, kein TLSA, nur unbrauchbare TLSA-Records, Null-MX, Domain existiert nichtNOTFOUNDnächste Map (MTA-STS) oder die globale Stufe
SERVFAIL, bogus DNSSEC oder Timeout beim MX-Lookup, oder beim TLSA-Lookup, wenn kein anderer MX brauchbares TLSA hatTEMPZustellung wird zurückgestellt, nie herabgestuft
Elterndomain (.example.com) oder [host]:port als Next-HopNOTFOUND

Die eigentliche Logik steckt in einer einzigen Funktion. Gekürzt sieht decide() aus policy.py so aus:

def decide(domain, backend, deadline=None):
    """Return (result, ttl, reason) for a normalized next-hop domain."""
    mx = query(domain, "MX")
    if mx.status == "fail":
        return RESULT_TEMP, 0, "MX lookup failed"
    if mx.status == "nxdomain":
        return RESULT_NONE, NEGATIVE_TTL, "domain does not exist"
    if not mx.secure:
        return RESULT_NONE, NEGATIVE_TTL, "MX not DNSSEC-validated"

    # ... null MX, implicit MX (no MX record = domain itself) ...

    tlsa_hosts = []
    failed_hosts = []
    for host in hosts:
        # follows a secure CNAME, counts only usable TLSA records
        found, failed, ttl = _tlsa_for_host(host, query)
        if found:
            tlsa_hosts.append(host)
            ttls.append(ttl)
        elif failed:
            failed_hosts.append(host)

    if tlsa_hosts:
        ttl = max(POSITIVE_TTL_MIN, min([POSITIVE_TTL_MAX] + ttls))
        return RESULT_DANE, ttl, "secure TLSA on " + ",".join(tlsa_hosts)
    if failed_hosts:
        return RESULT_TEMP, 0, "TLSA lookup failed for " + ",".join(failed_hosts)
    return RESULT_NONE, NEGATIVE_TTL, "no secure TLSA"

Ein paar Entscheidungen dahinter sind mir wichtig:

  • Der Dienst validiert DNSSEC nicht selbst. Er vertraut dem AD-Bit eines lokalen, validierenden Resolvers. Unter FreeBSD ist das typischerweise local_unbound aus dem Basissystem, das lauscht schon auf 127.0.0.1 und passt damit zu den Standardwerten des Dienstes.
  • Gemischte MX-Sets bekommen trotzdem dane-only. Haben nur manche MX-Hosts einen TLSA-Record, stellt Postfix dann nur an die Hosts zu, die es per DANE authentifizieren kann, und überspringt die anderen. Bei dane würde es auf ungeprüftes TLS zurückfallen. Das ist der bewusste Unterschied zu tlspol.
  • Fehler führen zu „später nochmal“. Ein SERVFAIL oder bogus DNSSEC ist genau das, was ein Angreifer am DNS provozieren würde. Darauf antwortet der Dienst mit TEMP, und Postfix stellt die Mail zurück. Fällt nur der TLSA-Lookup bei einem MX aus, während ein anderer MX brauchbares TLSA hat, antwortet er dane-only. Postfix prüft dann jeden Host selbst. Ist der Dienst weg, kann Postfix die Map nicht abfragen und stellt ebenfalls zurück. Setzt der Resolver beim regelmäßigen Selbsttest gegen die signierte Root-Zone kein AD-Bit, antwortet der Dienst grundsätzlich mit TEMP. In keinem Fall wird herabgestuft.
  • Cache im Speicher. Positive Antworten leben so lange wie die TTL der Records, mindestens 60 Sekunden und höchstens eine Stunde. Negative Antworten werden 5 Minuten gehalten, TEMP nie. Jede Entscheidung landet per syslog im maillog.
  • Nur Postfix darf fragen. Der Dienst läuft als eigener User danepolicy, den der Port anlegt. Der Socket liegt in einem Verzeichnis, das danepolicy:postfix gehört und Modus 0750 hat. Andere lokale Prozesse kommen nicht dran, und es gibt Limits für gleichzeitige Verbindungen und die Dauer der Lookups.

So sieht das im maillog aus:

postfix-dane-policy[13399]: domain=posteo.de result=dane reason=secure TLSA on mx01.posteo.de,mx02.posteo.de,mx03.posteo.de,mx04.posteo.de
postfix-dane-policy[13399]: domain=gmail.com result=none reason=MX not DNSSEC-validated
postfix-dane-policy[13399]: domain=evaluation.comcrypto.de result=none reason=no secure TLSA

Das Zusammenspiel in der main.cf

Der eigentliche Trick ist die Kombination aus Policy-Maps und globaler Stufe:

smtp_tls_security_level = verify
smtp_dns_support_level = dnssec
smtp_tls_policy_maps =
    socketmap:unix:/var/run/postfix_dane_policy/policy.sock:dane,
    socketmap:inet:127.0.0.1:8461:mta-sts
smtp_tls_CAfile = /usr/local/share/certs/ca-root-nss.crt
smtp_tls_mandatory_protocols = >=TLSv1.2
tls_required_enable = no
delay_warning_time = 4h

Postfix fragt die Maps der Reihe nach, der erste Treffer gewinnt. Zuerst kommt postfix-dane-policy. Hat der Empfänger DANE, gilt dane-only. Sonst fragt Postfix den MTA-STS-Resolver, und veröffentlicht der Empfänger eine MTA-STS-Policy im Modus enforce, gilt secure gegen die Hostnamen aus der Policy. Liefert keine der beiden Maps etwas, greift die globale Stufe verify: Das Zertifikat muss von einer vertrauenswürdigen CA stammen und zum MX-Hostnamen passen. DANE vor MTA-STS ist dabei keine Geschmacksfrage, so will es auch RFC 8461.

Das Ergebnis ist die Kette, die ich haben wollte: DANE, wenn veröffentlicht, sonst MTA-STS, sonst ein CA-geprüftes Zertifikat und niemals Klartext. smtp_dns_support_level = dnssec braucht Postfix, damit es selbst die AD-Bits auswertet und dane-only überhaupt umsetzen kann. Mit delay_warning_time erfährt ein Absender nach vier Stunden, dass seine Mail hängt, statt erst Tage später einen Bounce zu bekommen.

Drei Zeilen sehen unscheinbar aus, sind aber wichtig. smtp_tls_CAfile braucht verify und MTA-STS, sonst gibt es nichts, wogegen Postfix ein Zertifikat prüfen könnte. Unter FreeBSD kommt das Bundle aus dem Paket ca_root_nss. Die TLS-Mindestversion steht bewusst in smtp_tls_mandatory_protocols und nicht in smtp_tls_protocols. Letzteres gilt nur für opportunistisches TLS, bei verify, secure und dane-only zieht die mandatory-Variante, und deren Default erlaubt noch TLS 1.0. Die Manpage rät bei DANE übrigens davon ab, TLS 1.0 abzuschalten, weil ein paar DANE-Server nichts anderes können. Ich nehme das in Kauf.

Und tls_required_enable = no hat mich selbst überrascht. Seit Postfix 3.10 darf ein Absender per Header TLS-Required: no (RFC 8689) darum bitten, die TLS-Policy für seine Mail zu ignorieren. Postfix fragt dann keine Policy-Maps mehr und stellt mit may zu, also notfalls im Klartext. Das ist standardmäßig eingeschaltet. Ohne diese Zeile wäre „niemals Klartext“ also gelogen.

Vorher und nachher

Zum Prüfen eignet sich sendmail -bv sehr gut. Damit macht der Postfix-Client einen echten SMTP-Durchlauf zum Empfänger, inklusive TLS-Policy, stellt aber nichts zu. Das Ergebnis kommt als Bericht an den Absender und steht im maillog. Das sind die Werte von meinem Server vor und nach der Umstellung:

Zielvorherjetzt
posteo.dedane, Verifiedtls=dane-only, Verified
mailbox.orgdanetls=dane-only, Verified, sogar per Raw Public Key (RFC 7250)
gmail.comsecure (MTA-STS)tls=secure, Verified, Schlüsselaustausch X25519MLKEM768
Microsoft 365 (Kundendomain)may, nur „Trusted“tls=verify, Verified
verifier.port25.commay, „Untrusted“, trotzdem zugestellttls=!verify, 4.7.5, Zertifikat abgelaufen
comcrypto Downgrade-TestKlartext zugestellt (tls=may:none)Handshake-Fehler, 4.7.5, kein Klartext-Fallback

Die mailbox.org-Zeile finde ich schön, weil sie zeigt, was DANE kann und eine CA-Prüfung nicht. mailbox.org legt dort gar kein klassisches Zertifikat vor, sondern nur den nackten öffentlichen Schlüssel. Geprüft wird er gegen den TLSA-Record:

postfix/smtp[11293]: Verified TLS connection established to mx2.mailbox.org[2001:67c:2050:104:0:2:25:1]:25: TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519MLKEM768 server-signature RSA-PSS (4096 bit raw public key) server-digest SHA512
postfix/smtp[11293]: 1A23038B9A: to=<postmaster@mailbox.org>, relay=mx2.mailbox.org[2001:67c:2050:104:0:2:25:1]:25, delay=0.6, tls=dane-only, dsn=2.1.5, status=deliverable (250 2.1.5 Ok)

Die port25-Zeile ist meine Lieblingspointe. verifier.port25.com ist ein bekannter Dienst, um SPF, DKIM und DMARC zu testen, und ich schicke da seit Jahren Testmails hin. Erst heute ist aufgefallen, dass deren Zertifikat abgelaufen ist. Vorher war das meinem Postfix schlicht egal:

postfix/smtp[11301]: 2336538BA5: to=<check-auth@verifier.port25.com>, relay=verifier.port25.com[2600:1f14:a73:9b01:d769:5be1:de28:5251]:25, delay=1.8, tls=!verify, dsn=4.7.5, status=undeliverable (Server certificate not verified)

Und dann natürlich der eigentliche Gegenbeweis. Nach der Umstellung habe ich den comcrypto-Test mit einer neuen Testmail wiederholt. Statt im Klartext zugestellt zu werden, liegt sie jetzt in der Queue:

-Queue ID-  --Size-- ----Arrival Time---- -Sender/Recipient-------
DA4C538AD7     9844 Fri Nov 13 15:04:25  xxxx@kernel-error.com
                                         (Cannot start TLS: handshake failure)
                                         xxxx@evaluation.comcrypto.de

-- 9 Kbytes in 1 Request.

Im maillog steht bei jedem neuen Versuch tls=verify? und status=deferred, auch beim Wechsel auf die IPv6-Adresse des Empfängers. Nach vier Stunden bekommt der Absender eine Verzögerungswarnung, nach maximal_queue_lifetime einen Bounce. Das ist genau das Verhalten, das ich haben will: Lieber kommt eine Mail gar nicht an als unbemerkt im Klartext.

Probes sind das eine, echte Zustellungen das andere. Kurz nach der Umstellung habe ich deshalb noch Testmails an ein paar öffentliche Prüfdienste geschickt. Das maillog sagt dazu:

ZielAntwort des DienstesPostfixErgebnis
mecsa.dcslab.eu (MECSA, Mail-Security-Checker der EU-Kommission)danetls=dane-onlyzugestellt, per DANE verifiziert
mail.binsec.servicesnonetls=verifyzugestellt, Verified
mailsrv1.captaindns.comnonetls=verifyzugestellt, Verified
verifier.port25.comnonetls=!verifyzurückgehalten, Zertifikat abgelaufen
ein Wegwerf-Mail-Dienstnoneerst „TLS is required, but was not offered by host“, später tls=!verifyzurückgehalten, kein bzw. kein gültiges TLS

Über den ganzen Testzeitraum stehen im Log null Klartext-Zustellungen, kein fatal oder panic, kein einziger Socketmap-Fehler auf Postfix-Seite, und der Dienst lief durchgehend mit derselben PID. Die Prüfdienste, die selbst sauberes TLS oder DANE machen, bekommen ihre Mail ohne Probleme. Zurückgehalten wird nur, was die Anforderungen wirklich nicht erfüllt.

Nachbauen auf FreeBSD

Die Referenz ist das README im Repository, aktuell ist Version 0.3.1. Der Port liegt im Repository, die Einreichung in den offiziellen Ports-Tree folgt noch. Ein pkg install gibt es also noch nicht.

Zuerst braucht es einen validierenden Resolver. Der aus dem Basissystem reicht völlig:

sysrc local_unbound_enable=YES
service local_unbound start
drill -D posteo.de MX @127.0.0.1 | grep flags   # muss "ad" zeigen

Dann den Port aus dem Repository bauen, solange er nicht im Ports-Tree ist, und den Dienst starten. Der Port legt dabei den User danepolicy an und bringt dessen Definition selbst mit, solange der User im Ports-Tree noch nicht registriert ist:

git clone https://github.com/Kernel-Error/postfix-dane-policy
cp -R postfix-dane-policy/freebsd/mail/py-postfix-dane-policy /usr/ports/mail/
cd /usr/ports/mail/py-postfix-dane-policy && make install clean

sysrc postfix_dane_policy_enable=YES
service postfix_dane_policy start

Standardmäßig lauscht der Dienst auf dem Unix-Socket /var/run/postfix_dane_policy/policy.sock und fragt den Resolver auf 127.0.0.1, das passt zu local_unbound. Läuft Unbound woanders, etwa in einer Jail, setzt du die Adresse mit --dns über postfix_dane_policy_args in der rc.conf. TCP geht auch noch, ist aber nicht empfohlen, weil sich während eines Neustarts ein anderer lokaler Prozess den Port schnappen könnte. Ein kurzer Test von Hand:

postmap -q posteo.de socketmap:unix:/var/run/postfix_dane_policy/policy.sock:dane
# dane-only
postmap -q gmail.com socketmap:unix:/var/run/postfix_dane_policy/policy.sock:dane
# (leer, also NOTFOUND)

Für MTA-STS als zweite Map gibt es den Port mail/py-postfix-mta-sts-resolver, der lauscht standardmäßig auf Port 8461. Fehlt noch das CA-Bundle, kommt es mit pkg install ca_root_nss. Danach die main.cf wie oben anpassen, postfix reload, und mit sendmail -bv postmaster@posteo.de prüfen. Im maillog sollte dann je nach Ziel tls=dane-only, tls=secure oder tls=verify auftauchen.

Was du vorher wissen musst

Global verify hat Folgen, und die sollte man vorher kennen:

  • Empfänger ohne STARTTLS oder mit ungültigem Zertifikat sind nicht mehr erreichbar. Selbst signiert ohne DANE, abgelaufen oder falscher Name: Die Mail bleibt in der Queue, der Absender bekommt eine Warnung und später einen Bounce. Einen ersten Überblick gibt dein maillog: grep postfix/smtp /var/log/maillog | grep -E "(Untrusted|Anonymous) TLS connection|tls=[a-z]*:none". Das ist aber nur eine Vorauswahl. Auch eine Verbindung, die bisher als „Trusted“ geloggt wurde, kann an der Namensprüfung von verify scheitern. Den echten Test machst du mit sendmail -bv an die Ziele, die dir wichtig sind.
  • Bei mir hat es eine Weiterleitung erwischt. Meine Spamtraps haben ihre Mails bisher an einen externen Server ohne STARTTLS weitergereicht, rund 180 Mails pro Woche im Klartext. Die Weiterleitung habe ich abgeschaltet. Die Traps nehmen weiter an, rspamd lernt daraus, und danach wird lokal per discard: verworfen.
  • Wegwerf-Adressen und kleine Testdienste merken es zuerst. Solche Dienste haben oft gar kein oder kein gültiges TLS. Die Mail bleibt dann in der Queue, mailq zeigt dir den Grund, und nach delay_warning_time kommt die Warnung an den Absender.
  • verify hat eine eingebaute Grenze. Geprüft wird gegen den MX-Hostnamen. Ist der MX-Lookup des Empfängers nicht per DNSSEC gesichert, kann ein aktiver Angreifer die MX-Antwort fälschen und die Mail auf seinen eigenen Server umleiten, der ein gültiges Zertifikat für seinen eigenen Namen vorlegt. Diese Lücke schließen DANE und MTA-STS, oder fest eingetragene Identitäten pro Ziel. verify ist trotzdem deutlich besser als Klartext: STARTTLS zu stören reicht nicht mehr, der Angreifer muss DNS fälschen und einen eigenen Server mit passendem Zertifikat betreiben.
  • Der Dienst glaubt dem AD-Bit. Betreib ihn deshalb nur mit einem lokalen, validierenden Resolver und nicht mit einem entfernten über ein Netz, dem du nicht traust. Der Selbsttest fängt einen Resolver ab, der gar nicht validiert, aber keinen, der lügt.

Mein Fazit

Vor über zehn Jahren habe ich hier im Blog noch beschrieben, wie man Postfix ausgehend mit smtp_tls_security_level=may zum Verschlüsseln bringt. Damals war das ein Fortschritt. Heute ist es die letzte Downgrade-Lücke für ausgehende Mail, und die ist jetzt bei mir zu. DANE-Empfänger mit selbst signierten Zertifikaten bleiben erreichbar, DNS-Probleme oder ein ausgefallener Dienst führen zu „später nochmal“ und nie zu Klartext. Der Dienst ist klein, die Logik steckt in einer Datei, er ist MIT-lizenziert und lässt sich mit dem vorhandenen MTA-STS-Resolver kombinieren.

Wenn du Postfix unter FreeBSD betreibst, probier es gern aus. Issues und Pull-Requests auf GitHub sind willkommen, gerade Rückmeldungen von anderen Setups helfen mir, bevor der Port in den offiziellen Tree geht. Und falls ich irgendwo falsch liege, korrigiert mich bitte.

Quellen: RFC 7672 (SMTP Security via Opportunistic DANE TLS), RFC 8461 (MTA-STS), RFC 7435 (Opportunistic Security), Postfix TLS_README und postconf(5) zu smtp_tls_security_level.

Siehe auch: Postfix mit DANE und DNSSEC absichern, MTA-STS einrichten, Postfix: Eingehende E-Mails ohne TLS ablehnen, Post-Quantum TLS für E-Mail und Postfix 3.11.1 mit OpenSSL 3.5.

Fragen? Dann einfach fragen.