# 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 .