amavis MILTER HOWTO

Postfix beherrscht seit langem schon das MILTER-Protokoll. Das Protokoll ist mächtiger und feiner zu steuern als der meist angepriesene Ansatz über den smtpd_proxy_filter bei dem in der Regel nahezu gebetsmühlenartig betont wird man müsse ihn verwenden, weil man nur dann E-Mail nach Deutschem Recht gesetzskonform verarbeiten könne.

Das kann ein MILTER auch! MILTER verarbeiten - per Spezifikation der Schnittstelle - Nachrichten pre-Queue (vor der Annahme) und in Memory. Damit sind sie nicht nur gesetzeskonform, sondern ebenso schnell. Nur eben mächtiger…

In diesem kleinen HOWTO möchte ich zeigen, wie das Content Filter Framework amavisd-new als MILTER in die Mailverarbeitung eingebunden werden kann. Beginnen wir mit Postfix. Es soll Nachrichten an einen MILTER (amavis) abgeben.

Wichtig

Eine wichtige Information vorneg: Wer mit MILTER Content filtern will, muss auf den Einsatz von smtpd_proxy_filter verzichten. MILTER und smtpd_proxy_filter vertragen sich nicht gut miteinander. Wenn beide in derselben Postfix-Instanz eingesetzt werden, "sieht" der MILTER nur die SMTP-Kommandos, nicht aber die Header der Nachricht oder ihren Body. Das ist prinzipbedingt und wird sich nicht ändern.

MILTER in Postfix einbinden

Das MILTER-Protokoll stammt von Sendmail ab. Sendmail ist ein monolitisches Binary. Es sieht alles, was "in sich" passiert. Postfix ist anders aufgebaut. Es setzt auf eine "compartmentalized architecture". Die einzelnen Teile wissen absichtlich (Sicherheit) nicht voneinander.

Diese Trennung ist Ursache dafür, dass Postfix über zwei Parameter verfügt über die MILTER eingebunden werden können. Ein Parameter listet MILTER auf, die für E-Mail zuständig sind, welche über das (offline) sendmail-Kommando in das System gelangt sind. Der andere Parameter ist für alle E-Mails zuständig, die (von extern) über den smtpd-Server eingehen.

In diesem Beispiel konzentriere ich mich auf extern eingehende Nachrichten. Ich gehe davon aus, dass wir einen SMTP-Server bauen, der im MTA zu MTA Verkehr steht und eingehende Nachrichten auf Port 25 mit amavis filtern soll.

Bemerkung

Falls der Server auch noch Submission machen soll, kannst Du in der master.cf beim submission-Service von den folgenden globalen Settings abweichen.

Die Konfigurationsweisung, E-Mails an den Content Filter amavis zu senden, sind in der main.cf schnell gemacht:

smtpd_milters = inet:localhost:10024

Bemerkung

Ich könnte statt einer TCP-Verbindung auch einen UNIX-Domain Socket angeben. Der ist aber chroot-Beschränkungen und user/group permissions unterworfen und das funktioniert nicht auf allen Distributionen ohne grössere Anstrengungen oder gar nicht. Also TCP-Verbindung, denn die ist brauchbar schmerzfrei.

Damit weiß Postfix wo es amavis kontaktieren soll. Wir müssen amavis in die Verarbeitung einbinden. Dazu brauchen wir amavisd-milter.

amavisd-milter

amavisd-new beherrscht die Protokolle (E)SMTP, LMTP und AM.PDP (amavis policy delegation protocol) - das MILTER-Protokoll beherrscht amavis nicht. Trotzdem ist es möglich amavis in eine MILTER-basierte Mailverarbeitung einzubinden.

Vermittler zwischen der MILTER-Welt und amavis ist das Hilfsprogramm amavisd-milter. Es übersetzt - in beiden Kommunikationsrichtungen - Kommandos und Informationen der MILTER-Schnittstelle in das amavis-eigene AM.PDP-Protokoll.

amavisd-milter kann in der Regel über das Repository der Linux-Distribution installiert werden. Wer es von Hand bauen muss oder möchte, findet die Quellen auf <http://amavisd-milter.sourceforge.net/>. Name und Speicherort der Datei, über die das Programm konfiguriert wird, variieren je nach Distribution.

Ein konfiguriertes amavisd-milter bietet dem MTA eine MILTER-Schnittstelle auf die es connecten kann und eine für die Kommunikation mit amavis. Zusätzliche Optionen legen ein Limit für Verarbeitungsanfragen fest, übergeben einen milter_macro_name über den gezielt policy_banks in amavis angesprungen werden können und (je nach Distribution und Patchlevel) user/group mit denen UNIX-Domain sockets etabliert werden sollen.

/usr/sbin/amavisd-milter -s inet:10024@127.0.0.1 \
-p /var/amavis/amavisd-milter.pid -S /var/amavis/amavisd.sock \
-w /var/amavis/tmp -m 20

Hier (siehe oben) läuft ein amavisd-milter, das auf 127.0.0.1:10024 auf eingehende Verbindungen von Postfix wartet und diese über den Socket /var/amavis/amavisd.sock an amavis weitergibt. Die Anzahl maximaler Verbindungen wurde auf 20 beschränkt.

