spf-milter 0.0.4

Milter for SPF verification
Documentation
# SPF Milter

*SPF Milter* ist eine Milter-Anwendung zur Verifizierung von E-Mail-Absendern
mittels des SPF-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 immer 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` 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]. Wir erachten diese
Komponenten als eine solide Basis für SPF-Milter-Software.

[RFC 7208]: https://www.rfc-editor.org/rfc/rfc7208
[viaspf]: https://crates.io/crates/viaspf

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

Als Milter-Anwendung benötigt SPF Milter die C-Bibliothek libmilter. Unter
Debian und Ubuntu beispielsweise muss das Paket `libmilter-dev` installiert
werden.

Bei der Kompilierung wird das Programm `pkg-config` verwendet, um die
Milter-Bibliothek zu lokalisieren. Fehlen auf einer Distro die
pkg-config-Metadaten für libmilter, kann das in diesem Projekt vorhandene
Metadaten-File `milter.pc` Hilfe bieten: Einfach die Datei `milter.pc` in einem
Verzeichnis ablegen und dann beliebige Cargo-Befehle mit diesem Verzeichnis auf
dem pkg-config-Pfad ausführen. Das Kompilieren des `spf-milter`-Programms geht
dann so:

```
PKG_CONFIG_PATH=/path/to/dir cargo build
```

Die Bibliothek [domain] benötigt die OpenSSL-Bibliothek. Auf Debian und Ubuntu
muss das Paket `libssl-dev` installiert werden.

Als Mindestanforderung für Rust gilt Version 1.46.0.

[Rust]: https://www.rust-lang.org
[crates.io]: https://crates.io
[domain]: https://crates.io/crates/domain

### Kompilieren

Fürs Kompilieren aus dem Quellcode gelten die obengenannten Anforderungen.
Ausserdem ist für das Ausführen der Integrationstests das Programm `miltertest`
notwendig. (`miltertest` ist Teil der Software *OpenDKIM*.)

Der Standardpfad für die Konfigurationsdatei kann zur Kompilierzeit
überschrieben werden, indem die Umgebungsvariable `SPF_MILTER_CONFIG_FILE`
gesetzt wird.

## 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 Inspiration der
im Projekt vorhandene systemd-Service `spf-milter.service`. Diese Vorlage nach
Bedarf anpassen (zum Beispiel noch `User` 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
Verifizierungsresultat in das Log geschrieben.

## Konfiguration

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.

Die *man page* *spf-milter.conf*(5) ist momentan als Referenz massgebend. (Die
man page lässt sich auch ohne Installation ansehen mit: `man
./spf-milter.conf.5`)

Konfiguration kann im laufenden Betrieb vom File neu geladen werden, indem das
Signal `SIGUSR1` an den Milterprozess gesandt wird. Einzelheiten dazu stehen in
der *man page*.

Für jene, die SPF Milter zum ersten Mal verwenden, bietet der folgende Abschnitt
eine kurze praktische Einführung und eine Übersicht über die wichtigsten
Konfigurationsparameter.

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

*   <code>inet:<em>port</em>@<em>host</em></code> oder
    <code>inet6:<em>port</em>@<em>host</em></code> für ein TCP-Socket
*   <code>unix:<em>path</em></code> für ein UNIX-Domain-Socket

So muss das im Konfigurationsfile ausschauen:

```
# Ein TCP-Socket lauscht auf Port 3000:
socket = inet:3000@localhost
```

Zur Erinnerung: Die Konfigurationseinstellungen gehören ins File
`/etc/spf-milter.conf`. Die Syntax ist wie oben gezeigt und in der *man 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
non_smtpd_milters = $smtpd_milters
```

Diese Dinge eingestellt, den Milter aufgestartet und die Postfix-Konfiguration
neu geladen, beginnt SPF Milter alsbald eintreffende Nachrichten zu verarbeiten.

