Mailserver 10 von 10: SpamAssassin anlernen mit maillearn.phs

Jahrelang habe ich mich davor gedrückt, doch endlich ist es geschafft - die Einrichtung eines eigenen Mailservers. In einer kleinen Artikelserie möchte ich einmal meine Erfahrungen und Konfigurationsschritte festhalten. Diese Serie basiert auf zahllosen Tutorials, Best Practices, Gesprächen mit Mailserver-Betreibern, aus endlosem Dokumentationen lesen und dem Wissen der Bücher "Postfix" von Kyle D. Dent und "Postfix - Einrichtung, Betrieb und Wartung" von Ralf Hildebrandt und Patrick Ben Koetter. Vielleicht werden sie ja nützlich für andere Leute sein, die das gleiche erreichen wollen.

Doch eines vorab: Die Einrichtung und der Betrieb eines Mailservers ist eine komplexe Sache. Klar, die Software lässt sich schnell installieren, doch fertig ist man danach noch lange nicht, wenn man ernsthaft damit arbeiten möchte.

Der Fahrplan der Artikelserie:

  1. Einleitung
  2. Mailempfang und Mailversand mit Dovecot und Postfix
  3. Virenprüfung mit ClamAV
  4. Mailsignatur mit OpenDKIM
  5. Spamprüfung mit SpamAssassin
  6. Mailabruf mit Dovecot
  7. Mailboxen konfigurieren mit mailconf.phs
  8. Mails synchronisieren mit mailsync.phs
  9. Mailqueue überwachen mit mailqueue.phs
  10. SpamAssassin anlernen mit maillearn.phs

Mit dem heutigen und letzten Artikel neigt sich die Artikelserie zum Thema Mailservereinrichtung dem Ende zu. Wir werden uns nun einmal ansehen, wie wir den Spamfilter SpamAssassin ein wenig optimieren können.

Unser Mailserver ist bereits halbwegs gut gegen Spam gerüstet. Wir akzeptieren nur Mails für existierende Adressen, wir prüfen den absendenden Mailserver auf Herz und Nieren und SpamAssassin nutzt bereits ein paar Filter, die schon Spammails aussortieren, wenn sie allzu auffällig sein sollten.

Allerdings kann SpamAssassin noch mehr: Es beinhaltet ein Bayes Network - eine Art lernfähigen Filter - der relativ gut darin sein könnte, Spam zu erkennen. Könnte deshalb, da solch ein Bayes Network angelernt werden muss - man muss anhand von Beispielen vorführen, wie Gutfälle (Ham) und wie Schlechtfälle (Spam) aussehen. Anhand dieser Beispiele kann SpamAssassin dann eigene Entscheidungen treffen.

Eine Möglichkeit, die Gut- und Schlechtfälle zu erhalten, ist, vorgefertigte Pakete zu verwenden, die mit Spam angelernt werden - so ähnlich wie bei einem Virenscanner gibt es dann regelmäßig Updates, damit SpamAssassin auch neue Spamwellen zuverlässig erkennen kann.

Das ganze hat jedoch einen Haken: Durch diese Pakete erkennt SpamAssassin nur Spammails des Durchschnittsnutzers. Es kommt jedoch häufiger vor, dass man ganz spezifische Mails filtern will - z.B. E-Mail-Verteiler, die man nicht abbestellen kann oder Targeted Spam, den Verwalter solcher Pakete möglicherweise gar nicht zu Gesicht bekommen.

Aber wieso sollen wir uns darauf verlassen, dass jemand anderes unseren Spam kennt, wenn wir das selbst doch viel besser können? SpamAssassin bringt nämlich ein Tool - "sa-learn" - mit, mit dessen Hilfe wir SpamAssassin beibringen können, was für uns Gutfälle und was Schlechtfälle sind. Um das ganze ein wenig zu automatisieren, habe ich zwei Scripte geschrieben: maillearn-s.phs und maillearn-c.phs

maillearn-s.phs läuft regelmäßig auf dem Server, auf dem die E-Mails abgerufen werden können. Dort geht es die einzelnen Postfächer durch und schaut in den Unterordner, in dem die gelesenen Nachrichten des Posteingangs abgelegt sind. Diese E-Mails werden SpamAssassin als Gutfälle präsentiert. Zudem schaut es in den Unterordner, in dem die gelesenen Spammails abgelegt sind. Genau diese E-Mails sind natürlich dafür geeignet, als Schlechtfall zu dienen.

