RFC 7672 mit Mailcow / Postfix

Was können Sie tun, um bei der Sicherheit von Email besser zu werden? Implementieren Sie den RFC 7672. RFC 7672 oder mit vollem Titel RFC 7672 SMTP Security via Opportunistic DNS-Based Authentication of Named Entities (DANE) Transport Layer Security (TLS) ist ein Internetstandard, der die sichere Übertragung zwischen Emailservern ermöglicht. Sicher im Sinne des Datenschutzes und des Fernmeldegeheimnisses. Zumindest was ich denke, die Bundesnetzagentur hat ja noch keine Erwartung veröffentlicht. Auch im Sinne von, Sie erfüllen damit ggfs. die qualifizierte Transportverschlüsselung der Orientierungshilfe zur Emailverschlüsselung der Datenschutzkonferenz.

Was brauchen Sie für RFC 7672? Meist drei Dinge:

  • aktivieren Sie DNSSEC auf Ihrer Domäne wenn das noch nicht aktiv ist
  • konfigurieren Sie Ihre Emailserver richtig
  • veröffentlichen Sie TLSA-Records passend zu Ihren Zertifikaten für Ihre Emailserver

DNSSEC, TLSA-Records und auch geeignete Zertifikate sind vor allem für eingehende Emails wichtig. Wirklich konfigurieren müssen Sie dann aber das Versenden, denn da muss Ihr Server DNSSEC und TLSA-Records anderer Domänen auswerten. Ihr Emailserver muss nach am Die Konfiguration eines Postfix erkläre ich unten, und auch wie man mit etwas Tool-Unterstützung die qualifizierte Transportverschlüsselung für einzelne Domänen sicherstellen kann. Und natürlich testen Sie es dann, z.B. mit meinem Emailsicherheit – der Test, aber natürlich gibt es auch andere.

Konfiguration von Postfix

Ich betreibe seit rund vier Jahren einen eigenen Emailserver, basierend auf mailcow-dockerized in dem wiederum neben vielen anderen Komponenten ein Postfix läuft. Das einzig wirklich besondere ist, dass Mailcow bei mir nicht auf einem Server im Internet läuft, sondern zuhause hinter einem VPN-Tunnel, und dass der Linux-Server selbst eine virtuelle Maschine auf Hyper-V 2019 ist.

Als ich den ersten Artikel über Email-Verschlüsselung geschrieben habe, wusste ich, dass Mailcow die Anforderungen für qualifizierte Transportverschlüsselung für eingehende Emails erfüllt (DNSSEC hatte ich im Frühjahr 2021 aktiviert), nicht jedoch für abgehende. Es gibt in der Mailcow-Benutzeroberfläche zwar bei jeder Mailbox die Einstellungen "Encryption Policy": "Enforce TLS incoming" und "Enforce TLS outgoing", aber es ist in der Dokumentation unklar was die bewirken, und ich vermute inzwischen: nichts! Oder zumindest zu wenig für meine Verwendung, ein Test hat das dann ans Licht gebracht.

Also weiter recherchiert. Postfix hat verschiedene Optionen für die Einstellung smtp_tls_security_level, wie beim Senden verschlüsselt wird. Leider ist die Dokumentation wirklich schwere Kost, daher vereinfacht und auf deutsch:

EinstellungErklärung
noneVerschlüsselung wird nie verwendet
mayVerschlüsselung wird verwendet, wenn beim Verbindungsaufbau vom empfangenden Server STARTTLS angeboten wird
encryptWenn beim Verbindungsaufbau kein STARTTLS angeboten wird, wird der Versuch abgebrochen und später wiederholt. Wichtig: encrypt validiert keine Zertifikate, die dürfen völlig beliebig sein, einschließlich abgelaufen. Es können auch Verschlüsselungsverfahren ganz ohne Zertifikate verwendet werden
daneWenn für die empfangenden Server TLSA Records veröffentlich sind, dann wird RFC 7672 gefolgt, ansonsten wie may
dane-onlyWenn die Gegenstelle keine TLSA Records hat, wird der Versuch abgebrochen, ansonsten RFC 7672 gefolgt
verifyWenn beim Verbindungsaufbau kein STARTTLS angeboten wird oder das präsentierte Zertifikat nicht vertrauenswürdig ist, wird der Versuch abgebrochen
fingerprint, secureDiese beiden habe ich immer noch nicht vollständig verstanden. Da kann man anscheinend TLSA Records ersetzen. Erscheint mir nicht wirklich praktikabel und nur sinnvoll wenn man mit bestimmten Empfängern etwas vereinbart und dann gezielt etwas konfiguriert