Verschiedene Aspekte der Verarbeitung können angepasst werden. Die wichtigste
konfigurierbare Facette des Verifizierungsablaufs 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
```

Verifizierung der *HELO identity* einschalten bedeutet nicht Verifizierung der
*MAIL FROM identity* ausschalten. Vielmehr wird so die *HELO identity*
*zusätzlich* vor der *MAIL FROM identity* verifiziert. Nur wenn diese ein
definitives Ergebnis liefert, kann *MAIL FROM* übersprungen werden. Es ist im
Allgemeinen von Vorteil, 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 kommt oft mit einer weniger
komplexen SPF-Policy daher, die mit weniger aufwendigen DNS-Abfragen auskommt.
Insgesamt kann diese Einstellung daher die Auswertung beschleunigen.

Absender mit einem negativen Autorisierungsresultat oder einem Fehlerresultat
können vom Milter auf SMTP-Ebene mit einer vorübergehenden 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 mit Kommas getrennt aufgelistet. Im obigen Beispiel
werden etwa 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 Wahl
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 *man page* *spf-milter.conf*(5) erklärt.

[Postfix]: http://www.postfix.org

## Anwendungsfälle

Zu 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-Verifizierung wird von den
Standardeinstellungen von SPF Milter gut abgedeckt. Es genügt also, den
Parameter `socket` einzustellen und alle anderen Parameter auf der
Defaulteinstellung zu belassen, um diesen Anwendungsfall einzurichten.

`/etc/spf-milter.conf`:

```
socket = inet:3000@localhost
```

Gehen wir nun das Standardverhalten der Reihe nach durch.

Als erstes verifiziert SPF Milter die *HELO identity* (`verify_helo=yes`). Wenn
sich aus diesem Test keines der definitiven Autorisierungsresultate ergibt –
*pass* oder *fail* –, wird als nächstes die *MAIL FROM identity* verifiziert.
Die Menge der definitiven HELO-Resultate, infolge derer MAIL FROM übersprungen
wird, kann mit dem Parameter `definitive_helo_results` eingestellt werden. Wie
erwähnt ist der Initialwert `pass, fail`, der aber auf beliebige „definitive“
Resultate angepasst werden kann.

Aus dem HELO- oder MAIL-FROM-Test resultiert ein finales Ergebnis. Dieses wird
dann vom Milter gemäss Empfehlungen von RFC 7208 in eine Antwort umgesetzt: Die
SPF-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 wiederum mit einem Parameter, `reject_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 mit dem 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. Besonders 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, muss `softfail` zu
den definitiven HELO-Verifizierungsresultaten sowie zu den abzuweisenden
Resultaten hinzugefügt werden.

`/etc/spf-milter.conf`:

```
definitive_helo_results = pass, fail, softfail
reject_results = fail, softfail, temperror, permerror
```

**Beide Header-Typen verwenden.** SPF Milter unterstützt beide spezifizierten
Header-Typen. Es können nicht nur der eine oder der andere, sondern auch beide
gleichzeitig verwendet werden. Der RFC mahnt zwar, dass in dem Fall beide
Header-Typen nicht je ein abweichendes Resultat beinhalten sollten, aber
natürlich wird dies von SPF Milter gewährleistet.

`/etc/spf-milter.conf`:

```
header = Received-SPF, Authentication-Results
```

### SPF als Teil von DMARC

SPF kann auch als Teil einer DMARC-Verifizierung 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:3000@localhost
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-Verifizierung
abzuweisen, da eine solche Entscheidung ja an die nachfolgenden 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 und letztens wird oben der Typ der Headerzeile von `Received-SPF` zu
`Authentication-Results` abgeändert, da dies der Header ist, mit dem DMARC
arbeitet. Dieser Header wurde allgemein zur Transportierung von
Authentisierungsresultaten für spätere maschinelle Verarbeitung entworfen.
Spezifiziert wurde er in jüngerer Zeit 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 definitiven und den – nur 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
definitive_helo_results = fail
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 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.