Dadurch, dass der Nutzer selber E-Mails in den Spamordner legen kann, kann er somit aktiv mitentscheiden, was als Spam gilt. Gleichzeitig kann er mit dem Verschieben und Löschen von Mails aus dem Spamordner entscheiden, was evtl. doch kein Spam ist und nur fälschlich dort einsortiert wurde. Der Filter entwickelt sich also mit den Anforderungen und Gewohnheiten der Benutzer mit. Mir persönlich gefällt dieser Ansatz sehr.

Wenn alle Mailboxen durchgearbeitet wurden, exportiert maillearn-s.phs die gesammelten Daten und überträgt sie per SSH an den Server, auf dem SpamAssassin tatsächlich läuft.

Dort kommt dann maillearn-c.phs zum Einsatz. Es prüft, ob ein neuer Export vorliegt und importiert die Daten in die lokale SpamAssassin-Installation.

Die Installation von maillearn-s.phs läuft so wie immer: Wir benötigen PHP und Mercurial und ziehen dann die entsprechenden Repositories heran:

1
2
3
4
5
6
sudo apt-get update
sudo apt-get install php5-cli php5-curl php5-ssh2 mercurial

sudo hg clone https://hg.nhg.name/maillearn-s/
sudo hg clone https://hg.nhg.name/pushinfo/
sudo hg clone https://hg.nhg.name/unchroot/

Schauen wir uns einmal die Konfigurationen ein wenig genauer an. Bei maillearn-s.phs ist die Konfiguration wieder zweigeteilt - es gibt die statische Konfiguration in der Datei "maillearn-s.conf.phs" und es gibt die serverspezifische Konfiguration in der Datei "maillearn-s.conf.<x>.phs". Genau wie bei mailsync.phs müssen wir ja potentiell mehrere E-Mail-Empfangsserver mit den Spaminformationen versorgen.

Ein Blick in die Datei "maillearn-s.conf.phs" offenbart wenig neues - die Pfade zu den Mailordnern (Posteingang, Posteingang-Unterordner und Spamorder), der Pfad der Pushnachricht, der Pfad der Statusdatei, der Pfad der Lock-Datei (um Mehrfachstarts zu verhindern), sowie die eigentlichen Aufrufe von "sa-learn" (SA_LEARN_*):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php

  # defines where the push notification is located:
 # * this path MUST point to a single file
 # * the optional placeholder {%server} may be used which
 #   contains the parameter given to the script for execution
 define("PUSHINFO_PATH", "/srv/pushinfo/messages/maillearn-s.phs_{%server}");

  # defines where the script stores its status:
 # * this path MUST point to a single file
 # * the optional placeholder {%server} may be used which
 #   contains the parameter given to the script for execution
 define("STATUS_PATH", "/srv/maillearn-s/status/status.{%server}");

  # define timeout for PHP execution (as int)
 # alternatively defines a lock file (as string):
 # * this path MUST point to a single file
 # * the optional placeholder {%server} may be used which
 #   contains the parameter given to the script for execution
 define("TIMEOUT", "/srv/maillearn-s/status/lock.{%server}");

  # defines the local path to the maildir cur:
 # * this path MAY either point to a single folder OR to a
 #   list of folders by employing asterisks ("*")
 # * do NOT forget the trailing slash
 # * the optional placeholder {%server} may be used which
 #   contains the parameter given to the script for execution
 define("LOCAL_MAILDIR_CUR", "/srv/mailusers/*/Maildir/cur/");

  # defines the local path to the INBOX maildir cur:
 # * this path MAY either point to a single folder OR to a
 #   list of folders by employing asterisks ("*")
 # * do NOT forget the trailing slash
 # * the optional placeholder {%server} may be used which
 #   contains the parameter given to the script for execution
 define("LOCAL_MAILDIR_INBOX_CUR", "/srv/mailusers/*/Maildir/.INBOX.*/cur/");

  # defines the local path to the SPAM maildir cur:
 # * this path MAY either point to a single file OR to a
 #   list of files by employing asterisks ("*")
 # * do NOT forget the trailing slash
 # * the optional placeholder {%server} may be used which
 #   contains the parameter given to the script for execution
 define("LOCAL_MAILDIR_SPAM_CUR", "/srv/mailusers/*/Maildir/.Junk/cur/");

  # defines the command to clear learned HAM and SPAM
 define("SA_LEARN_CLEAR", "sa-learn --clear");

  # defines the command to learn HAM
 define("SA_LEARN_HAM", "sa-learn --no-sync --local --ham");

  # defines the command to learn SPAM
 define("SA_LEARN_SPAM", "sa-learn --no-sync --local --spam");

  # defines the command to save learned HAM and SPAM
 define("SA_LEARN_SYNC", "sa-learn --sync");

  # defines the command to backup learned HAM and SPAM
 define("SA_LEARN_BACKUP", "sa-learn --backup >");

  # defines the push message
 define("PUSHINFO_MESSAGE_HAM",  " hams and ");
  define("PUSHINFO_MESSAGE_SPAM", " spams have been learned.");

