# SPF Milter *SPF Milter* ist eine Milter-Anwendung zur Verifizierung von E-Mail-Absendern mittels des *Sender-Policy-Framework*-Protokolls. In einen milterfähigen Mailserver integriert setzt sie die im DNS veröffentlichte SPF-Policy gegenüber Clients durch. SPF Milter setzt die Vorgaben der SPF-Spezifikation [RFC 7208] wortgetreu um. Die Verifizierung wird stets in der von der Spezifikation empfohlenen Folge durchgeführt: Erst wird optional die *HELO identity* (der mittels SMTP-Befehl `HELO` angegebene Domainname) des Clients überprüft. Führt dieser Schritt nicht zu einem Ergebnis, dann wird seine *MAIL FROM identity* (der mittels SMTP-Befehl `MAIL FROM` angegebene Absender) überprüft. Das so ermittelte endgültige Resultat wird dann vom Milter in geeigneter Weise behandelt, entweder indem mit einem SMTP-Fehler geantwortet wird, oder indem das Resultat in den Kopfteil der Nachricht geschrieben wird. Innerhalb des von der Spezifikation gesetzten Rahmens bietet SPF Milter flexible Konfigurationsmöglichkeiten. Konfigurationsparameter für den Verifikationsablauf, die Behandlung des Resultats, SMTP-Antwort, Header und so weiter decken eine Bandbreite von Anwendungsszenarien ab. Konfiguration kann im laufenden Betrieb angepasst werden, ohne dass ein Neustart nötig ist. Was die Implementierung angeht, ist SPF Milter im Wesentlichen nicht mehr als eine Konfigurationsmaske, die über eine in das Milter-Protokoll integrierte SPF-Bibliothek gelegt ist. Die SPF-Implementierung besorgt [viaspf], die dazugehörige DNS-Implementierung besorgt [domain]. [RFC 7208]: https://www.rfc-editor.org/rfc/rfc7208 [viaspf]: https://crates.io/crates/viaspf [domain]: https://crates.io/crates/domain ## Installation SPF Milter ist ein [Rust]-Projekt. Es kann wie üblich mit Cargo kompiliert und/oder installiert werden. Das folgende Kommando etwa lädt und installiert die neuste Version von [crates.io]: ``` cargo install --locked spf-milter ``` Wie in den nachfolgenden Abschnitten erläutert, ist der ins Programm einkompilierte Standardpfad für die Konfigurationsdatei `/etc/spf-milter.conf`. Dieser Pfad kann beim Kompilieren überschrieben werden, indem die Umgebungsvariable `SPF_MILTER_CONFIG_FILE` mit dem gewünschten Pfad definiert wird. Nach der Kompilierung erfolgt die Installation durch Kopieren von `spf-milter` nach `/usr/sbin`; `spf-milter.service` nach `/etc/systemd/system`; `spf-milter.conf` nach `/etc`. Der Service lässt sich dann mit dem Kommando `systemctl enable --now spf-milter` starten. Die *manual pages* können ebenso durch Kopieren installiert werden: ``` cp spf-milter.8 /usr/local/share/man/man8/ cp spf-milter.conf.5 /usr/local/share/man/man5/ mandb ``` Als Mindestanforderung für Rust gilt Version 1.74.0. [Rust]: https://www.rust-lang.org [crates.io]: https://crates.io/crates/spf-milter ## Anwendung Nach der Installation kann SPF Milter auf der Kommandozeile als `spf-milter` aufgerufen werden. SPF Milter liest Konfigurationseinstellungen aus dem File `/etc/spf-milter.conf`. Darin muss mindestens der Parameter `socket` eingestellt werden. Die Beispielkonfiguration [`spf-milter.conf`] zeigt wie das geht. Beim Aufruf von `spf-milter` startet der Milter im Vordergrund. Er kann mit einem Signal oder mittels Tastenkombination Steuerung-C heruntergefahren werden. Soll SPF Milter als Dienst betrieben werden, empfiehlt sich als Vorlage der im Projekt vorhandene systemd-Service [`spf-milter.service`]. Diese Datei nach Bedarf anpassen (zum Beispiel `User`, `Group` und `UMask` beifügen), in `/etc/systemd/system` installieren und dann den Service aktivieren und starten. Statusmeldungen schreibt SPF Milter standardmässig ins Syslog. Neben Fehlermeldungen und Warnungen wird für jede überprüfte Identität das Verifizierungsergebnis in das Log geschrieben. [`spf-milter.conf`]: https://codeberg.org/glts/spf-milter/src/tag/0.7.0-alpha.1/spf-milter.conf [`spf-milter.service`]: https://codeberg.org/glts/spf-milter/src/tag/0.7.0-alpha.1/spf-milter.service ## Konfiguration Die Konfiguration von SPF Milter erfolgt in erster Linie über Konfigurationsparameter, die in der Datei `/etc/spf-milter.conf` hinterlegt werden. Alle Parameter haben sinnvolle Standardwerte, mit Ausnahme des Parameters `socket`, der zwingend angegeben werden muss. Als Referenz ist die *manual page* [*spf-milter.conf*(5)] massgebend. (Die *manual page* lässt sich auch ohne Installation mit Hilfe von `man` ansehen: `man ./spf-milter.conf.5`) Konfiguration kann im laufenden Betrieb vom File neu geladen werden, indem das Signal `SIGHUP` an den Milter-Prozess gesandt wird. Einzelheiten dazu stehen in der *manual page*. Für jene, die SPF Milter zum ersten Mal verwenden, bietet der folgende Abschnitt eine kurze praktische Einführung und Übersicht über die wichtigsten Konfigurationsparameter. [*spf-milter.conf*(5)]: https://codeberg.org/glts/spf-milter/src/tag/0.7.0-alpha.1/spf-milter.conf.5 ### Los gehts! Nehmen wir uns zwei Minuten Zeit um zum ersten Mal SPF Milter aufzusetzen. Der erste Schritt ist, das Socket des Milters, wohin sich der MTA verbinden wird, auszuwählen und einzurichten. Dazu dient der Parameter **`socket`**. Als Wert muss eine Socket-Spezifikation in einem von zwei Formaten stehen: * inet:host:port für ein TCP-Socket * unix:path für ein UNIX-Domain-Socket So muss das im Konfigurationsfile ausschauen: ``` # Ein TCP-Socket lauscht auf Port 3000: socket = inet:localhost:3000 ``` Zur Erinnerung: Die Konfigurationseinstellungen gehören ins File `/etc/spf-milter.conf`. Die Syntax ist wie oben gezeigt und in der *manual page* dokumentiert. Dabei nicht vergessen, den Milter mit dem MTA zu integrieren. Mit [Postfix] etwa heisst das, das Socket in `/etc/postfix/main.cf` anzugeben: ``` smtpd_milters = inet:localhost:3000 ``` Diese Dinge eingestellt, den Milter gestartet und die Postfix-Konfiguration neu geladen, beginnt SPF Milter sogleich eintreffende Nachrichten zu verarbeiten. Verschiedene Aspekte der Verarbeitung können angepasst werden. Die wichtigste konfigurierbare Facette des Verifikationsablaufs ist, ob die *HELO identity* überprüft wird oder nicht. Dazu dient der Boole’sche Parameter **`verify_helo`**: ``` # Auch HELO vor MAIL FROM verifizieren: verify_helo = yes ``` Verifikation der *HELO identity* einschalten bedeutet nicht Verifikation der *MAIL FROM identity* ausschalten. Vielmehr wird so die *HELO identity* *zusätzlich* vor der *MAIL FROM identity* überprüft. Sofern die *HELO identity* überhaupt berücksichtigt werden soll, kann es von Vorteil sein, diese Option eingeschaltet zu lassen. Denn, wie in Abschnitt 2.3 von RFC 7208 ausgeführt, ist die *HELO identity* gewöhnlich simpler als die komplexere *MAIL FROM identity*, und ist oft mit einer weniger aufwändigen SPF-Policy verknüpft, die mit weniger DNS-Abfragen auskommt. Absender mit einem negativen Autorisierungsresultat oder einem Fehlerresultat können vom Milter auf SMTP-Ebene mit einer temporären oder permanenten Fehlerantwort abgewiesen werden. Die abzuweisenden SPF-Resultate werden mit dem Parameter **`reject_results`** deklariert: ``` # Absender mit einem der folgenden Resultate abweisen: reject_results = fail, temperror, permerror ``` Die SPF-Resultate werden durch Kommas getrennt aufgelistet. Im obigen Beispiel würden Absender mit Resultat *fail* abgewiesen, Absender mit Resultat *softfail* jedoch angenommen. Bei angenommenen, also nicht abgewiesenen Absendern wird das Resultat in eine neue Headerzeile ihrer Nachricht geschrieben. Die Art der Headerzeile kann ebenfalls konfiguriert werden, und zwar mittels Parameter **`header`**. Zur Auswahl stehen die Header-Typen `Received-SPF` und `Authentication-Results`. Beispiel: ``` # Angenommene Nachrichten mit „Received-SPF“-Header versehen: header = Received-SPF ``` Und damit seid ihr gerüstet fürs selbständige Experimentieren. Beim Experimentieren mit der Konfiguration ist jeweils kein Neustart nötig, es genügt, dem Milter das Neuladen-Signal zu schicken. Dieses Feature sowie viele weitere Einstellungen sind in der *manual page* *spf-milter.conf*(5) erklärt. [Postfix]: https://www.postfix.org ## Anwendungsfälle Zur Vertiefung wollen wir uns in diesem Abschnitt zwei geläufige Anwendungsfälle genauer ansehen: „Standard“-SPF und SPF als Teil von DMARC. ### Standard-SPF Der RFC-konforme Standardanwendungsfall für SPF-Verifikation wird von den Standardeinstellungen von SPF Milter gut abgedeckt. Es genügt also, den Pflichtparameter `socket` einzustellen und alle anderen Parameter auf der Defaulteinstellung zu belassen, um diesen Anwendungsfall einzurichten. `/etc/spf-milter.conf`: ``` socket = inet:localhost:3000 ``` Gehen wir das Standardverhalten der Reihe nach durch. Als erstes verifiziert SPF Milter die *HELO identity* (`verify_helo = yes`). Wenn das Ergebnis dieses Tests weder in die Menge der abzuweisenden noch der „definitiven“ HELO-Resultate fällt, wird als nächstes die *MAIL FROM identity* verifiziert. Die genannten Mengeneinstellungen, `reject_helo_results` und `definitive_helo_results`, erlauben es, genau festzulegen, aufgrund welcher HELO-Resultate die Verifizierung der *MAIL FROM identity* ausgelassen oder übersprungen werden kann. Standardmässig wird sowohl für die *HELO* wie für die *MAIL FROM identity* derselbe Satz Resultate abgewiesen, und kein HELO-Resultat zählt als „definitiv“. Aus dem HELO- oder MAIL-FROM-Test resultiert ein finales Ergebnis. Dieses wird dann vom Milter gemäss den Empfehlungen von RFC 7208 in eine Antwort umgesetzt: Die Resultate *fail*, *temperror* und *permerror* werden mit einer permanenten beziehungsweise temporären SMTP-Fehlerantwort abgewiesen, alle anderen Resultate werden in einer Headerzeile der Nachricht hinzugefügt. Die Menge der abzuweisenden Resultate ist mittels der Parameter `reject_results` und `reject_helo_results` konfigurierbar. Ebenso können Statuscode und Text der SMTP-Antwort für jedes SPF-Resultat ajustiert werden. Für den Header wird standardmässig eine Headerzeile vom Typ *Received-SPF* generiert. Der Header-Typ lässt sich über den Parameter `header` anpassen (`header = Received-SPF`). Beide in RFC 7208 definierten Header-Typen sind „Standard“, dienen aber verschiedenen Zwecken: *Received-SPF* codiert vollständig die Eingabeparameter und Informationen zum SPF-Resultat, *Authentication-Results* hingegen dient nur der Übermittlung des Resultats. Hier und generell ist es das Bestreben von SPF Milter, die Spezifikationen genau zu befolgen, insbesondere RFC 7208 und 8601, sowie die dort referenzierten. Im Zweifelsfall also die RFCs lesen! #### Varianten **`softfail` wie ein negatives Autorisierungsresultat behandeln.** Unter besonders restriktiven Bedingungen kann das Resultat `softfail` wie `fail` behandelt, also abgewiesen werden. Um dies zu implementieren, genügt es, `softfail` zu den abzuweisenden Resultaten hinzuzufügen. `/etc/spf-milter.conf`: ``` reject_results = fail, softfail, temperror, permerror ``` **Mehr Informationen im Header.** SPF Milter unterstützt beide spezifizierten Header-Typen. Es können nicht nur der eine oder der andere, sondern auch beide gleichzeitig verwendet werden. Die Konfiguration erfolgt über den Parameter `header`. Zusätzlich sorgt `include_all_results` dafür, dass die Resultate aller verifizierten Identitäten aufgezeichnet werden (HELO *und* MAIL FROM). `/etc/spf-milter.conf`: ``` header = Received-SPF, Authentication-Results include_all_results = yes ``` ### SPF als Teil von DMARC SPF kann auch als Teil einer DMARC-Verifikation genutzt werden. *Domain-based Message Authentication, Reporting, and Conformance* (DMARC) ist in [RFC 7489] spezifiziert. Da DMARC das Resultat, das die SPF-Komponente produziert, als Eingabe für seinen eigenen Validierungsprozess verwendet, sind einige Anpassungen an der Standardkonfiguration vorzunehmen. `/etc/spf-milter.conf`: ``` socket = inet:localhost:3000 verify_helo = no reject_results = header = Authentication-Results ``` Erstens ist bei DMARC nur die *MAIL FROM identity* von Belang, die *HELO identity* spielt keine Rolle. Daher sollte dieser Schritt übersprungen werden, indem `verify_helo` deaktiviert wird. Zweitens liegt es nahe, Absender nicht gleich nach der SPF-Verifikation abzuweisen, da eine solche Entscheidung ja an die nachfolgende DMARC-Komponente delegiert werden soll (je nach dem kann diese Anforderung aber auch anders gelöst werden). In der obigen Konfiguration werden dank der Einstellung `reject_results =` (die leere Menge) keine Abweisungen mit SMTP-Fehlerantwort getätigt. Der Milter schreibt alle Resultate nur in den Header. Drittens wird mit dem Parameter `header` der Typ der Headerzeile von `Received-SPF` zu `Authentication-Results` abgeändert, da dies der Header ist, mit dem DMARC arbeitet. Der *Authentication-Results*-Header wurde allgemein zur Transportierung des Authentisierungsstatus für spätere maschinelle Verarbeitung entworfen. Spezifiziert wurde er jüngst in [RFC 8601]. [RFC 7489]: https://www.rfc-editor.org/rfc/rfc7489 [RFC 8601]: https://www.rfc-editor.org/rfc/rfc8601 #### Varianten **Fehlende Autorisierung der *HELO identity* frühzeitig abweisen.** Mit ein wenig Fantasie lässt sich von der *HELO identity* auch in einem DMARC-Szenario Gebrauch machen: Ein Absender mit nicht autorisierter *HELO identity* führt bestimmt nichts Gutes im Schilde und darf abgewiesen werden. Zur Implementierung dieser Anforderung fügen wir nur `fail` den für die *HELO identity* abzuweisenden Resultaten hinzu. In allem Übrigen verhält sich diese Konfiguration wie die oben. `/etc/spf-milter.conf`: ``` verify_helo = yes reject_helo_results = fail reject_results = ``` ## Mitmachen Jeder Beitrag ist willkommen. Bitte ein Ticket auf dem Issue-Tracker aufmachen, sei es für Fragen, Anregungen, Bugs, Features, Dokumentation, Übersetzungen usw. Um das Projekt langfristig am Leben zu erhalten, kann ich Interessierten auch Commit-Rechte geben. Bitte neue Features zuerst diskutieren, bevor ihr sie implementiert. Wir glauben, dass die Implementierung verhältnismässig oft das einfachere ist, die meiste Zeit braucht das Design und die Motivation eines Features. Genaue und fehlerresistente Implementierung der RFCs ist im Vergleich mit ähnlicher Software eine der herausragenden Leistungen von SPF Milter – immer die RFCs zu Rate ziehen! Dieses Projekt wird als freie Software unter einer GPL-Lizenz entwickelt. Wir bitten, diese Entscheidung zu respektieren. ## Lizenz Copyright © 2020–2024 David Bürgin This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see .