Diese Möglichkeiten gibt es sowohl als Voreinstellung in Postfix's main.cf/extra.cf als auch für jede Domäne extra (bei Mailcow in einer SQL-Tabelle mit der Postfix gesteuert wird). Voreinstellung des Mailcows ist "may", da könnte aber genauso gut auch "dane" stehen, denn DNSSEC wertet ein Mailcow sowieso aus. Wenn Sie einen anderen Postfix haben, prüfen Sie ob der DNS-Resolver DNSSEC kann und setzen Sie in main.cf/extra.cf die Option smtp_dns_support_level = dnssec. Allerdings würde "dane" bei falsch konfigurierten TLSA-Records dann einen Fehler werfen, der bei "may" nicht entdeckt wird – das unterstützt dann die Faulheit der Administratoren, nicht aber die Sicherheit. Für die Sicherheit wäre es eindeutig besser auf "dane" zu wechseln, andernfalls passiert genau dass, was im Test bei dismail.de passiert ist – Downgrade-Attacken sind sonst selbst bei RFC 7672 konformen Empfängern möglich. Und damit kein Missverständnis entsteht: für den Wechsel auf "dane" braucht es das Tool unten nicht.

Obligatorische Transportverschlüsselung beginnt demnach bei "encrypt", qualifizierte Transportverschlüsselung entspricht "dane-only" oder "verify". Alles Einstellungen die bei einer Vielzahl von Empfängern nicht funktionieren, weil sie keine Verschlüsselung anbieten (in meinen Tests rot), die Zertifikate nicht geeignet sind (gelb), oder weil die Anforderungen von RFC 7672 ("dane-only") und von MTA-STS ("verify") da halt nicht ganz zusammen passen. Und leider gibt es keine Einstellungen "dane or encrypt", oder "dane or verify". Wenn man also besser als "dane" werden will, dann muss man selbst aktiv werden.

Bei meinem Postfix ist "verify" der Standardwert. Es könnte auch "dane-only" sein – aber gültige Zertifikate sind deutlich verbreiteter als TLSA-Records, daher braucht "verify" weniger Ausnahmen. Bei meinem kleinen Server wäre das vermutlich egal. An dieser Einstellung scheitern dann erstmal ganz viele Versuche Emails zu senden, nämlich bei allen Mailservern die in meinen Tests rot oder gelb ernten, sowie grünen die Zertifikate verwenden die nur mit DANE gültig sind (z.B. bayern.de). Dann bleiben Mails in der Queue (Warteschlange) hängen, und würden da ohne Hilfe auch bleiben bis entweder der Administrator nachhilft, der empfangende Server besser wird, oder irgendwann so nach vier Tagen die Zustellung als gescheitert angesehen wird.

Dass die Mails nicht rausgehen will man natürlich nicht. Daher läuft auf der gleichen virtuellen Maschine wie Mailcow/Postfix alle fünf Minuten ein Job, der die Queue von Postfix ausliest, die Gründe ansieht und je nach Grund einen Eintrag in der oben erwähnten Tabelle anlegt. Existieren TLSA-Records, dann wird ein "dane-only" Eintrag angelegt, andernfalls bei "Server certificate not verified" oder "Server certificate not trusted" ein "encrypt" oder auch "may", wenn STARTTLS nicht angeboten wurde ("TLS is required, but was not offered'"). Noch nicht realisiert ist ein Aufräumen, der alte Einträge (vielleicht außer "dane-only" – davon will man nicht wieder weg) wieder weglöscht um bei den Empfängern auch Verbesserungen zu nutzen.

Das ist natürlich nicht perfekt, aber ich kann immerhin sagen, dass zu allen Empfängern die RFC 7672 unterstützen oder DNSSEC verwenden und vertrauenswürdige Zertifikate anbieten, eine qualifizierte Transportverschlüsselung erreicht wird. Auch wird zu allen Empfängern die STARTTLS anbieten eine obligatorische Transportverschlüsselung erreicht. Erzwingen kann ich beides so nicht, sondern dann organisatorisch. Bietet ein Empfänger kein STARTTLS an, schicke ich dem Datenschutzbeauftragten eine Email und bitte darum, den Missstand zu beenden. Würde ich das gleiche auch bei fehlender RFC-7672-Unterstützung machen, hätte ich viel Arbeit. Vielleicht schreibe ich dafür auch ein Tool, dass das automatisiert. Erstmal müssen da meine Veröffentlichungen herhalten. Will man seinen eigenen Mailserver überprüfen, dann hilft Emailsicherheit – der Test.

Das Tool

Nochmal: das Tool ist nicht erforderlich wenn man nur RFC 7672 aka "dane" verwenden will, sondern ist eine Möglichkeit besser zu werden als "dane". Das Tool besteht aus einer Mixtur von Shellskript, Javascript, und SQL. Ein bisschen Quick&Dirty halt. Das Tool liegt bei mir im Verzeichnis /opt/monque neben dem eigentlichen /opt/mailcow-dockerized, Einstiegspunkt ist mon.sh und das ist auch in /etc/crontab eingetragen und wird alle 5 Minuten ausgeführt. Die Dateien sind nicht alphabetisch wiedergeben sondern so, dass man Top-Down ein Verständnis bekommen kann. Hoffentlich sind in der folgenden Wiedergabe keine spitzen Klammern oder andere Sonderzeichen verlorengegangen. Auf dem System muss natürlich auch der MariaDB-client installiert sein. Einige Zwischenschritte und das Aufheben von Dateien könnte man auch weglassen, die sind halt beim Debuggen hilfreich gewesen.

