Gemischtes Doppel: Mails transportieren, säubern und sortieren mit Exim, ClamAV, Procmail und Spamassassin
Nachdem Exim ja bekanntlich der Standard-MTA von Debian Sarge (dem von mir verwendeten Betriebssystem) ist und zudem als sehr flexibel und gut dokumentiert gilt, habe ich mich dazu entschlossen, auf meinem Router einen Mailserver um Exim herum aufzusetzen. Dabei galt es einige Besonderheiten zu berücksichtigen, wie z.B. ausgehende Mails abhängig von der Absenderadresse an verschiedene Mailprovider zu versenden. (Siehe auch mein esmtp-Howto.)
Dieses HowTo beschreibt, wie ich meine spezielle Konfiguration eingerichtet habe und wie sie funktioniert. Es kann und soll nicht die Dokumentationen der einzelnen Programme ersetzen. Außerdem gehe ich hier nicht auf das Einsammeln von Mails auf externen Server mittels Fetchmail o.ä. und das Zurverfügungstellen der Mails mittels IMAP oder POP.
Noch ein weiterer wichtiger Hinweis: Das HowTo beschreibt die beteiligten Programme und deren Konfiguration in einer logischen Reihenfolge. Es sollten aber unbedingt alle Schritte nachvollzogen werden, bevor das Mailsystem aktiviert wird. Falls nicht, kann es zu Problemen von Fehlermeldungen bis hin zu Mailverlust kommen.
Exim sollte bei einem frisch installierten Debian-System eigentlich installiert sein und laufen. Es schadet aber nichts, zur Sicherheit nochmals
# apt-get install exim4-daemon-heavy
auszuführen. Dazu kommt noch, daß Debian Exim in einer „Heavy“- und etwas abgespeckten „Light“-Version anbietet – mit obiger Zeile stellt man sicher, daß die Version mit allen Möglichkeiten installiert ist. Sollten bei der Installation einige Informationen abgefragt werden („Konfiguration in kleine Dateien aufteilen“, Konfigurationsart, etc.), so können diese beliebig beantwortet werden. Diese Konfiguration wird anschließend ohnehin überschrieben.
Anschließend legt man unter /etc/exim4/exim4.conf eine Datei folgenden Inhalts an:
CONFDIR = /etc/exim4
primary_hostname = tigersclaw.suchanek.lan
domainlist local_domains = @:localhost:suchanek.lan:suchanek.dyndns.org
domainlist relay_to_domains =
hostlist relay_from_hosts = 127.0.0.1 : ::::1 : 10.1.0.0/16
av_scanner = clamd:/var/run/clamav/clamd.ctl
tls_certificate = /etc/exim4/exim4.cert.pem
tls_privatekey = /etc/exim4/exim4.key.pem
tls_advertise_hosts = *
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data
never_users = root
host_lookup = *
rfc1413_hosts = *
rfc1413_query_timeout = 0s
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d
begin acl
acl_check_rcpt:
accept hosts = : 127.0.0.1
deny message = Restricted characters in address
domains = +local_domains
local_parts = ^[.] : ^.*[@%!/|]
deny message = Restricted characters in address
domains = !+local_domains
local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
accept local_parts = postmaster
domains = +local_domains
require verify = sender
accept domains = +local_domains
endpass
verify = recipient
accept domains = +relay_to_domains
endpass
verify = recipient
accept hosts = +relay_from_hosts
accept authenticated = *
deny message = relay not permitted
acl_check_data:
warn message = X-Virus: FOUND ($malware_name)
demime = *
malware = *
warn condition = ${if !def:header_Date: {1}}
hosts = :
message = Date: $tod_full
accept
begin routers
smarthost_list:
debug_print = "R: smarthost list for $local_part@$domain"
driver = manualroute
domains = ! +local_domains
senders = wildlsearch;CONFDIR/sender.smarthost.passwd
transport = remote_smtp_list
route_list = * ${extract{1}{:}{${lookup{$sender_address}wildlsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}
host_find_failed = defer
no_more
system_aliases:
driver = redirect
allow_fail
allow_defer
data = ${lookup{$local_part}lsearch{/etc/aliases}}
file_transport = address_file
pipe_transport = address_pipe
localuser:
driver = accept
check_local_user
transport = procmail_pipe
cannot_route_message = Unknown user
begin transports
remote_smtp:
driver = smtp
remote_smtp_list:
debug_print = "T: remote_smtp_list for $local_part@$domain"
driver = smtp
hosts_require_auth = ${extract{1}{:}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}
address_pipe:
driver = pipe
return_output
address_file:
driver = appendfile
delivery_date_add
envelope_to_add
return_path_add
procmail_pipe:
debug_print = "T: procmail_pipe for $local_part@$domain"
driver = pipe
path = "/bin:/usr/bin:/usr/local/bin"
command = "/usr/bin/procmail"
user = $local_part
return_path_add
delivery_date_add
envelope_to_add
begin retry
* * F,2h,15m; G,16h,1h,1.5; F,4d,6h
begin authenticators
plain:
driver = plaintext
public_name = PLAIN
client_send = "^${extract{2}{::}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}\
^${extract{3}{::}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}"
server_condition = \
${if eq {${readsocket{/var/run/courier/authdaemon/socket}\
{AUTH ${strlen:exim\nlogin\n$2\n$3\n}\nexim\nlogin\n$2\n$3\n}}}{FAIL\n}{no}{yes}}
server_set_id = $2
server_advertise_condition = ${if eq{$tls_cipher}{}{}{*}}
login:
driver = plaintext
public_name = LOGIN
client_send = ": ${extract{2}{::}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}} \
: ${extract{3}{::}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}"
server_prompts = Username:: : Password::
server_condition = ${if eq {${readsocket{/var/run/courier/authdaemon/socket} \
{AUTH ${strlen:exim\nlogin\n$1\n$2\n}\nexim\nlogin\n$1\n$2\n}}}{FAIL\n}{no}{yes}}
server_set_id = $1
server_advertise_condition = ${if eq{$tls_cipher}{}{}{*}}
cram_md5:
driver = cram_md5
public_name = CRAM-MD5
client_name = ${extract{2}{:}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}
client_secret = ${extract{3}{:}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}
Allgemeiner Teil der Konfiguration
Zunächst ein paar Worte darüber, wie Exim Mails annimmt und verteilt: Wenn eine SMTP-Verbindung aufgebaut wird, werden während des SMTP-Dialoges zu bestimmten Zeitpunkten sogenannte ACLs abgearbeitet. Diese entscheiden darüber, ob die Mail angenommen oder abgelehnt wird. Sie bestehen aus Regelsätzen, die nacheinander abgearbeitet werden. So lange, bis eine Regel zutrifft.
Wird die Mail angenommen, kontrolliert Exim, ob auf sie die Bedingungen eines Routers zutreffen. Diese Router beschreiben, unter welchen Umstände eine Mail wohin zugestellt wird. Ein solcher Router übergibt die Mail schließlich an einen von mehreren Transports, der die Mail dann tatsächlich zustellt.
Eine genauere Beschreibung dieser Zusammenhänge finden sich in Kapitel 3 der Exim-Dokumentation. Dementsprechend gliedert sich die Exim-Konfigurations-Datei in unterschiedliche Blöcke:
- Allgemeiner Teil, der unbedingt am Anfang stehen muß
- Die Blöcke „acl“, „routers“, „transports“, „retry“, „rewrite“ und „authenticators“, die in beliebiger Reihenfolge stehen können.
Die oben gezeigte Konfiguration basiert zudem auf der mitgelieferten Beispiel-Konfiguration, die sich unter /usr/share/doc/exim4-base/example/example.conf.gz befindet und die in Kapitel 7 der Exim-Dokumentation ausführlich besprochen wird.
Im folgenden sollen die einzelnen Zeilen und Blöcke der Konfiguration etwas näher erklärt werden:
CONFDIR = /etc/exim4
Hier wird eine Variable als „Abkürzung“ zur späteren Verwendung definiert.
primary_hostname = tigersclaw.suchanek.lan
Mit dieser Zeile wird der Hostname gesetzt. Ist der Hostname im System bereits richtig gesetzt, kann auf diese Zeile auch verzichtet werden.
domainlist local_domains = @:localhost:suchanek.lan:suchanek.dyndns.org
domainlist relay_to_domains =
hostlist relay_from_hosts = 127.0.0.1 : ::::1 : 10.1.0.0/16
Anschließend werden drei wichtige Listen definiert. (Exim verwendet als Listentrennzeichen einen Doppelpunkt.) Die erste Liste, local_domains legt fest, an welche Domains Mails gerichtet sein dürfen, damit sie von Exim angenommen werden – sofern nicht eine ACL-Bedingung dagegen spricht. „@“ ist dabei die Abkürzung für den Namen des localhost.
Mit relay_to_domains wird festgelegt, für welche Domains Exim Mails annimmt, um sie anschließend dorthin weiterzuleiten (Relaying). Diese Liste sollte man tunlichst leer lassen, wenn man nicht sehr genau weiß, was man tut, da man sonst sehr schnell ein bei Spammern beliebtes „offenes Relay“ bauen kann.
Schließlich gibt es noch die relay_from_hosts-Liste, mit der festgelegt werden kann, von welchen Hosts E-Mails zum Relaying akzeptiert werden. (::::1 steht dabei für den IPv6-Ausdruck „::1“ für Localhost. Die Doppelpunkte müssen hier verdoppelt werden, um von Exim nicht mit Listentrennzeichen verwechselt zu werden.)
Allen drei Listen ist übrigens gemein, daß sie noch keine tatsächlichen Bedingungen zum Annehmen oder Ablehnen von Mails definieren, sondern lediglich Variablen-Deklarationen sind, die später noch Verwendung finden.
av_scanner = clamd:/var/run/clamav/clamd.ctl
Hierbei handelt es sich um eine spezielle Variablendeklaration, mir man Exim mit ClamAV bekanntmacht. Näheres zu diesem Virenscanner findet sich im entsprechenden Unterpunkt.
tls_certificate = /etc/exim4/exim4.cert.pem
tls_privatekey = /etc/exim4/exim4.key.pem
tls_advertise_hosts = *
Da Exim mit verschlüsselten Verbindungen betrieben werden soll, sind hier die Pfade zum TLS-Zertifikat und -Schlüssel angegeben. Die dritte Zeile weist Exim an, die Möglichkeit einer TLS-Verbindung allen anfragenden Hosts bekanntzugeben. (Als Antwort auf ein EHLO im SMTP-Dialog.)
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data
Von den zahlreichen vorhandenen Möglichkeiten, zu verschiedenen Zeiten eine abbr lang="en" title="Access Control List">ACL abzuarbeiten, werden hier zwei genutzt: Einmal unmittelbar nach Senden des DATA-Kommandos im SMTP-Dialog. und einmal nach dem Ende der Mail, jedoch noch vor Exims Antwort mit einer abschließenden Bestätigung (oder Fehlermeldung).
never_users = root
Aus Sicherheitsgründen soll keine Postzustellung unmittelbar an root direkt vorgenommen werden. Dies ist jedoch nicht weiter problematisch, da üblicherweise ohnehin eine Umleitung dieser Mails an einen anderen Account mittels /etc/aliases erfolgt.
host_lookup = *
Durch das Setzen dieser Option erfragt Exim zu jeder IP-Adresse einer eigehenden Verbindung den zugehörigen Hostnamen im DNS. Wenn man das aus dem einen oder anderen Grund nicht möchte, können hier auch bestimmte Adressbereiche definiert werden, für die die Ausflösung erfolgen soll. (Bis zu einer leeren Liste, was gar keiner Auflösung entspricht.)
rfc1413_hosts = *
rfc1413_query_timeout = 0s
Mit diesen beide Optionen kann definiert werden, ob, wohin und mit welchem Timeout Exim bei SMTP-Verbindungen einen Identifikations-Anfrage gemäß RfC 1413 an den sendenden Host absetzen soll. Der Timeout von 0s deaktiviert dieses Verhalten komplett, da es bei mir zu Komplikationen mit Windows-Clients geführt hatte.
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d
Die letzten beiden Optionen des allgemeinen Teils definieren schließlich noch, daß Bounce-Mails, die ihrerseits Fehler hervorrufen nach zwei Tagen und „frozen“ („eingefrorene“, d.h. wegen Problemen auf Eis gelegte Mails) nach sieben Tagen kommentarlos aus der Warteschlange gelöscht werden sollen.
begin acl
acl_check_rcpt:
accept hosts = : 127.0.0.1 : ::::1
Mit den ersten beiden Zeilen wird der Konfigurationsblock der ACLs sowie die ACL „acl_check_rcpt“ eingeleitet.
Die dritte Zeile sorgt dafür, daß alle Mails von Localhost aus angenommen werden. Sowohl per Socket-Verbindung (erster, leerer Eintrag) als auch per TCP/IP. Das dient u.a. auch dazu, daß Mails, die von Fetchmail eingeliefert werden, angenommen werden, ohne daß es bei bestimmten Spam-Mails zu Fehlermeldungen kommt.
deny message = Restricted characters in address
domains = +local_domains
local_parts = ^[.] : ^.*[@%!/|]
deny message = Restricted characters in address
domains = !+local_domains
local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
Diese beiden Regeln sorgen dafür, daß Mails, die ungültige Zeichen in der Adresse enthalten, abgelehnt werden. (Eine detailierter Erklärung findet sich in Kapitel 7.2 der Dokumentation)
accept local_parts = postmaster
domains = +local_domains
Der Postmaster-Account sollte immer erreichbar sein, daher werden Mails an ihn sehr früh in der ACL zugelassen.
require verify = sender
Damit wird festgelegt, daß Absenderadressen verifiziert werden müssen. Diese Maßnahme hilft u.a., das Spam-Aufkommen zu reduzieren. Dies gilt allerdings nur für Mails, welche direkt an den Server gesendet werden. Mails, die Fetchmail von externen Servern abgeholt hat, wurden ja bereits weiter oben ungefragt akzeptiert.
accept domains = +local_domains
endpass
verify = recipient
Alle Mails, die an die lokalen Domains gerichtet sind und deren Empfänger auch tatsächlich existiert, werden angenommen. Existiert der Empfänger nicht, wird die Mail abgelehnt (implizit) und keine weiteren ACL-Regeln mehr abgearbeitet („endpass“).
accept domains = +relay_to_domains
endpass
verify = recipient
Gleiches gilt für alle Domains, für die Relaying zugelassen werden soll.
accept hosts = +relay_from_hosts
Hier werden nun tatsächlich Mails von bestimmten, oben definierten Hots zum Relaying angenommen. Dies ist eine Möglichkeit, das Versenden von Mails aus einem LAN heraus über Exim zuzulassen.
accept authenticated = *
Außerdem werden Mails grundsätzlich angenommen, wenn sich der Absender authentifziert hat. (Zu den Authentifizierungs-Mechanismen siehe weiter unten.)
deny message = relay not permitted
Schließlich werden noch alle Mails, auf die keine der bisherigen Regeln zugetroffen hat, explizit mit der Meldung „relay not permitted“ abgelehnt.
acl_check_data:
warn message = X-Virus: FOUND ($malware_name)
demime = *
malware = *
Hiermit beginnt die zweite ACL. Die letzten beiden Zeilen bewirken, daß bei allen Mails evtl. MIME-Container entpackt und einem Virenscan unterzogen werden sollen. Dazu wird der oben im allgemeinen Teil definierte „av_scanner“ verwendet. Wird ein Virus gefunden, wird die Header-Zeile „X-Virus: FOUND“ zusammen mit dem Namen des Viruses in die Mail geschrieben.
warn condition = ${if !def:header_Date: {1}}
hosts = :
message = Date: $tod_full
accept
Bei diesen Zeilen handelt es sich um eine kleine zusätzliche Spielerei: Ist kein Date-Header definiert, so wird er hiermit gesetzt.
begin routers
smarthost_list:
debug_print = "R: smarthost list for $local_part@$domain"
driver = manualroute
domains = ! +local_domains
senders = wildlsearch;CONFDIR/sender.smarthost.passwd
transport = remote_smtp_list
route_list = * ${extract{1}{:}{${lookup{$sender_address}wildlsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}
host_find_failed = defer
no_more
Mit diesen Zeilen wird zunächst der Router-Block eingeleitet und der erste Router definiert. Er kümmert sich um alle Mails, die nicht für die lokale Zustellung vorgesehen sind (also an externe Hosts weitergeleitet werden sollen). Dazu schaut er in einer Datei /etc/exim4/sender.smarthost.passwd nach, die wie folgt aufgebaut sein muß:
AbsenderAdresse:ExternerServer:Username:Passwort
Mehrere Zeilen sind natürlich möglich. Dadurch sendet Exim Mails an diejenigen externen Server, bei man jeweils einen Mail-Account hat. Für eine GMX-Adresse sähe dies beispielsweise so aus:
user@gmx.de:mail.gmx.net:1234567:Geheim
Damit wird jede Mail, die den Absender „user@gmx.de“ trägt, an GMX gesendet. (Username und Passwort werden später von den Authenticators ausgelesen.) Konnte kein passender externer Server gefunden werden, wird die Zustellung vorübergehend ausgesetzt (host_find_failed = defer), außerdem wird grundsätzlich kein weiterer Router bearbeitet (no_more), falls dieser Router sich um eine Mail gekümmer hat.
system_aliases:
driver = redirect
allow_fail
allow_defer
data = ${lookup{$local_part}lsearch{/etc/aliases}}
file_transport = address_file
pipe_transport = address_pipe
Dieser Router kümmert sich um Weiterleitungen von Mails, wie sie in /etc/aliases definiert sind.
localuser:
driver = accept
check_local_user
transport = procmail_pipe
cannot_route_message = Unknown user
Schließlich erfolgt die Zustellung an lokale User, indem die Mail an Procmail übergeben wird. Sollte der User nicht existieren, wird eine Fehlermeldung („Unkown User“) von Exim erzeugt.
begin transports
remote_smtp_list:
debug_print = "T: remote_smtp_list for $local_part@$domain"
driver = smtp
hosts_require_auth = ${extract{1}{:}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}
Nun beginnt der Block der Transports. Dies werden immer durch einen Router und nie „selbständig“ aufgerufen.
Der erste Transport kümmert sich um die Einlieferung von Mails bei externen Servern.
address_pipe:
driver = pipe
return_output
address_file:
driver = appendfile
delivery_date_add
envelope_to_add
return_path_add
Diese beiden Transports sind für die Zustellung durch eine Pipe und in eine mbox-Datei zuständig.
procmail_pipe:
debug_print = "T: procmail_pipe for $local_part@$domain"
driver = pipe
path = "/bin:/usr/bin:/usr/local/bin"
command = "/usr/bin/procmail"
user = $local_part
return_path_add
delivery_date_add
envelope_to_add
Hiermit wird eine Mail schließlich zur endgültigen Zustellung an Procmail übergeben.
begin retry
* * F,2h,15m; G,16h,1h,1.5; F,4d,6h
Im Retry-Block werden die Zeitintervalle definiert, nach denen Exim eine erneute Mailzustellung versucht, falls zuvor ein Fehler aufgetreten ist. Eine detailierte Beschreibung dieser Zeile findet sich in Kapitel 7.5 der Dokumentation.
begin authenticators
plain:
driver = plaintext
public_name = PLAIN
client_send = "^${extract{2}{::}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}\
^${extract{3}{::}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}"
server_condition = \
${if eq {${readsocket{/var/run/courier/authdaemon/socket}\
{AUTH ${strlen:exim\nlogin\n$2\n$3\n}\nexim\nlogin\n$2\n$3\n}}}{FAIL\n}{no}{yes}}
server_set_id = $2
server_advertise_condition = ${if eq{$tls_cipher}{}{}{*}}
Schlußendlich werden noch die Authenticators definiert. Diese sind dafür zuständig, die Authentifizierung zu regeln. Sollen die Optionen das Verhalten von Exim als Server regeln, beginnen sie mit einem server_, für das Verhalten als Client (also bei der Anmeldung an einem fremden Server) mit client_.
Der erste Authenticator kümmert sich um den Plain-Mechanismus, bei dem Username und Passwort im Klartext übertragen werden. Wird ein externer Server angesprochen, werden Username und Passwort in der in Router beschriebenen Datei /etc/exim4/sender.smarthost.passwd nachgeschlagen (client_send-Zeile). Möchte sich ein Client bei Exim anmelden, erfolgt die Prüfung auf korrekte Anmeldung durch Rückfrage an einem Courier-Auth-Daemon. Dies hat den Vorteil, daß die Passwörter für IMAP/POP und SMTP stets automatisch konsistent gehalten werden. Unabhängig davon, ob die Userverwaltung über das Betriebssystem, über eine MySQL-Datenbank, über LDAP o.ä. erfolgt. Der Nachteil ist, daß eben ein Courier-Auth-Daemon laufen muß. Dies war jedoch Dank Courier-IMAP automatisch der Fall.
Diese Konfiguration ist im Übrigen aus /etc/exim4/conf.d/auth/30_exim4-config_examples entnommen.
Die letzte Zeile sorgt dafür, daß dieser Mechanis nur Clients angeboten wird, die über eine TLS-gesicherte Verbindung kommunizieren wollen. Dadurch wird verhindert, daß Usernamen und Passwort unverschlüsselt durchs Netz gesendet werden.
login:
driver = plaintext
public_name = LOGIN
client_send = ": ${extract{2}{::}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}} \
: ${extract{3}{::}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}"
server_prompts = Username:: : Password::
server_condition = ${if eq {${readsocket{/var/run/courier/authdaemon/socket} \
{AUTH ${strlen:exim\nlogin\n$1\n$2\n}\nexim\nlogin\n$1\n$2\n}}}{FAIL\n}{no}{yes}}
server_set_id = $1
server_advertise_condition = ${if eq{$tls_cipher}{}{}{*}}
Dieser Block verhält sich im wesentlichen wie der vorhergehende, behandelt jedoch den LOGIN-Mechanismus.
cram_md5:
driver = cram_md5
public_name = CRAM-MD5
client_name = ${extract{2}{:}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}
client_secret = ${extract{3}{:}{${lookup{$sender_address}lsearch{CONFDIR/sender.smarthost.passwd}{$value}fail}}}
Schließlich wird noch die Möglichkeit zur Verschlüsselten CRAM-MD5-Anmeldung eingerichtet. Auf die Nutzung dieses Mechanismus durch externe Clients wurde hier verzichtet, da dies komplizierter einzurichten wäre und wegen der bereits beschriebenen TLS-Sicherung kein Grund für eine zweite Verschlüsselung besteht.
Die Installation und Konfiguration von ClamAV ist schnell erledigt:
# apt-get install clamav clamav-freshclam clamav-daemon
Danach sollte man sicherstellen, daß die Datei /etc/clamav/clamd.conf die Zeile AllowSupplementaryGroups enthält, was aber bei Debian Sarge standardmäßig der Fall ist. Schließlich muß noch der ClamAV-User der Exim-Gruppe hinzugefügt werden, um Problemen mit Zugriffsrechten aus dem Weg zu gehen:
# adduser clamav Debian-exim
Da Exim bei der Installation ClamAV normalerweise automatisch startet, muß ClamAV nun noch neu gestartet werden, um ihn mit der neuen Gruppenzugehörigkeit bekannt zu machen:
# /etc/init.d/clamav-daemon restart
Der zweite Daemon, Freshclam, dient im Übrigen dazu, ClamAVs Virendefinitionen auf dem aktuellen Stand zu halten. Sein Verhalten wird bereits mit einigen einfachen Fragen bei der Installation festgelegt.
Procmail ist dafür zuständig, die Mails in die Postfächer in die User zuzustellen und dort auch ggf. auch in Unterordner einzusortieren. Ich habe mich dazu entschlossen, in Procmail auch die Spam-Filterung mit Spamassassin einzubauen. Dieses Verfahren bietet mehrere Vorteile:
- Die Spamfilterung kann einfach userspezifisch (de-)aktiviert werden
- Bestimmte Mails können auf einfache Art am Spamfilter vorbeigeschleust werden
- Spamassassin baut userspezifische Datenbanken für den Bayes-Filter auf
- Spamassassin kann so konfiguriert werden, daß erkannte Spam-Mails in einen Mailanhang verpackt werden (hilfreich bei übereifrigen Mailclients, die sonst durch das Nachladen von Grafiken in HTML-Mails über speziell präparierte URLs die eigene Adresse verfizieren können) und zudem ein detailierter Filterreport beigefügt wird.
Wer stattdessen eine zentrale Spamfilterung in Exim bevorzugt, findet im c't-Server-Forum eine Anleitung dazu.
Procmail selbst lässt sich ebenfalls sehr einfach installieren:
# apt-get install procmail
Falls – wie bei mir – ein IMAP-Server zum Einsatz kommen soll, sollte nun im Homeverzeichnis jedes Users ein Mailverzeichnis angelegt werden. Dies erledigen komfortabel die folgenden Zeilen:
# cd /home
# for a in *;
> do su $a -c "maildirmake /home/$a/Maildir";
> done
Ansonsten würden alle Mails an einen User in einer mbox-Datei unter /var/spool/mails/user landen.
Möchte man auch Quarantäne-Verzeichnise für Viren- und Spam-Mails einrichten, geschieht dies ebenfalls schnell und problemlos hiermit:
# cd /home
# for a in *;
> do su $a -c "maildirmake /home/$a/Maildir/VIRUS";
> done
# cd /home
# for a in *;
> do su $a -c "maildirmake /home/$a/Maildir/SPAM";
> done
Schließlich muß noch in jedem User-Home-Verzeichnis eine .procmailrc-Datei angelegt werden. Diese könnte zum Beispiel so aussehen:
SHELL=/bin/sh
PATH=$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin
MAILDIR=$HOME/Maildir/
LOGFILE=$MAILDIR/procmail.log
LOGABSTRACT="all"
VERBOSE="on"
:0
* ^X-Virus: FOUND
.VIRUS/
:0fw
* < 256000
| spamc
:0
* ^X-Spam-Flag: YES
.SPAM/
:0
*
$HOME/Maildir/
Hierbei stehen am Anfang zunächst einige allgemeine, selbsterklärende Deklarationen. Danach folgen die Filterregeln, die nacheinander abgearbeitet werden, bis eine Bedingung zutrifft. Danach folgende Regeln werden nicht mehr geprüft.
Die erste Regel dient dazu, an Hand der Zeile „X-Virus: FOUND“ (siehe auch ClamAV) Mails mit Viren darin zu identifizieren und in das Unterverzeichnis „VIRUS“ zu verschieben.
Die zweite Regel ruft für alle Mails, die kleiner als 256 kB sind (Spam-Mails sind selten so groß), Spamassassin auf.
Hat Spamassassin eine Mail als Spam erkannt, was sich am „X-Spam-Flag: YES“-Header erkennen lässt, wird diese Mail in das „SPAM“-Unterverzeichnis verschoben.
Die letzte Regel sorgt schließlich dafür, daß alle Mails, die weder Spam noch Viren sind, im Mail-„Hauptverzeichnis“ abgelegt werden. Diese Regel ist äußerst wichtig, da Mail ansonsten kommentarlos verworfen werfen.
Weitere persönliche Filterregeln sollten also unbedingt vor dieser letzten Regel eingefügt werden.
Spamassassin wird zunächst mit einigen weiteren Zusatzpaketen installiert:
# apt-get install spamassassin spamc pyzor razor
Nun muß Spamassassin noch konfiguriert werden. Eine komfortable Möglichkeit bietet http://www.yrex.com/spam/spamconfig.php, wo man sich mit Hilfe eines Webformulars eine Basis-Konfiguration zusammenstellen kann. Meine eigene Konfiguration /etc/spamassassin/local.cf ist so aufgebaut:
required_score 5.0
rewrite_header subject *****SPAM*****
report_safe 2
use_bayes 1
bayes_auto_learn 1
bayes_auto_learn_threshold_nonspam 0.2
bayes_auto_learn_threshold_spam 10
bayes_ignore_header X-purgate
bayes_ignore_header X-purgate-ID
bayes_ignore_header X-purgate-Ad
bayes_ignore_header X-GMX-Antispam
bayes_ignore_header X-Antispam
bayes_ignore_header X-Spamcount
bayes_ignore_header X-Spamsensitivity
skip_rbl_checks 0
use_razor2 1
use_dcc 1
use_pyzor 1
ok_languages en de
ok_locales en
score ALL_TRUSTED 0
Spamassassin vergibt beim Scannen Punkte, für jede Regel, die zutrifft, die addiert werden. „required_score“ gibt dabei den Schwellwert an, ab dem eine Mail als Spam klassifiziert wird.
Die nächsten beiden Zeilen sorgen dafür, daß Spam-Mails um die Zeichenkette „*****SPAM*****“ im Betreff ergänzt werden und als Mail-Anhang verpackt werden sollen.
Die folgenden vier Optionen mit „bayes“ im Namen aktivieren das Bayes-Filter und konfigurieren ihn. Dabei handelt es sich um ein Filter, das nicht nach festen Regeln arbeitet, sondern eine statistische Analyse, basierend auf Spam- und Nicht-Spam-Mails, die man ihm zuvor zum Lernen vorgelegt hat, erstellt. Dieses Filter wird standardmäßig erst aktiv, nachdem es mindestens 200 Spam-Mails gelernt hat. Im Interesse einer guten Treffergenauigkeit sollte man dieses Filtertraining auch wirklich mit eigenen Mails durchführen und nicht mit generischen Spam-Mail-Katalogen aus dem Internet.
Außerdem wird noch die Autolearning-Funktion aktiviert. Diese bewirkt, daß Mails, die zuvor von festen Filtern als Spam oder Nicht-Spam erkannt wurden, automatisch als solche gelernt werden. Die dritte und vierte Option definieren Schwellwerte für die Spam-Punktezahl, die für das Lernen eingehalten werden müssen.
Schließlich wird das Bayes-Filter noch angewiesen, bestimmte Header anderer Spam-Filter unberücksichtigt zu lassen, um davon nicht beeinflusst zu werden.
Die vier nachfolgenden Zeilen aktivieren einige weitere Filter-Methoden.
Die „ok_languages„-Option gibt an, in welchen Sprachen man Mails empfängt. Spamassassin versucht, die Sprache, in der eine Mail verfasst ist, zu erkennen. Handelt es sich dabei um eine nicht „freigegebene„ Sprache, wird der Spam-Punktestand erhöht.
Die „ok_locales“ hat den selben Effekt für Zeichensätze. „en“ steht hier stellvertretend für alle Sprachen mit lateinischen Buchstaben.
Die nächste Zeile deaktiviert de facto die „ALL-TRUSTED“ Filterregel, in dem man ihr den Punktewert 0 zuweißt. Diese Regel für ansonsten verstärkt zu dem Problem, daß Spam-Mails nicht als solche erkannt werden.
Wie bereits erwähnt, arbeitet das Bayes-Filter auf der Basis einer statistischen Analyse. Dabei kann es natürlich vorkommen, daß diese Analyse falsch ist, was zu einer falschen Einstufung der Mail als Spam bzw. Nicht-Spam führen kann. Daher ist es notwendig, auch während des späteren Betriebs Spamassassin an Hand von Mails weiter lernen zu lassen. (Vorzugsweise von solchen Mails, die falsch erkannt wurden.)
Um diesen Lernvorgang komfortabel vom Mail-Client aus vornehmen zu können, habe ich folgendes System eingerichtet: Zunächst werden als Unterordner des „SPAM“-Ordners die zwei Unterordner „Spam lernen“ und „Ham lernen“ eingerichtet. Dies kann entweder vom IMAP-Mail-Client aus oder mittels maildirmake (vgl. Procmail) geschehen.
In diesem Unterordner kann man nun einfach per Mail-Client Mails, die netsprechend gelernt werden sollen, verschieben. Für den Lernprozeß selbst sorgt das folgende Shell-Script, das ich per cron regelmäßig alle 5 min laufen lasse:
#!/bin/sh
# Skript zum Lernen von Spam und Ham aus IMAP-Postfächern der User
# Sebastian Suchanek, 2006-01-13
cd /home
for user in *; do # alle User bearbeiten
if [ -r "/home/$user/Maildir/.SPAM.Spam lernen/cur/" ]; then # Schauen, ob es ein Spam-Lernen-Verzeichnis gibt
cd /home/$user/Maildir/.SPAM.Spam\ lernen/cur/
for mail in *; do # alle Spam-Mails lernen
if [ "$mail" != "*" ]; then # Schauen, ob Mails vorhanden sind
/usr/bin/sa-learn --spam --dbpath /home/$user/.spamassassin/ /home/$user/Maildir/.SPAM.Spam\ lernen/cur/$mail
cat $mail | procmail -d $user
rm $mail # Spam-Mail lernen, neu sortieren und löschen
else
echo "No Spam to learn for user $user"
fi
done
fi
if [ -r "/home/$user/Maildir/.SPAM.Ham lernen/cur/" ]; then # Schauen, ob es ein Spam-Lernen-Verzeichnis gibt
cd /home/$user/Maildir/.SPAM.Ham\ lernen/cur/
for mail in *; do # alle Ham-Mails lernen
if [ "$mail" != "*" ]; then # Schauen, ob Mails vorhanden sind
/usr/bin/sa-learn --ham --dbpath /home/$user/.spamassassin/ /home/$user/Maildir/.SPAM.Ham\ lernen/cur/$mail
cat $mail | /usr/bin/spamassassin -d | procmail -d $user
rm $mail # Ham-Mail lernen, säubern, neu sortieren und löschen
else
echo "No Ham to learn for user $user"
fi
done
fi
done
Die Kommentare im Skript sollten eigentlich für das Verständnis der Funktion ausreichen.
Inbetriebnahme und Ausblick
Nachdem nun alle beteiligten Programme konfiguriert sind, können sie nun (neu-)gestartet werden. Es ist dringend anzuraten, daß zuerst mit einigen Testmails die Funktionsfähigkeit des Systems überprüft wird, um späteren Produktivbetrieb keine Mails zu verlieren.
Hat man etwas an den ACLs geändert, so empfiehlt es sich außerdem, auf der Konsole folgenden Befehl einzugeben:
# telnet relay-test.mail-abuse.org
Dadurch veranlasst man einen kurzen Test, ob der eigene Mailserver möglicherweise doch ein offenes Relay ist. Das Ergebnis bekommt man unmittelbar danach über die Telnet-Verbindung ausgegeben.
Was nun zu einem wirklich kompletten Mailserver noch fehlt, ist die Zurverfügungstellung der Mails an externe Clients mittels IMAP oder POP, sowie ggf. das Abholen von Mails von externen Servern mittels Fetchmail. Beides überlasse ich anderen Tutorials und HowTos.
Auch sonst bietet Exim noch viel Raum für weitere Optionen und Spielereien, die möglicherweise irgendwann später ebenfalls in weiteren Texten beschrieben werden.
Anleitung von Sebastian Suchanek
|