?>

Auch die serverspezifische Konfiguration in der Datei "maillearn-s.conf.<x>.phs" ist einfach verständlich:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

  # defines the remote absolute path to the exchanged file
 define("REMOTE_PATH", "/home/maillearn/status");

  # defines the remote hostname
 define("REMOTE_HOSTNAME", "<REPLACE-ME>");

  # defines the remote port
 define("REMOTE_PORT", 22);

  # defines the remote user
 define("REMOTE_USERNAME", "<REPLACE-ME>");

  # defines the remote password
 define("REMOTE_PASSWORD", "<REPLACE-ME>");

?>

Der Aufruf von maillearn-s.phs funktioniert dann auf folgende Art, wobei die entsprechende Konfigurationsdatei "maillearn-s.conf.<server>.phs" für die Ausführung mit herangezogen wird:

1
sudo php maillearn-s.phs <server>

Ihr könnt den Aufruf entweder nach Bedarf durchführen oder aber z.B. CRON regelmäßig laufen lassen. Zu oft sollte das jedoch nicht geschehen, da dabei sämtliche abgelegten E-Mails durchgearbeitet werden.

Die Installation von maillearn-c.phs auf dem E-Mail-Empfangsserver, auf dem auch SpamAssassin installiert ist, ist ähnlich einfach. Ich gehe dabei davon aus, dass ihr selbst einen Nutzer anlegt, den maillearn-s.phs für die Übertragung der Daten verwenden kann. Beachtet auch, dass dieses Mal das Repository "maillearn-c" verwendet wird, anstelle von "maillearn-s":

1
2
3
4
5
6
sudo apt-get update
sudo apt-get install php5-cli php5-curl mercurial

sudo hg clone https://hg.nhg.name/maillearn-c/
sudo hg clone https://hg.nhg.name/pushinfo/
sudo hg clone https://hg.nhg.name/unchroot/

Die Konfiguration ist bei maillearn-c.phs nicht geteilt, sie befindet sich in der Datei "maillearn-c.conf.phs". Ich zeige erst einmal den Inhalt der Datei und beschreibe sie anschließend etwas genauer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

  # defines the local absolute path to the spamassassin backup
 define("LOCAL_ABSOLUTE_PATH", "/home/maillearn/status");

  # defines where the push notification is located
 define("PUSHINFO_PATH", "/srv/pushinfo/messages/maillearn-c.phs");

  # defines where the script stores its status
 define("STATUS_PATH", "/srv/maillearn-c/status/status");

  # define timeout for PHP execution (as int)
 # alternatively defines a lock file (as string)
 define("TIMEOUT", "/srv/maillearn-c/status/lock");

  # defines the command to restore learned HAM and SPAM
define("SA_LEARN_RESTORE", "sa-learn --restore");

  # defines the dbpath parameter
 define("DBPATH_PARAM", "--dbpath");

  # defines the relative dbpath
 # do not forget the leading slash
 define("DBPATH_RELATIVE", "/.spamassassin/bayes");

  # defines the prefspath parameter
 define("PREFSPATH_PARAM", "--prefspath");

  # defines the relative prefspath
 # do not forget the leading slash
 define("PREFSPATH_RELATIVE", "/.spamassassin/user_prefs");

  # defines the user the script shall run as
 define("PROCESS_USER", "debian-spamd");

  # defines the group the script shall run as
 define("PROCESS_GROUP", "debian-spamd");

  # defines the push message content
 define("PUSHINFO_MESSAGE", "new HAM and SPAM learned");

?>