Damit steht die Brücke zwischen Postfix und amavis und wir können amavis konfigurieren.

amavisd-new

Im folgenden Code-Beispiel definieren wir mit @listen_sockets drei Listener in amavisd-new. Sie stehen für eingehende Verbindungen zur Verfügung und dienen z.B. dem Filtern von E-Mail oder um Nachrichten aus der Quarantäne zu freizugeben.

#############################################################################
## SERVER
#

# Wieviel Instanzen soll amavis starten?
$max_servers = 16;

# Auf welchen Sockets sollen die Instanzen auf eingehende Verbindungen
# lauschen?
@listen_sockets = (
    # Quarantine Release
    '[::1]:9998',
    # Post-Queue, Submission
    '[::1]:10024',
    # Pre-Queue, MTA zu MTA
    "$MYHOME/amavisd.sock"
    );

Eingehende Verbindungen müssen wir nun bestimmten Policies ($policy_banks) zuführen. Wir mappen sie über die folgenden Mechanismen den $policy_banks zu:

#############################################################################
## POLICY MAPPING
#

# Hier mappen wir eingehende Verbindungen Policy Banks zu. Eine eingehende
# Verbindung können wir anhand verschiedener Eigenschaften erkennen:
#
# - TCP/UNIX-Socket einer eingehenden Verbindung
# - IP-Adresse/IP-Range einer eingehenden Verbindung
# - DKIM-authentifizierter Sender/Senderdomain

# In welche Policy routen wir die @listen_sockets?
$interface_policy{'SOCK'}   = 'AM.PDP-SOCK';
$interface_policy{'9998'}   = 'AM.PDP-INET';
$interface_policy{'10024'}  = 'SUBMISSION';

# In welche Policy routen wir bestimmte IPs/Netzwerke?
@client_ipaddr_policy = (
    [qw( 0.0.0.0/8 127.0.0.1/32 [::] [::1] )] => 'LOCALHOST',
    [qw( !172.16.1.0/24 172.16.0.0/12 192.168.0.0/16 )] => 'PRIVATENETS',
    [qw( 192.0.2.0/25 192.0.2.129 192.0.2.130 )] => 'PARTNER',
    \@mynetworks => 'MYNETS'
);

# In welche Policy routen wir DKIM-verifizierte Sender/Senderdomains?
@author_to_policy_bank_maps = ( {
    '.paypal.de'                => 'WHITELIST',
    'amazon.de'                 => 'WHITELIST',
} );

Jetzt, da amavis weis, wohin es die Signale routen soll, müssen wir ihm noch beibringen wie es diese verarbeiten soll. Für das AM.PDP habe ich diese beiden Policies vorbereitet:

#############################################################################
## POLICY BANKS: AM.PDP
#

# Quarantäne Release Policy
$policy_bank{'AM.PDP-INET'} = {
    protocol => 'AM.PDP',
    inet_acl => [qw( 127.0.0.1 )],
    auth_required_release => 0,
};

# MILTER Policy für MTA zu MTA Traffic
$policy_bank{'AM.PDP-SOCK'} = {
    protocol => 'AM.PDP',
    notify_method => 'smtp:127.0.0.1:10025',
    auth_required_release => 0,
};

Hervorzuheben in der MILTER-Policy Bank ist die dedizierte $notify_method. Über das MILTER-Protokoll gelangen Verarbeitungsanfragen von Postfix zu amavis. Sie können aber nur von Postfix und nicht von amavis initiiert werden. Das ist ein Problem, denn Benachrichtigungen die amavis generiert können so nicht in den Mailtransport gelangen.

Die Antwort auf das Problem ist die dedizierte $notify_method. Sie weist amavis an, Benachrichtigungen an einen lokalen Postfix smtpd-Listener zu routen. Der nimmt sie dann an und transportiert sie näher zu ihrem Ziel.


Kommentare

  1. Thomas

    Thomas (1 Jahr, 10 Monate) # Reply

    Sehr spannend.
    Ich frage mich aber, warum ich Amavis per Milter einbinden sollte, wenn ich doch ClamAV und Spamassassin auch direkt per Milter ansprechen kann.
    So spare ich mit den "Amavis overhead".

  2. Thomas

    Thomas (1 Jahr, 9 Monate) # Reply

    Echt netter Artikel ;)
    Du schreibst das man mit den milter_macro_name gezielt Policy Banks ansprechen kann. Kannst du hier ein Beispiel bringen?
    Danke dir.

  3. Michael

    Michael (1 Jahr, 9 Monate) # Reply

    Heisst das, dass man damit für Submission nicht mehr über einen expliziten Port an Amavis übergeben muss, sondern der macro_daemon ausreicht?

  4. Michael

    Michael (1 Jahr, 9 Monate) # Reply

    Danke Patrick, das ist ein wirklich guter Artikel. Mir persönlich fehlt aber noch der Hinweis, was man alles umstellen muss um vom smtpd_proxy_filter auf smtpd_milters zu wechseln. Kannst Du darauf bitte noch etwas näher eingehen?!

Kommentare deaktiviert.