Als willkommener Nebeneffekt des Tools habe ich übrigens immer Kenntnis davon, dass eine Nachricht in der Warteschlange hängt.

/opt/monque/mon.sh

#!/bin/bash

cd /opt/mailcow-dockerized
docker-compose exec -T postfix-mailcow postqueue -p >../monque/postqueue.out
docker-compose exec -T postfix-mailcow postqueue -j | tr "\n" ",\n" >../monque/postqueue.json

cd ../monque

cat postqueue.out | sed -nre 's/^([A-F0-9]+)\s+.*$/\1/p' | sort >postqueue.new
cmp postqueue.new postqueue.old
if [ $? -ne 0 ];
then
        cat prolog.js postqueue.json epilog.js | nodejs >../monque/newpolicy.txt
        ./stripdnssec.sh <newpolicy.txt >newpolicy.sql
        cat setpolicy.sql newpolicy.sql | ./setpolicy.sh
        cp postqueue.out postqueue.`date +"%m%d.%T"`
        cp postqueue.json postqueue.json.`date +"%m%d.%T"`
        echo  "From: ***@lindenberg.one" >postqueue.mail
        echo  "To: ***@lindenberg.one" >>postqueue.mail
        echo  "Subject: Queue Status" >>postqueue.mail
        echo -n "Date: ">>postqueue.mail
        date --rfc-email >>postqueue.mail
        echo  >>postqueue.mail
        cat postqueue.out >>postqueue.mail
  curl --url 'smtps://mail.lindenberg.one:465' --ssl-reqd \
  --mail-from 'noreply@lindenberg.one' --mail-rcpt '***@lindenberg.one' \
  --upload-file postqueue.mail --user 'noreply@lindenberg.one:***'
  mv postqueue.new postqueue.old
fi

/opt/monque/prolog.js

const console = require('console');

var a = [

/opt/monque/epilog.js

];
for (var r of a)
  if (r.queue_name == "deferred")
    for (var s of r.recipients)
    {
       var address = s.address;
       address = address.substring(address.indexOf('@')+1);
       var policy ='';
       switch(s.delay_reason.substring(0,36)) {
         case 'Server certificate not verified':
         case 'Server certificate not trusted':
               policy = 'encrypt'; break;
         case 'TLS is required, but was not offered':
               policy = 'may'; break;
        }
        if (policy != '') console.debug("%s,%s\n", address, policy);
    }

/opt/monque/stripdnssec.sh

#!/bin/bash

sed -e 's/,/\n/g'  | (while read d; do
  if [[ $d == "" ]] ; then
          continue
  fi
  read p;
  mx=$(dig +dnssec -t MX $d @1.1.1.1 | sed -nre 's/^.*\s+IN\s+MX\s+[0-9]+\s+(.*)\.$/\1/p')
  tlsa=$(dig +dnssec -t TLSA _25._tcp.$mx @1.1.1.1 | grep -E '\s+IN\s+TLSA\s+')
#  echo $mx, $tlsa >&2
  if [[ $tlsa != "" ]] ; then
         p='dane-only'; # good choice w/o DNSSEC
  else
    dig +dnssec -t MX $d @1.1.1.1 | grep -E '\s+IN\s+RRSIG\s+MX\s+' >/dev/null
    if  [[ $? == 0 ]] ; then
        continue # if we ever get here, then the cert was invalid and DANE unsupported
        p='verify'; # DNSSEC explicit
    fi;
  fi;
  echo  call setPolicy\(\'$d\',\'$p\'\);
done)

/opt/monque/setpolicy.sql

DELIMITER ;;

CREATE OR REPLACE PROCEDURE setPolicy (
    IN domain CHAR(255),
    IN pol CHAR(20)
)

BEGIN

update ignore tls_policy_override set active=1, policy=pol where dest = domain collate 'utf8mb4_unicode_ci';
if (ROW_COUNT() = 0) then
  insert into tls_policy_override SET dest = domain collate 'utf8mb4_unicode_ci', policy=pol, active=1;
end if;

select* from tls_policy_override;

END ;;

DELIMITER ;

Hier überlege ich, nur auf insert zu gehen und dann ohne die Stored Procedure auszukommen.

/opt/monque/setpolicy.sh

cd /opt/mailcow-dockerized
source mailcow.conf
mariadb -h 127.0.0.1 --port 13306 -u ${DBNAME} -p${DBPASS} mailcow
docker-compose exec -T postfix-mailcow postqueue -f
cd /opt/monque

Veröffentlicht am 28.02.2022.

© 2022 Joachim Lindenberg. Diese Seite spiegelt meine persönliche Meinung wieder. Sie stellt keine Rechtsberatung dar. Fragen Sie doch einen Anwalt der sich damit auskennt.