Der LOCAL_ABSOLUTE_PATH gibt an, wohin die SpamAssassin-Daten hochladen wird. Dies entspricht also dem REMOTE_ABSOLUTE_PATH in der Konfiguration von maillearn-s.phs.
Auch maillearn-c.phs kann Pushnachrichten mit Hilfe von Pushinfo versenden (PUSHINFO_PATH) und es braucht ebenfalls eine Datei, in der es seinen aktuellen Status ablegen kann (STATUS_PATH).
Ebenfalls wird wieder Locking durch eine Datei verwendet, um parallele Starts zu vermeiden, da das zu Problemen mit der SpamAssassin-Datenbank führen kann (TIMEOUT).

Nun wird es jedoch etwas komplexer, wobei ich mich hier beispielhaft auf Debian beziehe. SpamAssassin (genauer gesagt spamd) wird als ein spezifischer Nutzer (in unserem Fall "debian-spamd") ausgeführt. In dessen Home-Verzeichnis müssen die Daten schlussendlich importiert werden. Um das zu erreichen, muss der PROCESS_USER und die PROCESS_GROUP auf den Nutzer und die Gruppe von spamd gesetzt werden.

Bei der Abarbeitung sucht sich maillearn-c.phs das Home-Verzeichnis des PROCESS_USER aus der Datei "/etc/passwd" heraus und parameterisiert "sa-learn" so, dass die Daten anschließend in das richtige Verzeichnis importiert werden. Diese Parameterisierung kann durch DBPATH_* und PREFSPATH_* beeinflusst werden. Im Normalfall sollte das jedoch nicht notwendig sein.

Der Aufruf von maillearn-c.phs erfolgt gänzlich ohne Parameter. Auch hier steht es euch frei, den Aufruf nach Bedarf zu tätigen oder regelmäßig z.B. per CRON durchzuführen. Denkt daran, dass ihr maillearn-c.phs nur starten braucht, wenn vorher maillearn-s.phs auf dem anderen Server verwendet wurde:

1
sudo php maillearn-c.phs

Die Arbeit, die durch die beiden Scripte maillearn-s.phs und maillearn-c.phs verrichtet wird, ist überschaubar. Natürlich kann sie auch manuell oder durch andere kleine Scripte realisiert werden. Es steht euch wie immer frei, eigene Implementierungen zu verwenden. Ich persönlich mag es jedoch, dass diese Prozesse automatisiert ablaufen und mir - dank Pushinfo - mitteillen, wie ihr Status ist. 🙂

Puuuuuuh...

An dieser Stelle endet dann auch unsere kleine Exkursion in die Welt der Mailserveradministration. Wir haben gesehen, welche Komponenten für solch ein Vorhaben benötigt werden, wie diese aufeinander abgestimmt konfiguriert werden, in welche Zwickmühle man gerät, wenn man ein System mit höherer Ausfallsicherheit etablieren will und deshalb Daten hin- und her synchronisieren muss und wir haben uns angesehen, welche Themen sonst noch interessant sind - wie die Überwachung der Mailqueue, weiterführende DNS-Einträge, Whitelisten, Blacklisten und dergleichen.

Auch von mir fällt nun eine Last ab. Ich hatte vor, diese Artikelserie und die parallele Einrichtung meiner Server noch vor meinem Jahresendurlaub zu beenden. Darin enthalten sind etwa 1 Monat Bücher lesen, 2 Monate Internetrecherche samt Aufbau der einzelnen Konfigurationen, das Schreiben der eigentlichen Artikel und das Entwickeln der einzelnen Scripte (mailsync, mailconf, mailqueue, maillearn-c und maillearn-s) - das alles natürlich neben dem Vollzeitjob.

Ich hoffe, dass ihr ein wenig Spaß an dieser Artikelserie hattet und, dass sie einigen von euch hilft, ihren eigenen Mailserver aufzusetzen oder ihre vorhandene Konfiguration zu verbessern. Solltet ihr Fehler, Anmerkungen, Verbesserungsvorschläge oder Rückfragen haben, zögert nicht, einen Kommentar zu schreiben. 😉

Lehrende Grüße, Kenny

Schreibe einen Kommentar

Um Ihnen beim weiteren Kommentieren auf dieser Webseite die erneute Eingabe Ihrer Daten zu ersparen, wird beim Absenden Ihres Kommentars ein Cookie an Ihren Browser gesendet und von diesem gespeichert. Mit dem Absenden eines Kommentars auf dieser Webseite stimmen Sie der Speicherung und Übertragung dieses Cookies explizit zu.

Pflichtfelder sind mit * markiert.