ISP Mail Server mit Exim 4, Dovecot, MySQL und SpamAssassin auf Debian GNU/Linux etch

Autor: B.Sc. Inf. Dominik Schulz <lkml@ds.gauner.org>
Version: 0.2.653
Date:2009-02-05
Copyright: This document is released under the terms of the GNU Free Documentation License.
Status: Released
Abstract:Dieses Dokument beschreibt die Einrichtung eines Mailservers für Multi-User-Hosting Umgebungen unter Verwendung von Debian etch, exim4, Dovecot, MySQL und SpamAssassin.

Inhalt

Flattr this

Squeeze

Obwohl inzwischen nicht einmal mehr Security Support für etch verfügbar ist lässt sich dieses Tutorial größtenteils noch auf aktuellen Systemen umsetzen, ggf. mit kleineren Anpassungen. Für Debian Squeeze habe ich ein Postfix Tutorial erstellt, das viele der hier vorgestellten Ideen aufgreift und vertieft.

Lenny

Im Moment habe ich nicht die Absicht diese Anleitung für Lenny zu aktualisieren. Zum einen ist die Anleitung fast unverändert auf Lenny übertragbar und zum anderen setze ich im Moment hauptsächlich auf Postfix. Dazu kann ich das Tutorial von C. Hass empfehlen: ISPmail tutorial for Debian Lenny.

Einleitung

Dieses Dokument beschreibt die Einrichtung eines Mailservers für Shared-Hosting Umgebungen auf einem System mit einer festen IP-Adresse. Das Ziel dieses Dokumentes ist es ein System zu schaffen das über folgenden Eigenschaften verfügt:

Dieses Dokument schildert eine Mailserver-Konfiguration die von mir auf einigen Systemen schon seit längerem erfolgreich eingesetzt wird und sich bewährt hat. Die ursprüngliche Exim Konfiguration beruht auf Informationen von http://www.xmn-berlin.de/~marte/exim/exim4_mysql_amavis_spamassasin.html, die Struktur dieses Dokumentes orientiert sich an dem ISP-Mail Tutorial von C. Haas. In der Regel wird so ein Setup auf einem, oder mehreren, Rootservern betrieben.

Folgende Techniken und Protokolle werden im Laufe der Einrichtung berührt werden, daher sollten Sie damit grundlegend vertraut sein.

Komponenten

Diese Konfiguration baut auf die folgenden Komponenten auf, fast alle sind als Debian Pakete in den offiziellen Repositories enthalten:

Verwaltung der virtuellen Domains und Adressen

Auf klassischen UNIX-Systemen ist dem System genau ein Hostname (z.b. server) in eine Domäne (z.b. doe.org) zugeordnet woraus genau ein FQDN (Fully-Qualified-Domain-Name) folgt (z.b. server.doe.org). Die auf diesem System existierendem Benutzerkonten bilden zusammen mit dem FQDN und einem @ als Trennzeichen die öffentliche Email Adresse. Also etwas wie john@zeus.dfn.de. Auf diesen Systemen werden die eintreffenden Nachrichten i.d.R. unter /var/mail/benutzer oder im Home des Benutzers abgespeichert. Da die Benutzer auf einem klassischen Webhosting System aber gar keine Systemkonten haben sollen ist dieser Ansatz in diesem Fall unpraktisch. Viele Hoster bevorzugen es die Benutzerzugänge in einer Datenbank, also so etwas wie MySQL, PostgreSQL oder auch LDAP, zu speichern. Da diese Benutzer dem System erst einmal nicht bekannt sind muss man dem Mailserver beibringen. Ein Ansatz wäre die Benutzerkonten von Hand in eine Textdatei einzutragen. Da dies von Hand zu kompliziert und zeitaufwändig wäre, ist es wünschenswert, dass der Mailserver quasi in Echtzeit die Benutzerdatenbank abfragen kann um dort die Benutzerkonten auszulesen.

Darüber hinaus benötigt der Mailserver noch eine Datenbank der Domains für die er zuständig ist. Es bietet sich an diese im gleichen Format wie die Benutzerdaten abzulegen.

Id Domain Aktiv Server
0 domain.de yes 1
1 domain.com no 2
2 domain.net yes 1
3 server.tld yes 3
n ...    

Da die Datenbank in diesem Setup nur die Informationen über die Postfächer, nicht jedoch die Mails, abspeichert benötigt der Mailserver noch Informationen über den Speicherort des Postfaches im Dateisystem. Für das speichern von Postfächern gibt es zwei verbreitete - und noch ein paar nicht so weit verbreitete - Formate. Dies ist zum einen das klassische Mbox Format und das etwas neuere Maildir Format. Beide Formate haben Vor- und Nachteile, ich bevorzuge hier Maildir. Unter anderem weil es besser mit meinem Backupsystem kooperiert. Ich verwende ein inkrementelles Backup das auf rsync aufbaut. Da beim Mbox Format alle Nachrichten eines Postfaches in einer Datei gespeichert werden, würde das bedeuten, dass bei geringfügigen Änderungen die komplette Mailbox erneut gesichert würde. Maildir spart mir hier einfach Speicherplatz. Es gibt vom Entwickler von Dovecot ein neues Format mit dem Namen dbox das dem Maildir ähnelt. Dieses Format ist noch sehr neu, aber eine interessant Option für die Zukunft.

Die Speicherung der Postfächer erfolgt in meinem Setup unter dem Pfad /srv/mail/domain.tld/benutzer. Die Struktur des Pfades wird Exim und Dovecot fest in der Konfiguration vorgegeben und die variablen Teile, also Domain und Benutzer, werden dann zur Laufzeit anhand der Informationen aus der Datenbank ergänzt.

Virtueller Benutzer Speicherort des Postfaches
john@doe.org /srv/mail/doe.org/john
adam@doe.org /srv/mail/doe.org/adam
bob@doe.org /srv/mail/doe.org/bob

Die Tabelle hosts speichert die Informationen über die vorhandenen Server ab. Dies kann entweder ein einzelner sein, oder auch mehrere.

Server FQDN
server1 server1.domain.tld
server2 server2.domain.tld

Warum MySQL?

Ein Speicherung der Daten in einer SQL-Datenbank ermöglicht eine einfache Verwaltung der Daten über ein Webfrontend das so ohne erweiterte Berechtigungen auskommt. MySQL wurde nur gewählt weil sich darauf von allen relevanten Programmen problemlos zugreifen lässt. Eine Nutzung von PostgreSQL oder einer anderen SQL-Datenbank wäre mit Sicherheit auch möglich.

Greylisting - Pro und Kontra

Zum Thema Greylisting gibt es viele Gegensätzliche Meinungen. Die einen preisen es als Wundermittel gegen Spam die anderen stellen seine Wirksamkeit in Frage und einige Nutzer können nicht mit den dadurch entstehenden Verzögerungen leben. Fakt ist, dass die Spam-Belastung meiner System nach dem Einsatz um über 90% zurückgegangen ist. Inzwischen ist zwar zu beobachten, dass wieder mehr Spam durch das Greylisting durchkommt, aber solange ich noch einen positiven Effekt feststellen kann werde ich Greylisting weiterhin einsetzen. Wen die Nachteile von Greylisting - verzögerte Zustellung und zusätzlicher Ressourcenbedarf - stören, der kann das dargestellte Setup ohne Probleme auch ohne Greylisting umsetzen.

Die Datenbankschmata

Für das Ablegen der Benutzerdaten und Domains gibt es drei relevante Datenbank Schemata. Zwei davon werden zum speichern der zuständigen Domains verwendet, eines für die Informationen über die Postfächer und die Weiterleitungen. Was die Datenbankschema angeht ist man im Grund sehr flexibel. Es wäre durchaus auch möglich getrennte Schemata für Postfächer und Weiterleitungen zu verwenden und die Domains in einer Datenbank zu speichern.

mail_accounts:

Feld Typ Beschreibung
id int(9) Primärschlüssel, auto_increment
local_part varchar(255) Der Benutzerteil der Adresse
domain int(16) Der Domainteil der Adresse, verweist auf die Tabelle Domains
forward varchar(255) Das Ziel der Weiterleitung
cc varchar(255) Alle Nachrichten an diese Adresse werden hierhin kopiert
name varchar(255) Ein Benutzerdefinierter Name
pwclear varchar(255) Das Passwort im Klartext
pwcrypt varchar(255) Das Passwort im UNIX Crypt Format
is_away enum('yes','no') Auto-Responder aktiv?
away_text enum('yes','no') Text für den Auto-Responder
spam_check enum('yes','no') SpamAssassin ausführen?
spam_purge enum('yes','no') Spam Mails direkt löschen?
virus_check enum('yes','no') Virus-Scan ausführen?
is_enabled enum('yes','no') Account aktiv?
created_at int(16) Zeitstempel der Erstellung
updated_at int(16) Zeitstempel der letzten Modifikation

domains:

Feld Typ Beschreibung
id int(16) Primärschlüssel, auto_increment
domain varchar(255) Domainname (FQDN)
is_enabled enum('yes','no') Aktiv?
mail_host int(16) Der zuständige Mailserver
created_at int(16) Zeitstempel der Erstellung
updated_at int(16) Zeitstempel der letzten Modifikation

hosts:

Feld Typ Beschreibung
id int(16) Primärschlüssel, auto_increment
name varchar(255) Hostname
fqdn varchar(255) Kompletter Name (FQDN)
created_at int(16) Zeitstempel der Erstellung
updated_at int(16) Zeitstempel der letzten Modifikation

Einrichtung der Umgebung

Vor der Einrichtung der benötigten Pakete sollten Sie zunächst sicherstellen, dass ihr System über einen korrekten Hostnamen verfügt. Dies lässt sich in den Dateien /etc/hostname und /etc/hosts festlegen. Wenn das Kommando hostname --fqdn den korrekten, voll-qualifizierten, Hostname ausgibt, dann ist diese Einstellung in Ordnung.

Falsch wäre folgender Eintrag:

192.168.0.1             server  server.doe.org

Der korrekte Eintrag:

192.168.0.1             server.doe.org  server

Speicherort erstellen

Legen Sie zunächst das Verzeichnis /srv/mail an und vergeben Sie korrekte Zugriffsrechte:

mkdir -p /srv/mail
chown mail:mail /srv/mail
chmod 700 /srv/mail

Installation der benötigten Pakete

Die meisten der benötigten Pakete lassen sich einfach über Debians Paketverwaltung nachinstallieren. Manuell müssen nur der Webmailer und die Verwaltungsoberfläche nachinstalliert werden. Da diese beiden Pakete in PHP5 geschrieben sind werden hierfür noch ein Webserver - am besten Apache 2 - sowie eine PHP5 Installation benötigt. Wichtig zu erwähnen ist in diesem Zusammenhang, dass der Webserver nicht unbedingt auf dem Mailserver System laufen muss. Es ist also möglich ein System auf zu setzen, das lediglich als Mailserver fungiert.

Zunächst wird nun Exim4 mit MySQL Unterstützung installiert:

$> aptitude install exim4-daemon-heavy

Wenn der Mailserver auch POP3 und IMAP anbieten soll benötigen wir noch Dovecot mit POP3 und IMAP Support.

$> aptitude install dovecot-imapd dovecot-pop3d

Es ist auch möglich nur eines der beiden Protokolle zu unterstützen. Wenn Sie z.B. nicht genug Ressourcen für einen IMAP-Server haben, dann lassen Sie dovecot-imapd einfach weg. Im folgenden gehe ich davon aus, dass Sie beiden Protokolle unterstützen wollen.

Wenn Sie den Datenbankserver auf dem gleichen System laufen lassen wollen installieren Sie noch das entsprechende Paket:

$> aptitude install mysql-server-5.0

Nach meiner Erfahrung spricht nichts dagegen. Wenn Sie allerdings schon über einen leistungsfähigen Datenbankserver in Ihrem Netzwerk verfügen kann es Sinn machen stattdessen diesen zu verwenden. Dann entfällt die Installation dieses Paketes natürlich.

Wenn Sie SpamAssassin einsetzten möchten um die hereinkommenden Mails zu klassifizieren, installieren Sie noch die folgenden Pakete nach:

$> aptitude install pyzor razor spamassassin spamc dcc-client

Hier sind die Pakete pyzor, razor und dcc-client optional, jedoch können dadurch die Erkennungsleistungen erheblich verbessert werden.

Wenn Sie Greylisting verwenden möchten benötigen Sie noch das Greylistd Paket:

$> aptitude install greylistd

Wenn Sie die PHP basierten Pakete installieren möchten, sollten Sie nun den Apache Webserver und PHP5 installieren.

$> aptitude install libapache2-mod-php5 php5-mysql phpmyadmin

Das Paket libapache2-mod-php5 zieht über seine Abhängigkeiten automatisch den Apache Webserver mit dem passenden (prefork) Worker nach. PhpMyAdmin ist komplett optional, kann die Einrichtung und Verwaltung der Datenbank aber deutlich erleichtern.

Für die Absicherung der Verbindungen des Mailserver wird noch das OpenSSL Paket benötigt um die entsprechenden Zertifikate zu erzeugen:

$> aptitude install openssl

Verbindung zur Datenbank

Jedem der beteiligten Programme, das Zugriff auf die Datenbank benötigt, müssen die entsprechenden Verbindungsinformationen mitgeteilt werden. Zunächst müssen jedoch die Datenbank und die Benutzer angelegt werden.

Nach der Installation der Datenbank gestattet es MySQL root zunächst sich ohne Passwort einzuloggen. Sie sollten das Ändern.

$> mysql -uroot

Danach sollten Sie eine MySQL Konsole erhalten, dort können Sie mit folgendem Befehl ein Passwort für root setzen.

mysql> SET PASSWORD = PASSWORD('ihrkompliziertesPasswort');

Denken Sie daran, das Passwort zu ändern. Verwenden Sie nicht das hier vorgeschlagene Passwort, sondern ein gutes und sicheres Passwort, das sie sich merken können.

Jetzt müssen zunächst die Datenbank und ein entsprechender Benutzer erstellt werden. Dies geht sehr einfach über PhpMyAdmin oder alternativ per Hand.

$> mysql -uroot -p

Sie werden jetzt nach dem eben erstellten root Passwort gefragt. In der MySQL Konsole legen Sie nun bitte die Datenbank und einen Benutzer an.

mysql> CREATE DATABASE maildb;
mysql> GRANT ALL PRIVILEGES ON maildb.* TO 'maildb_user'@'localhost' IDENTIFIED BY 'password';
mysql> FLUSH PRIVILEGES;

Zunächst wird Exim mit den Datenbankinformationen ausgestattet. Dazu die exim4.conf anpassen. Die Informationen stehen gleich zu Beginn der Datei.

# MySQL defines
MYSQL_SERVER=localhost
MYSQL_USER=DBUSER
MYSQL_PASSWORD=DBPASS
MYSQL_DB=DBNAME
MYSQL_EMAILTABLE=mail_accounts
MYSQL_DOMAINTABLE=domains
MYSQL_HOSTTABLE=hosts
# MySQL connection
hide mysql_servers = "MYSQL_SERVER/MYSQL_DB/MYSQL_USER/MYSQL_PASSWORD"

Danach muss auch Dovecot wissen wie er sich zur Datenbank verbinden kann. Diese Informationen werden in der dovecot-sql.conf eingetragen.

driver = mysql
connect = host=localhost dbname=DBNAME user=DBUSER password=DBPASS

Initialisierung der Datenbank

Nachdem die Datenbank und die Benutzer angelegt sind, müssen nun die Datenbanktabellen erstellt werden. Dazu reicht es aus einfach die gegebenen Schemata zu importieren.

Wie bereits weiter oben beschrieben, enthält die Tabelle mail_accounts alle Informationen über die Postfächer und Weiterleitungen:

mysql> CREATE TABLE IF NOT EXISTS `mail_accounts` (
  `id` int(9) NOT NULL auto_increment,
  `local_part` varchar(255) NOT NULL default '',
  `domain` int(16) NOT NULL,
  `forward` varchar(255) NOT NULL,
  `cc` varchar(255) default NULL,
  `name` varchar(255) NOT NULL default '',
  `pwclear` varchar(255) NOT NULL default '',
  `pwcrypt` varchar(255) NOT NULL default '',
  `is_away` enum('yes','no') NOT NULL default 'no',
  `away_text` text,
  `spam_check` enum('yes','no') NOT NULL default 'no',
  `spam_purge` enum('yes','no') NOT NULL default 'no',
  `virus_check` enum('yes','no') NOT NULL default 'no',
  `is_enabled` enum('yes','no') NOT NULL default 'yes',
  `created_at` int(16) NOT NULL default '0',
  `updated_at` int(16) NOT NULL default '0',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `UNIQUE_EMAIL` (`domain`,`local_part`)
) ENGINE=InnoDB ;
mysql> ALTER TABLE `mail_accounts`
  ADD CONSTRAINT `mail_accounts_ibfk_1` FOREIGN KEY (`domain`) REFERENCES `domains` (`id`) ON DELETE CASCADE;

Die Tabellen für die Domains sind fast identisch:

mysql> CREATE TABLE IF NOT EXISTS `domains` (
  `id` int(16) NOT NULL auto_increment,
  `domain` varchar(255) NOT NULL,
  `is_enabled` enum('yes','no') NOT NULL,
  `mail_host` int(16) NOT NULL,
  `created_at` int(16) NOT NULL,
  `updated_at` int(16) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `domain` (`domain`),
  KEY `mail_host` (`mail_host`)
) ENGINE=InnoDB;
mysql> ALTER TABLE `domains`
  ADD CONSTRAINT `domains_ibfk_3` FOREIGN KEY (`mail_host`) REFERENCES `hosts` (`id`) ON DELETE CASCADE;

In der Host Tabelle werden die Zuordnungen zu den Servern festgehalten.

mysql> CREATE TABLE IF NOT EXISTS `hosts` (
  `id` int(16) NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  `fqdn` varchar(255) NOT NULL,
  `notes` text NOT NULL,
  `created_at` int(16) NOT NULL,
  `updated_at` int(16) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

Die Foreign Key Constraints sorgen dafür, dass die Datenbank immer in einem konsistenten Zustand ist. Wird z.B. eine Domain gelöscht so sorgt die Datenbank dafür, dass auch alle abhängigen Mail Accounts entfernt werden. Oder wenn ein Host gelöscht wird, dann werden sowohl die abhängigen Domains als auch die davon abhängigen Accounts gelöscht. Daher Vorsicht beim löschen von Einträgen aus den übergeordneten Tabellen.

Erstellen der Datenbankabfragen

Die Datenbankabfragen für Exim werden als Makros definiert damit sie sich zentral ändern lassen und die restliche Konfigurationsdatei nicht unnötig aufgebläht wird. Ich werde hier kurz auf die einzelnen Abfragen eingehen. Natürlich können die SQL Abfragen beliebig angepasst werden wenn ein anderes Datenbankschema zugrunde gelegt wird. Lediglich die Rückgabewerte müssen unverändert erhalten bleiben. Die WHERE Bedingung die den Host abfragt ist durch eine geeignete Angabe zu ersetzten oder zu entfernen. Sie ist deshalb vorhanden, da das Setup für den Betrieb von mehreren unabhängigen Mailservern an einer Datenbank ausgelegt ist. D.h. es ist möglich mit einer Datenbank und einer Verwaltungsoberfläche mehrere Mailserver mit eigenen Domains und Postfächern zu betreiben. Auf dieses Feature wird in der aktuellen Version dieses Dokumentes nicht weiter eingegangen. In den unten genannten Abfragen ist der Platzhalter 'HOST' durch den Namen des aktuellen Hosts zu ersetzen. Dieser Name muss natürlich auch in der Hosts Tabelle existieren.

Diese Abfrage stellt fest ob der Benutzer als Abwesend markiert ist (Urlaubsbenachrichtigung).:

MYSQL_Q_ISAWAY=SELECT d.domain FROM MYSQL_EMAILTABLE AS ma, domains AS d, hosts AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='${quote_mysql:$local_part}' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST' AND ma.is_away='yes'

Die Abfrage AWAYTEXT liefert die dazu passende Abweseneheitsnachricht die an den Absender geschickt wird.:

MYSQL_Q_AWAYTEXT=SELECT ma.away_text FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='${quote_mysql:$local_part}' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST'

Dieses Abfrage liefert ein positives Ergebnis zurück wenn eine gegebene Adresse ein Weiterleitungsziel hat, also ein Alias ist.:

MYSQL_Q_FORWARD=SELECT ma.forward FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='${quote_mysql:$local_part}' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST' AND ma.forward != '' AND ma.is_enabled = 'yes'

Die Abfrage CC liefert die Adresse an die eine Kopie der aktuellen Nachricht gehen soll.:

MYSQL_Q_CC=SELECT cc FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='${quote_mysql:$local_part}' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST' AND ma.is_enabled = 'yes'

Die wohl am häufigsten aufgerufene Abfrage stellt fest ob es sich bei einer gegebenen Adresse um ein lokales Postfach handelt. Diese Abfrage wird bei jedern ankommenden Nachricht abgesetzt.:

MYSQL_Q_LOCAL=SELECT d.domain FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='${quote_mysql:$local_part}' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST' AND ma.is_enabled = 'yes' AND ma.forward = ''

Die gleiche Abfrage für Wildcards.:

MYSQL_Q_WCLOCAL=SELECT d.domain FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='*' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST' AND ma.is_enabled = 'yes' AND ma.forward = ''

Und die Wildcard Abfrage nochmal für Weiterleitungen:

MYSQL_Q_WCLOCFW=SELECT d.domain FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='*' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST' AND ma.is_enabled = 'yes' AND ma.forward != ''

Deaktivierte Accounts werde gleich zu Beginn des Routing Prozesses abgelehnt und daher mit einer eigenen Abfrage ermittelt. Der Absender erhällt daraufhin einen entsprechenden Hinweis.:

MYSQL_Q_DISABLED=SELECT d.domain FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='${quote_mysql:$local_part}' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST' AND ma.is_enabled = 'no'

Mit der LDOMAIN Abfrage stellt der Mailserver fest für welche Domains er zuständig ist, d.h. welche Domains er lokal zustellt.:

MYSQL_Q_LDOMAIN=SELECT d.domain FROM MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE d.mail_host = h.id AND h.name = 'HOST' AND d.domain='${quote_mysql:$domain}'

Für alle anderen Domains in der Datenbank werden Nachrichten zwar engegengenommen aber nicht lokal zugestellt.:

MYSQL_Q_RDOMAIN=SELECT d.domain FROM MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE d.mail_host = h.id AND h.name != 'HOST' AND d.domain='${quote_mysql:$domain}'

Mit der Abfrage MYSQL_Q_BOXPATH wird der Pfad des Postfaches ermittelt. Diese Abfrage ist nur notwendig wenn auch Dovecots deliver verzichtet wird.:

MYSQL_Q_BOXPATH=SELECT CONCAT(d.domain,'/',ma.local_part) AS boxpath FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='${quote_mysql:$local_part}' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST'

Da möglicherweise nicht alle Adressen auf Spam überprüft werden sollen, z.B. wenn der Kunde dies nicht wünscht oder dieses Dienstleistung nicht in seinem Tarif enthalten ist, wird dies ebenfalls abgefragt:

MYSQL_Q_SPAMC=SELECT d.domain FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='${quote_mysql:$local_part}' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST' AND ma.spam_check='yes'

Die Option als Spam erkannte Mails direkt zu löschen ist sehr gefährlich, aber zumindest im Datenbankschema vorgesehen und daher mit einer eigenen Abfrage erreichbar. Ob man diese Optionen seinen Nutzern anbieten möchte ist allerdings fraglich.:

MYSQL_Q_SPAMPURGE=SELECT d.domain FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='${quote_mysql:$local_part}' AND d.domain='${quote_mysql:$domain}' AND h.name='HOST' AND ma.spam_purge='yes'

Die nächsten drei Abfragen dienen zur Authentisierung der Nutzer für SMTP-AUTH:

MYSQL_Q_AUTHPLAIN=SELECT if(count(ma.id), "1", "0") FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND CONCAT(ma.local_part,'@',d.domain)='${quote_mysql:$2}' AND h.name='HOST' AND ma.pwclear='${quote_mysql:$3}'
MYSQL_Q_AUTHLOGIN=SELECT if(count(ma.id), "1", "0") FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND CONCAT(ma.local_part,'@',d.domain)='${quote_mysql:$1}' AND h.name='HOST' AND ma.pwclear='${quote_mysql:$2}'
MYSQL_Q_AUTHCRAM=SELECT ma.pwclear FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d, MYSQL_HOSTTABLE AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND h.name='HOST' AND CONCAT(ma.local_part,'@',d.domain)='$1'

Damit ist die definition der MySQL-Abfragen für Exim abgeschlossen. Weiter unten wird der konkrete Einsatz der einzelnen Abfragen besprochen. Hier folgen nun noch die zwei relevanten Abfragen für die POP3- und IMAP-Server Dovecot. Er benötigt zum einen die Möglichkeit das Passwort des Benutzers abzufragen und zum anderen eine Möglichkeit den Aufbewahrungsort der Maildirs zur ermitteln. Da für Dovecot das prefetch Feature verwendet wird, siehe unten, gibt die Passwort Abfrage auch gleich den Speicherort des Maildirs und weitere Informationen mit zurück. Damit wäre eine separate User Query eigentlich überflüssig, bei Verwendung von Dovecots LDA deliver ist dies jedoch notwendig. Die beiden Abfragen sind identisch und liefern ein Ergebnis der folgenden Form zurück.

User Password Home UID GID
user@domain.tld <pass> /srv/mail/domain.tld/user 8 8

Die eigentlichen Abfragen unterscheiden sich lediglich in der Bezeichnung der Felder:

password_query = password_query = SELECT concat(ma.local_part, '@', d.domain) AS user, pwclear AS password, concat('/srv/mail/', d.domain, '/', ma.local_part) AS userdb_home, '8' AS userdb_uid, '8' AS userdb_gid FROM mail_accounts AS ma, domains AS d, hosts AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='%n' AND d.domain='%d' AND h.name='HOST'
user_query = SELECT concat(ma.local_part, '@', d.domain) AS user, pwclear AS password, concat('/srv/mail/', d.domain, '/', ma.local_part) AS home, '8' AS uid, '8' AS gid FROM mail_accounts AS ma, domains AS d, hosts AS h WHERE ma.domain = d.id AND d.mail_host = h.id AND ma.local_part='%n' AND d.domain='%d' AND h.name='HOST'

Den Platzhalter HOST muss man durch den Hostnamen ersetzen wie er in der Tabelle hosts eingetragen ist oder den Teil AND h.name='HOST' entfernen wenn man nur einen Server benutzt und keine Verwendung für die Tabelle hosts hat.

SSL: Schlüsselpaar und Zertifikate erstellen

Um die Übertragung zwischen dem Mailserver und den Benutzern auf der einen Seite sowie anderen Mailservern auf der anderen Seite abzusichern ist es sinnvoll verschlüsselte Verbindungen mittels Secure-Socket-Layer, kurz SSL, zu verwenden. Die notwendigen Funktionalität bringen die verwendeten Programme schon mit, nur die entsprechenden Schlüssel und Zertifikate müssen noch erstellt werden. Für Details zum Thema PKI sei auf Wikipedia und RFC3280 verwiesen.

Für die Erzeugung der benötigten Schlüsselpaare wird openssl benötigt. Dies sollten Sie im Schritt Installation der benötigten Pakete bereits installiert haben.

Zunächst wird ein privater Schüssel unter dem Namen private.key angelegt. Hier können Sie natürlich auch einen anderen Namen wählen. Als Schlüssellänge empfiehlt sich 1024 oder 2048.:

openssl genrsa -out private.key 2048

Danach kann daraus der Certificate Signing Request (CSR) erzeugt werden:

openssl req -new -key private.key -out server.domain.csr

Hierbei wird OpenSSL die X.509 Informationen zu Ihrer Person abfragen.

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:DE
State or Province Name (full name) [Some-State]:Hesse
Locality Name (eg, city) []:Frankfurt
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Internet Widgits Pty Ltd
Organizational Unit Name (eg, section) []:CA
Common Name (eg, YOUR name) []:server.domain.tld
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Diese Angaben müssen - je nach Policy Ihrer CA - der Wahrheit entsprechen und ggf. eindeutig nachprüfbar sein. Wichtig ist hier insbesondere der Punkt Common Name. Hier müssen Sie den Hostname (FQDN) Ihres Mailserver eintragen. Wichtig ist auch, dass dieser Hostname sowohl über DNS als auch über Reverse DNS auflösbar ist. Auf die Angabe eines Passwortes sollte man hier verzichten. Dies ist zwar ein möglicher Angriffsweg, aber wenn Sie hier ein Passwort angeben müssen Sie dieses entweder in die Konfigurationsdateien eintragen, was auch keine zusätzliche Sicherheit bietet, oder es bei jedem Neustart der Dienste von Hand eingeben. Es ist Ihre Entscheidung.

Dieser CSR kann jetzt dazu verwendet werden um ein richtiges Zertifikat bei einer Certification Authority (CA) zu bestellen. Je nach Einsatzzweck des Servers können Sie entweder ein selbstsigniertes Zertifikat, ein kostenloses Zertifikat von CACert oder StartSSL oder ein kostenpflichtiges Zertifikat von VeriSign, Thawte, etc. erstehen.

Wenn Sie ein selbstsigniertes Zertifikat verwenden wollen, können Sie sich an folgendem Tutorial orientieren.

Nachdem Sie das Zertifikat erhalten haben legen Sie es unter /etc/ssl/certs/server.domain.tld ab. Den dazugehörigen privaten Schlüssel speichern Sie unter /etc/ssl/private/server.key. Notieren Sie sich diese Pfade, sie werden später benötigt.

Eventuell ist es notwendig, dass Sie die Zugriffsrechte für /etc/ssl/private/ anpassen. Entweder erlauben Sie mit:

chmod o+x /etc/ssl/private

beliebigen Benutzern in dieses Verzeichnis zu wechseln oder Sie speichern den private Key im Exim Konfigurationsverzeichnis unter /etc/exim4.

Exim konfigurieren

Als Basis für Ihre Exim4 Konfiguration verwenden Sie am besten das bereitgestellte Template. Alle Stellen die von Ihnen geändert werden müssen sind mit einem CHANGEME markiert. Hier folgt eine Schritt-für-Schritt Besprechung der relevanten Optionen.

Grundeinstellungen

Zunächst müssen Sie bei primary_hostname den Name angeben unter dem ihr Mailserver arbeitet. Dieser Name sollte konsistent per DNS und Reverse-DNS auflösbar sein. D.h. eine DNS Abfrage auf den primary Hostname sollte die IP-Adresse ergeben unter der der Mailserver arbeitet und eine Reverse-DNS Abfrage auf diese IP-Adresse wieder den FQDN.:

primary_hostname = server.localdomain.tld

In der Domainlist local_domains definieren wir alle Domains für die Exim lokal zuständig ist, d.h. alle Domains deren Postfächer von der aktuelle Exim Instanz zugestellt werden. Hier kommt die erste MySQL Abfrage zu Einsatz. Zur Laufzeit wird Exim die Abfrage ausführen und dann die Liste der Domains erhalten.:

domainlist      local_domains = localhost:mysql;MYSQL_Q_LDOMAIN

Für die Domains die nicht lokal zugestellt werden existiert die Domainslist relay_to_domains. Diese Domains werden vom lokalen Exim engegen genommen und dann versucht weiterzuleiten.:

domainlist      relay_to_domains = mysql;MYSQL_Q_RDOMAIN

Über qualify_domain wird angegeben welche Domain an Adressen gehängt wird die lediglich aus einem Benutzernamen bestehen. Hier sollte die Hauptdomain eingetragen werden.:

qualify_domain = server.tld

Sehr wichtig ist die Option local_interfaces wird festgelegt auf welchen Netzwerkschnittstellen der Mailserver verfügbar sein soll. Wichtig ist hier zum einen die Loopback Schnittstelle und zum anderen die öffentliche IP-Adresse des Mailservers anzugeben. Ausserdem gibt es hier die Möglichkeit eine IPv6 Adresse anzugeben.:

local_interfaces = <; 127.0.0.1 ; 192.168.0.1 ; \
                2001:0DB8::1

Der Standardport für SMTP ist 25, daher muss der Mailserver hier auf jeden Fall lauschen wenn er von anderen Systemen Nachrichten empfangen will. Port 587 ist ein neuerer Port, definiert vom Message Submission Protokoll das eine Unterscheidung zwischen dem Nachrichtenaustausch zwischen Mailservern und einliefernden Clients bieten will. Auf Port 587 sollen die Clients nur authentifizierte Übertragungen von Clients über SMTP annehmen.:

daemon_smtp_port = 25 : 587

ACL Konfiguration

In der ACL Konfiguration kann festgelegt werden wie sich Exim während der einzelnen Phasen des SMTP Protokolls verhällt. Hier lassen sich diverse Checks und z.B. Greylisting einbinden.

Mit dem Check acl_check_from kann nach dem FROM: Kommando sichergestellt werden, dass auf Port 587 gemäß SMA nur authentifizierte Verbindungen akzeptiert werden:

acl_check_from:
  # drop connections on the SMA Port that did not auth
  drop condition = ${if={$interface_port}{587} {1}{0}}
  !authenticated = *
  # accept everything else (policy checks are in rcpt acl)
  accept

Die weiteren Überprüfungen finden erst nach Bekanntgabe des Empfängers, also nach dem RCPT TO: Kommando statt. Hier werden unter anderem der Syntax der Adressen, RBLs und das Greylisting überprüft.

Router

Nach dem ACL Abschnitt folgt die Konfiguration der Router. Hierbei ist wichtig zu beachten, dass die Reihenfolge in der die Router definiert werden relevant ist. Jede Nachricht die die Eingangsüberprüfungen überstanden hat wird der Reihe nach jedem Router vorgelegt bis einer die Nachricht akzeptiert. Der fail_router, der alle Nachrichten abweist die in der Datenbank als inaktiv vermerkt sind, darf erst nach dem dnslookup Router stehen, da es sonst Probleme mit deaktivierten Accounts geben kann.

Der dnslookup Router verarbeitet alle Nachrichten die nicht lokal zugestellt werden, daher auch domains = ! +local_domains das dafür sorgt, dass dieser Router nicht für lokale Domains angewandt wird.

Der blacklist_router sorgt dafür, dass der Header X-Spam-Flag: YES hinzugefügt wird wenn der Absender auf der schwarzen Liste steht. Der spamcheck_director übergibt die Nachricht an SpamAssassin und setzt dann die Verarbeitung fort. Der spampurge_director sorgt, mit der weiter oben besprochene SQL-Abfrage, dafür, dass Spam-Mails ggf. direkt entsorgt werden. Der vacation_director erzeugt über den Transport vacation_autoreply eine Abwesenheitsnachricht wenn dies in der Benutzerdatenbank aktiviert ist. Die Direktoren virtual_cc_director und virtual_forward_director sorgen dafür, dass die Nachrichten dupliziert bzw. weitergeleitet werden. Interessant ist hier vor allem der virtual_local_mailbox Direktor der alle Nachrichten an lokale Adressen, die bis hierhin nicht von einem anderen Router behandelt wurden, an den Dovecot Transport übergibt damit dieser mit Hilfe von Dovecots deliver die Nachricht in das Maildir des Benutzers zustellt.

virtual_local_mailbox:
  driver = accept
  domains = ${lookup mysql {MYSQL_Q_LOCAL}{$value}}
  transport = virtual_local_dovecot_delivery

Transports

Nach den Routern folgt noch eine kurze Besprechung der relevanten Tranports. Der remote_smtp Transport sorgt für die Auslieferung von Nachrichten an andere Hosts per SMTP. Mit dem Parameter interface = <ip> wird er angewiesen für ausgehende Verbindungen die korrekte Adresse zu verwenden. Hier ist die IP-Adresse des Mailservers einzutragen. Wichtig ist diese Option nur wenn auf dem Mailserver mehrere IP-Adressen eingerichtet sind. Etwas mehr Aufmerksamkeit erfordert der spamcheck Transport der dafür sorgt, dass die entsprechenden Nachrichten an SpamAssassin übergeben werden. Dazu wird der Driver pipe benutzt der die Ausgabe von transport_filter an command - in diesem Fall wieder exim selbst - übergibt. Über die Option spam-scanned wird das entsprechende Protokoll gesetzt damit Exim diese Nachricht als gescannt anerkennt.

spamcheck:
  driver = pipe
  command = /usr/sbin/exim4 -oMr spam-scanned -bS
  use_bsmtp = true
  transport_filter = "/usr/bin/spamc -u $local_part@$domain"
  home_directory = "/tmp"
  current_directory = "/tmp"
  user = mail
  group = mail
  log_output = true
  return_fail_output = true
  return_path_add = false
  message_prefix =
  message_suffix =

Der zweite interessante Transport ist der virtual_local_dovecot_delivery. Dieser Transport ist dafür zuständig, dass die Nachrichten über den Dovecot LDA im Postfach des Benutzers landen. Hier wird die Nachricht an deliver übergeben der daraufhin die Benutzerinformationen aus der Datenbank liest - siehe Dovecot Konfiguration - ggf. Sieve Skripte ausführt und daraufhin die Nachricht zustellt. Gleichzeitig sorgt deliver dafür, dass die internen Datenstrukturen von Dovecot, zustätzliche Indices, aktuelle gehalten werden. Ein weiterer Vorteil gegenüber der internen exim Funktionalität.

virtual_local_dovecot_delivery:
  driver = pipe
  command = /usr/lib/dovecot/deliver -d $local_part@$domain
  message_prefix = ""
  message_suffix = ""
  delivery_date_add
  envelope_to_add
  return_path_add
  log_output
  user = mail
  group = mail

Retry

Die Retry Konfiguration wird hier aus den exim Vorgaben übernommen und wird nicht weiter besprochen. Bei der Rewrite Konfiguration ist es möglich weitere Angaben zu machen.

Authentifikation

Der letzte interessante Abschnitt in der exim Konfiguration ist der Abschnitt zur Authentifikation. Hier werden die möglichen Methoden für SMTP-AUTH definiert. Vorgegeben sind PLAIN, LOGIN und CRAM-MD5.

Nachrichten mit dem Dovecot Delivery Agent (LDA) zustellen

Exim4 ist in der Lage die Nachrichten für die lokalen Benutzer direkt zuzustellen, da es das Maildir Format unterstüzt. Es fungiert also per Voreinstellung sowohl als MTA (Mail-Transfer-Agent) als auch als LDA (Local Delivery Agent). Dovecot bringt jedoch einen eigenen LDA mit, der einige Vorteile gegenüber der eingebauten Funktionalität von exim bietet. Der wichtigste Unterschied ist wohl die Unterstützung der Sieve Filtersprache, die eine Server-seitige Filterung der Nachrichten ermöglicht. Insbesondere Ihre IMAP-Nutzer werden diese Funktionalität zu schätzen wissen.

##
## LDA specific settings
##
protocol lda {
  # Address to use when sending rejection mails.
  postmaster_address = postmaster@server.tld
  # Hostname to use in various parts of sent mails, eg. in Message-Id.
  # Default is the system's real hostname.
  #hostname =
  # Support for dynamically loadable plugins. mail_plugins is a space separated
  # list of plugins to load.
  #mail_plugins =
  mail_plugin_dir = /usr/lib/dovecot/modules/lda
  # Binary to use for sending mails.
  sendmail_path = /usr/lib/sendmail
  # UNIX socket path to master authentication server to find users.
  auth_socket_path = /var/run/dovecot/auth-master
  # Enabling Sieve plugin for server-side mail filtering
  mail_plugins = cmusieve
  # Logging
  #log_path = /var/log/dovecot-deliver.log
  #info_log_path = /var/log/dovecot-deliver.log
}

Exim unter hoher Last

Obwohl das vorgestellte Exim Setup durchaus Situationen mit mehr als 6500 Mails pro Stunde verkraften kann gibt es einige Optionen die die Belastung des Systems in Umgebungen mit sehr hoher Last etwas reduzieren können:

queue_only
split_spool_directory
queue_run_max = 1
remote_max_parallel = 1

Diese Anweisungen sorgen dafür, dass alle eingelieferten Nachrichten zunächst in der Queue platziert werden, maximal eine Instanz diese Queue abarbeitet und das Spool Verzeichnis mit weiteren Unterverzeichnissen aufgeteilt wird. Weitere Informationen finden sich im Exim FAQ.

Eine Testmail versenden

Nachdem Sie den Mailserver konfiguriert haben, sollten Sie nun testen ob er auch funktioniert. Dazu kann man händisch mit Hilfe von Telnet eine SMTP Sitzung simulieren.

$> telnet localhost 25

Darauf hin sollte sich der Server bei Ihnen vorstellen:

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 server.domain.tld ESMTP Exim 4.63

Jetzt ist Exim bereit SMTP Befehle engegen zu nehmen. Zunächst müssen wir uns mittels EHLO vorstellen.

EHLO localhost.localdomain

Danach gibt Exim preis welche Funktionen er anbietet:

250-server.domain.tld Hello User at localhost [127.0.0.1]
250-SIZE 10240000
250-PIPELINING
250-AUTH PLAIN LOGIN CRAM-MD5
250-STARTTLS
250 HELP

Den Absender angeben:

MAIL FROM: <jane@domain.tld>

Ein positiver Statuscode (2xx)? Gut. Dann weiter.

250 OK

Den Empfänger angeben.

RCPT TO: <john@domain.tld>
250 Accepted

Wieder ein positivert Statuscode? Gut. Haben Sie die Verzögerung zwischen dem Drücken der Enter Taste und der Rückmeldung gemerkt? In dieser Zeit arbeitet Exim die acl_check_rcpt ACL Regel ab. Hier wird unter anderem der Greylisting Daemon - wenn konfiguriert - und die SQL Datenbank abgefragt. Das dauert einen Moment. Mit DATA leiten wir den Datenteil ein. Ab hier folgt dann unsere Nachricht.

DATA
354 Enter message, ending with "." on a line by itself

Der Text der Mail.

Hallo John Doe,
ich wollte mich nur mal bei dir melden.
.
250 OK id=1K4a1j-0004GI-FP

So, das wars. Wieder ein positiver Statuscode bedeutet, dass Exim die Nachricht angenommen hat. In der Datei /var/log/exim4/mainlog kann man überprüfen ob die Nachricht korrekt zugestellt wurde.

2008-06-06 13:22:56 1K4a1j-0004GI-FP <= jane@domain.tld H=localhost (localhost.localdomain) [127.0.0.1] U=User P=esmtp S=300
2008-06-06 13:22:59 1K4a24-0004R9-KZ <= jane@domain.tld U=mail P=spam-scanned S=861
2008-06-06 13:22:59 1K4a24-0004R9-KZ => john <john@domain.tld> R=virtual_local_mailbox T=virtual_local_dovecot_delivery
2008-06-06 13:22:59 1K4a24-0004R9-KZ Completed
2008-06-06 13:22:59 1K4a1j-0004GI-FP => john@domain.tld R=spamcheck_director T=spamcheck H=localhost
2008-06-06 13:22:59 1K4a1j-0004GI-FP Completed

Probleme? exim -bh!

Sollte es bei der Einlieferung von Nachrichten zu Problemen kommen kann man exim bzw. exim4 mit dem Parameter -bh <ip> aufrufen um eine SMTP Sitzung von der Adresse <ip> zu simulieren. Hier muss die SMTP Sitzung wie gerade beschrieben händisch durchgeführt werden, allerdings wird die so eingelieferte Nachricht nicht zugestellt. Stattdessen versorgt Exim den Anwender so mit sehr ausführlichen Informationen was er gerade macht.

exim4 -bh 127.0.0.1

Hinweis

Es macht natürlich keinen Sinn die Einlieferung von localhost (127.0.0.1) zu testen, da exim so konfiguriert wurde Mails von diesem Host immer anzunehmen. Verwenden Sie stattdessen z.B. ihre aktuelle IP-Adresse.

Dovecot konfigurieren

Die Dovecot Konfiguration ist zwar vom Autor aus sehr ausführlich und vorbildlich kommentiert, aber die Menge an Informationen kann verwirrend wirken. Daher gehe ich hier auf die wichtigen Abschnitte ein. Der Abschnitt protocols sollte um die POP3 Protokolle ergänzt werden.:

protocols = imap imaps pop3 pop3s

In der Listen Anweisung wird festegelegt auf welcher Interface der Server lauschen soll. Im Abschnitt Logging sollte man unter log_path einen Pfad zur Logdatei angeben, da alles Ausgaben sonst im syslog landen, was schnell recht unübersichtlich werden kann.

log_path = /var/log/dovecot.log

Auf diese Datei sollten Sie ein chown dovecot:dovecot /var/log/dovecot.log sowie ein chmod 770 /var/log/dovecot.log ausführen, damit Dovecots Unterprozesse problemlos in die Logdatei schreiben können.

Wenn Sie sich für ein Setup mit SSL Zertifikaten entschieden haben, dann aktivieren Sie die entsprechende Option und geben Dovecot über ssl_cert_file und ssl_key_file den Pfad zu Ihrem Schlüsselpaar an.

ssl_disable = no
ssl_cert_file = /etc/ssl/certs/server.domain.tld.crt
ssl_key_file = /etc/ssl/private/server.domain.tld.key

Die anderen SSL Optionen können Sie einfach so übernehmen. Als nächstes sollten Sie die Option login_greeting anpassen.

login_greeting = server.tld Mailserver (powered by Dovecot) ready.

Hier tragen Sie am besten den Hostnamen Ihres Servers ein. Mit der Option mail_location teilen Sie dovecot mit wo Ihre Postfächer liegen. Wenn Sie der hier beschriebenen Verzeichnisstruktur gefolgt sind, dann lautet diese Zeile wie folgt:

mail_location = maildir:/srv/mail/%d/%n/Maildir

Im einzelnen bedeutet dies, dass Sie maildir verwenden und die entsprechenden Verzeichnisse unter dem Pfad /srv/mail/<domain>/<local_part>/Maildir liegen. Die Variable %d wird von dovecot durch die Domain ersetzt, die Variable %n durch den lokalen Teil der Adresse. Die nun folgenden Einstellungen zum Namespace können Sie einfach übernehmen, genau wie die weitern Optionen bis zu first_valid_uid. Dies muss angepasst werden. Da in dem vorgestellten Setup keine echten Benutzer für den Mailempfang angelegt werden, befinden sich alle Postfächer in Besitz von mail. Dieser Benutzer hat normalerweise die Used-ID 8. Daher müssen Sie den Bereich der gültigen UIDs wie folgt anpassen:

first_valid_uid = 8
last_valid_uid = 8

Die weiteren Einstellungen zu IMAP und POP3 können Sie einfach so übernehmen. Die Optionen im Abschnitt protocol lda sollten Sie wie in Nachrichten mit dem Dovecot Delivery Agent (LDA) zustellen beschrieben anpassen.

Der nächste interessante Abschnitt ist auth default. Hier deaktivieren Sie den Abschnitt passdb passwd-file sowie alle weiteren bis aus passdb sql. Die einzige Option für diesen Abschnitt ist der Pfad zur SQL Konfiguration. Deren Konfiguration wird später betrachtet.

passdb sql {
  args = /etc/dovecot/dovecot-sql.conf
}

Die Abschnitte zu userdb müssen ebenfalls alle auskommentiert werden, bis aus die folgenden:

userdb passwd {
}
userdb sql {
 args = /etc/dovecot/dovecot-sql.conf
}

Damit Dovecots LDA deliver arbeiten kann muss noch der entsprechende Socket aktiviert werden:

socket listen {
  master {
    path = /var/run/dovecot/auth-master
    mode = 0600
    user = mail # User running Dovecot LDA
    group = mail # Or alternatively mode 0660 + LDA user in this group
  }
  client {
    path = /var/run/dovecot/auth-client
    mode = 0600
    user = mail
    group = mail
  }
}

Nach der Hauptkonfiguration in dovecot.conf fehlt noch die Konfiguration der SQL Abfragen in dovecot-sql.conf. Diese wurde bereits im Abschnitt Verbindung zur Datenbank besprochen.

Damit ist die Konfiguration von Dovecot abgeschlossen.

Eine Testmail über POP3 abrufen

Das POP3 Protokoll ist recht simpel, so dass Sie den Empfang von Nachrichten über POP3 problemlos per Hand testen können.

$> telnet localhost 110

Nachdem Sie sich verbunden haben werden Sie von dovecot begrüßt.

Trying 192.168.0.1...
Connected to server.domain.tld.
Escape character is '^]'.
+OK server.domain.tld Mailserver (powered by Dovecot) ready.

Der Server ist jetzt Bereit. Jetzt kann man sich einloggen.

user john@domain.tld

Der Server sollte den Benutzernamen akzeptieren.

+OK

Dann muss noch das Passwort angegeben werden.

pass pass

Wenn der Server das Passwort akzeptiert sind Sie eingeloggt.

+OK Logged in.

Jetzt kann man sich eine Liste der Nachrichten im Postfach anzeigen lassen.

list

Dovecot teilt nun mit wieviele Nachrichten vorhanden sind.

+OK 1 messages:
1 2958
.

Mit retr kann man die Nachrichten abrufen. Holen Sie die erste Nachricht.

retr 1

Jetzt liefert Ihnen dovecot die Nachricht.

+OK 2958 octets
[Hier folgt die Nachricht]

Nach erledigter Arbeit können Sie sich ausloggen.

quit

Was von Dovecot nochmal bestätigt wird.

+OK Logging out.
Connection closed by foreign host.

Eine Testmail über IMAP abrufen

Das das IMAP Protokoll etwas komplizierter als SMTP oder POP3 ist empfiehlt es sich hierfür mutt oder einen anderen Client zu benutzen.

mutt -f ./Maildir

Aber Sie können es natürlich auch gerne von Hand versuchen.

$> telnet localhost 143
Trying 127.0.0.1...
Connected to server.domain.tld.
Escape character is '^]'.
* OK [CAPABILITY IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS STARTTLS        AUTH=PLAIN] server.domain.tld Mailserver (powered by Dovecot) ready.

Gut, der Server anwortet. Jetzt können wir uns anmelden.

1 login john@domain.tld password

Wenn Benutzername und Passwort stimmen, sollte der Server ein OK liefern.

1 OK Logged in.

Jetzt lassen Sie sich die vorhandenen Nachrichten anzeigen.

2 list "" "*"
* LIST (\HasNoChildren) "." "INBOX"
2 OK List completed.

Wählen Sie jetzt den Posteingang (INBOX) aus:

3 select "INBOX"

Daraufhin erhalten Sie automatisch einige Informationen über diesen Ordner:

* FLAGS (\Answered \Flagged \Deleted \Seen \Draft Junk NonJunk  $FORWARDED $TODO $WATCHED $IGNORED)
* OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft Junk NonJunk  $FORWARDED $TODO $WATCHED $IGNORED \*)] Flags permitted.
* 1 EXISTS
* 0 RECENT
* OK [UIDVALIDITY 1158586288] UIDs valid
* OK [UIDNEXT 136835] Predicted next UID
3 OK [READ-WRITE] Select completed.

Anhand der dritten Zeile (* 1 EXISTS) können Sie sehen, dass eine Nachricht vorliegt. Rufen Sie diese nun ab.

4 fetch 1 all

Sie erhalten nun zunächst nur ein paar Informationen über die Nachricht, nicht jedoch den Nachrichtentext.

* 1 FETCH (FLAGS (\Seen) INTERNALDATE "17-Apr-2008 09:23:44 +0200" RFC822.SIZE 2958 ENVELOPE ("Thu, 17 Apr 2008 00:02:40 -0400" "Subject" (("Hello" NIL "Jeo" "jeo@ceo.org")) (("Hello" NIL "Jeo" "jeo@ceo.org")) (("Hello" NIL "Jeo" "jeo@ceo.org")) ((NIL NIL "ceo" "ceo.org")) NIL NIL NIL "<CEO.20080417.564403.001.1208404960@ceo.org>"))
4 OK Fetch completed.

Den Text müssen Sie explizit anfordern:

5 fetch 1 body[]

Jetzt liefert Dovecot Ihnen den kompleten Inhalt zurück:

* 1 FETCH (BODY[] {486}
[Nachrichtentext]
5 OK Fetch completed.

Sie können sich jetzt abmelden.

6 logout

Dovecot verabschieded sich noch von Ihnen und schließt dann die Verbindung.

* BYE Logging out
6 OK Logout completed.
Connection closed by foreign host.

SpamAssassin gegen Spam

Die ständige Belästigung gehört heute mit zu den größten Problemen bei der Nutzung des Mediums Email. Zum Glück gibt es mit SpamAssassin einen ziemlich wirkungsvollen Klassifizierer mit dessen Hilfe Sie die meisten Spam Nachrichten korrekt aussortieren können.

Wenn Sie die bis hierhin aufgeführten Schritte befolgt haben, sollten SpamAssassin bereits installiert und eingerichtet sein. Was jetzt noch fehlt ist ein wenig Feintuning. Zunächst einmal müssen Sie in der Datei /etc/default/spamassassin dafür sorgen, dass der SpamAssassin Daemon gestartet wird. Dazu ändern Sie die Zeile:

ENABLED=0

in:

ENABLED=1

Danach muss noch die OPTIONS Zeile angepasst werden. Ändern Sie die Zeile folgendermaßen ab:

OPTIONS="--max-children 8 --helper-home-dir -u mail -x -q -s local5"

Jetzt öffnen Sie bitte die Datei /etc/spamassassin/local.cf und überprüfen Sie ob die folgenden Optionen mit den gegebenen Werten vorhanden sind. Ergänzen oder ändern Sie die Angaben ggf.

report_safe 1
use_bayes 1
bayes_auto_learn 1
bayes_auto_expire 0
bayes_ignore_header X-Bogosity
bayes_ignore_header X-Spam-Flag
bayes_ignore_header X-Spam-Status
bayes_ignore_header Return-Path
bayes_ignore_header Received
bayes_ignore_header X-Spam-Level
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-Sieve
bayes_ignore_header To
bayes_ignore_header X-WEBDE-FORWARD
bayes_path /var/mail/bayesdb
skip_rbl_checks 0
use_razor2 1
use_dcc 1
use_pyzor 1
dns_available yes
rbl_timeout 4
ok_locales all
score DCC_CHECK 4.000
score SPF_FAIL 3.000
score SPF_HELO_FAIL 3.000
score DKIM_VERIFIED -1.3
score DKIM_POLICY_TESTING 0
score DK_VERIFIED -1.1
score USER_IN_DKIM_WHITELIST -4.0
score USER_IN_DK_WHITELIST -3.9
score RAZOR2_CHECK 2.500
score BAYES_99 5.000
score BAYES_90 4.500
score BAYES_80 3.500

Danach ist die Konfiguration vom SpamAssassin abgeschlossen. Laden Sie den SpamAssassin Daemon neu um die Einstellungen zu übernehmen.

$> /etc/init.d/spamassassin reload

Nützliche Extras

Die grundlegende Einrichtung des Mailserver ist jetzt abgeschlossen, aber es gibt noch einige lohnenswerte Extras die Sie bei Interesse einbauen können.

Greylistd

Greylisting ist ein Verfahren um Spam Mails vor der Einlieferung abzuwehren. Es macht sich die Tatsache zu Nutze, dass die meisten Spamschleudern das SMTP Protokoll nicht korrekt implementieren. Die meisten Spam Versender versuchen einfach die Mails so schnell wie möglich zu verschicken. Das SMTP Protokoll sieht jedoch auch vor, dass ein Server dem Absender einen temporären Fehler liefert und der Absender es nach einer gewissen Zeitspanne erneut versuchen soll. Die meisten Spam-MTAs geben hier sofort auf und versuchen nicht erneut die Nachricht zuzustellen. Die regulären MTAs implementieren das Protokoll jedoch meistens korrekt und versuchen nach einigen Minuten erneut die Nachricht zuzustellen. Der Greylistd merkt sich alle Kombinationen aus Zieladresse und Absender IP. Beim ersten Versuch wird der MTA einen temporären Fehler liefern, nach einer gewissen Wartezeit wird die Nachricht dann akzeptiert. Dies gilt auch für alle weiteren Nachrichten von dem gleichen Absender in einem gewissen Zeitraum. Die Voreinstellung ist 60 Tage, expire in der Konfiguration. Die Wartezeit bevor eine Nachricht nach dem ersten Versuch angenommen wird ist 10 Minuten voreingestellt und kann über retryMin angepasst werden. Ungenutzte Kombinationen werden nach 8 Stunden verworfen retryMax. Über die Datei whitelist-hosts lassen sich Mailserver angeben die niemals durch Greylisting blockiert werden sollen. Hier kann man z.B. wichtige Kunden oder als problematisch bekannte Absender eintragen.

Spam und Ham (nicht-Spam) lernen

Der Spam-Klassifizierer SpamAssassin kombiniert verschiedene Techniken um Spam Mails zu erkennen. Dazu zählen klassische Textmuster-Filter, echtzeit Sperrlisten (RBL) und verteilte Checksum-Netzwerke (Razor, Pyzor, dcc). Eine weitere ist ein Naive Bayes Klassifizierer der anhand bestimmter Trainingsbeispiele lernt und je bessere Erkennungsleistungen zeigt, desto mehr Trainigsdaten er erhällt. Ihre Benutzer können dazu aktiv beitragen indem Sie ihre Spam Mails explizit in einem Spam Ordner ablegen. Über diese Ordner kann man dann das Trainigsskript vom SpamAssassin laufen lassen. Aus den Trainigsdaten berechnet der Klassifizierer dann pro Nachricht eine Wahrscheinlichkeit, dass es sich um Spam handelt anhand der enthaltenen Worte. In der vorangegangenen Konfiguration von SpamAssassin haben wir festgelegt, dass SpamAssassin als Benutzer mail läuft dessen Arbeitsverzeichnis unter /var/mail liegt. Die Bayes-Datenbank liegt demzufolge unter /var/mail/bayesdb_{journal,seen,toks}.

Zum automatischen trainieren des naive Bayes Klassifizierers bietet es sich an ein Shell Skript per cron regelmäßig über ausgewählte Verzeichnisse laufen zu lassen. Dazu müssen Sie zunächst festlegen welche Postfächer verwertbare Nachrichten enthalten. Wichtig ist hier, dass die Nachrichten regelmäßig von Hand kontrolliert werden, damit der Klassifizierer keine falschen Informationen lernt.

#!/bin/bash
DBPATH=/var/mail/bayesdb
SPAMFOLDERS="\
/srv/mail/domain.tld/important.customer/Maildir/.Spam/cur \
/srv/mail/domain.tld/john.doe/Maildir/.Spam/cur \
"
HAMFOLDERS="\
/srv/mail/domain.tld/john.doe/Maildir/cur \
/srv/mail/domain.tld/important.customer/Maildir/cur \
"

for sfolder in $SPAMFOLDERS ; do \
        nice sa-learn --spam --dbpath /var/mail/bayesdb $sfolder
done

for hfolder in $HAMFOLDERS ; do \
        nice sa-learn --ham --dbpath /var/mail/bayesdb $hfolder
done

Legen Sie dieses Script unter /var/mail/satrain.sh ab, versehen Sie es mit Ausführrechten und tragen Sie es als cronjob für den Benutzer mail ein.

$> su mail
$> crontab -e

Im sich öffnenenden Editor tragen Sie dann den Crontab ein.

15 4 * * * /bin/bash /var/mail/satrain.sh >/dev/null

Diese Zeile sorgt dafür, dass das Skript jeden Tag um 4:15 ausgeführt wird und alle Ausgaben, mit Ausnahme von Fehlern, unterdrückt werden.

Dananch können Sie kontrollieren wieviele Spam bzw. Ham Nachrichten bereits gelernt wurden. Entweder über sa-learn oder per Autolearn. Die Zeile die mit nspam endet gibt die Anzahl der gelernten Spam Nachrichten an während die Zeile nham die Anzahl der Ham Nachrichten angibt.

$> sa-learn --dbpath /var/mail/bayesdb --dump magic
0.000          0        813          0  non-token data: nspam
0.000          0       7737          0  non-token data: nham

Webmail einrichten

Als Webmail Client bietet sich Roundcube aufgrund der unkomplizierten Einrichtung, des guten Funktionsumfanges und der ansprechenden Oberfläche an. Natürlich kann man sattdessen auch Horde/IMP oder einen andere Webmail Lösung verwenden, aber ich rate zu Roundcube.

Die Installation von Roundcube ist einfach und unkompliziert. Zunächst wechseln man in das Zielverzeichnis, z.B. /var/www, und lädt die aktuelle Installationsdatei herunter. Den Link findet man auf der Homepage von Roundcube.

$> cd /var/www
$> wget <URL>
$> tar xvfj roundcube-XXX.tar.bz2

Roundcube benötigt eine Datenbank um Einstellungen und Nachrichten zu speichern. Am besten legen Sie dafür eine eigene Datenbank an. Danach muss die Datei mysql.initial.sql aus dem Verzeichnis SQL in diese Datenbank importiert werden und die Datenbankinformationen in der Datei db.inc.php eingetragen werden.

$> cd roundcube-XXX/SQL
$> mysql -uDBUSER -pDBPASS DBNAME < mysql.initial.sql
$> cd ../config
$> cp db.inc.php.dist db.inc.php

In der Datei db.inc.php muss der Parameter $rcmail_config['db_dsnw'] angepasst werden.

$rcmail_config['db_dsnw'] = 'mysql://DBUSER:DBPASS@localhost/DBNAME';

Danach muss noch der Mailserver in der Datei main.inc.php eingetragen werden. Dazu wird diese Datei mit einem Texteditor geöfnet und der Parameter $rcmail_config['default_host'] angepasst.

$rcmail_config['default_host'] = 'mail.domain.tld';

Damit ist die Einrichtung von Roundcube auch schon abgeschlossen und das Webmail-Interface kann über den Webserver aufgerufen werden.

Verwaltungsinterface einrichten

Zum Verwalten der Benutzer in der Datenbank bietet sich ein Web-Frontend an. Dazu findet sich im Archiv mit den Konfigurationsdateien im Ordner scripts/backend/ ein Archiv mit dem Namen ispmail_admin.zip das, das Grundgerüst eines Verwaltungsfrontends in PHP enthällt. Mit diesem Interface ist die komplette Benutzerverwaltung möglich, allerdings ist es noch nicht sehr komfortabel. Der interessierte Leser mit PHP-Kentnissen kann anhand des Grundgerüstes allerdings schnell eine ansprechendere Oberfläche erstellen. Die Installation des Verwaltungsinterfaces ist denkbar einfach. Nachdem das Archiv an einem per Browser zugänglichen Ordner auf dem Webserver entpackt wurde, muss lediglich die Datei config/_conf.dba.inc.php angepasst werden. Dort sind insbesondere die folgenden Zeilen interessant.

$dba->database = "ispmail";
$dba->server = "localhost";
$dba->user = "ispmail";
$dba->password = "ispmail";

Das Verwaltungsfrontend benötigt zusätzlich zu den weiter oben beschriebenen Tabelle noch die Tabelle config in der einige Einstellungen abgespeichert werden. Dazu bitte folgenden SQL-Code ausführen.

DROP TABLE IF EXISTS `config`;
CREATE TABLE IF NOT EXISTS `config` (
  `id` int(16) NOT NULL auto_increment,
  `property` varchar(255) character set latin1 NOT NULL default '',
  `value` text character set latin1 NOT NULL,
  `created_at` int(16) NOT NULL default '0',
  `updated_at` int(16) NOT NULL default '0',
  PRIMARY KEY  (`id`),
  KEY `property` (`property`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=0;
INSERT INTO `config` (`id`, `property`, `value`, `created_at`, `updated_at`) VALUES
(0, 'url_protocol', 'http', 1201340788, 1216806584),
(1, 'list_limit', '34', 1204548583, 1213956805),
(2, 'site_name', 'ISP-Mail Admin', 0, 0);

Hier müssen die gleichen Daten eingetragen werden wie sie weiter oben für die Konfiguration der anderen Dienste verwendet wurden. Danach sollte man das Verzeichnis noch mit einem Passwortschutz über .htaccess versehen und man kann sofort loslegen Benutzer anzulegen.

Ein Screenshot der Liste der Postfächer

Dieses Bild zeigt eine Ansicht der Liste der verfügbaren Postfächer.

Ein Screenshot der Neuanlage eines Postfaches

Dieses Bild zeigt eine Ansicht der Neuanlage eines Postfaches

Ein Screenshot der Liste der Domains

Dieses Bild zeigt eine Ansicht der Liste der verfügbaren Domains.

Ein Screenshot der Liste der Hosts

Dieses Bild zeigt eine Ansicht der Liste der verfügbaren Hosts.

Sieve zum ausfiltern von Spam

Dovecot unterstützt Sieve Skripte über ein Plugin das man im Abschnitt protocol lda mit der Zeile

mail_plugins = cmusieve

aktivieren muss. Danach ist es möglich im Benutzerverzeichnis eine Datei mit dem Namen .dovecot.sieve abzulegen. Dort kann man Filter Regeln definieren die vom LDA während der Zustellung ausgeführt werden.

Beispiel Sieve Script:

require ["fileinto"];
# Move tagged Spam Mails into the Spam-Folder
if header :contains "X-Spam-Flag" ["YES"] {
  fileinto "spam";
  stop;
}

Dieses Skript kann auch dazu genutzt werden für alle Benutzer automatisch die Spam-Mails vorzusortieren. Dazu speichert man das Skript z.B. unter /srv/mail/globalsieve ab und setzt mit global_script_path = /srv/mail/globalsieve den globalen Skript Pfad auf das Skript. In diesem Zusammenhang sei davor gewarnt, dass Benutzer die ihre Mails ausschließlich per POP3 abrufen nur Nachrichten in ihrer Inbox, exklusive Unterverzeichnisse, sehen. Diese Benutzer können die Nachrichten im Spam Ordner nur über Webmail oder IMAP abrufen. Das kann bei unbedarften Benutzern unter Umständen zu Verwirrung und Ärger führen wenn false-positives im Spam Ordner landen. Weitere Informationen zu Sieve gibt es im RFC3028.

Mailman für Mailinglisten einrichten

Mit Mailman existiert ein moderner und leistungsfähiger Mailinglisten-Manager für Unix Systeme. Die Installation gestaltet sich wie bei Debian üblich recht unkompliziert.:

$> aptitude install mailman

Für die Einbindung in Exim sind zwei Parameter interessant. Zum einen die Alias Datei, in der die Pipe-Befehl für Mailman eingetragen werden sowie die Domainliste für die sich Mailman zuständig fühlt. Bei dem data Parameter kann der Pfad /etc/aliases.mailman einfach übernommen werden, wenn man plant die entsprechenden Aliase dort abzulegen. Die Daten die dort eingetragen werden gibt der Befehl newlist nach dem einrichten einer neuen Liste auf der Kommandozeile aus. Man muss die Angaben dann einfach in die Datei kopieren und die neue Liste ist einsatzbereit. Der Parameter domains gibt an für welche Domains dieser Router angewandt wird, d.h. hier definiert man den Domain Teil unter dem die Listen erreichbar sind. Wenn man z.B. als Domains lists.domain.tld eingetragen hat und eine List mit dem Namen exim angelegt hat, dann ist diese Liste unter exim@lists.domain.tld erreichbar.

mailman_aliases:
  driver = redirect
  allow_fail
  allow_defer
  data = ${lookup{$local_part}lsearch{/etc/aliases.mailman}}
  file_transport = address_file
  pipe_transport = address_pipe
  domains = lists.localdomain.tld
  user = list

Backup Mailserver einrichten

Ein Backup-MX, also ein zusätzlicher Mailserver, dient der Ausfallsicherheit und dem Lastausgleich. Das SMTP Protokoll sieht zwar vor das eine Nachricht vom Absender bis zu 4 Tage versucht wird zuzustellen, aber wenn Ihnen das nicht ausreicht, dann besteht eine Option darin einen Backup Mailserver einzurichten. Das DNS Protokoll erlaubt es für eine Domain mehrere Mailserver anzugeben.

Wenn Sie über die Einrichtung eines Backup-MX nachdenken, sollte Ihnen bewusst sein, dass, wenn ihr primärer Mailserver ausfällt, ihre Nutzer weder Nachrichten versenden noch empfangen können. Das einzige was der Backup-MX macht, ist Nachrichten anzunehmen die ihr primärer Mailserver im Moment nicht annehmen kann und sie dann an diesen weiterleitet. Die Einrichtung der benötigten Softwarepakete ist weitgehend mit denen des primären MX identisch, so dass hier auf den Abschnitt Installation der benötigten Pakete verwiesen sei.

Ein wichtiger Aspekt beim Einrichten eines Backup-MX ist es auf die Anfälligkeit gegenüber Spam zu achten. Es ist in der heutigen Zeit unverzeihlich einen Backup-MX einzusetzen, der nicht mindestens genauso gegen Spam abgesichert ist wie der primäre Mailserver. Spammer verschicken heute in der Regel Nachrichten mit gefälschen Abesnder Adressen. Oftmals sind dies existierende Adressen von vollkommen Unbeteiligten. Würde der Backup-MX diese Nachrichten annehmen ohne den lokalen Empfänger zu prüfen und der primäre MX würde später feststellen, dass der Benutzer nicht exisitiert wäre er genötigt laut RFC ein Fehlermeldung (Bounce) an den Absender zu schicken. Dieser hat damit allerdings nichts zu tun. Somit würde zum einen die Belastung des primären MX steigen und zum anderen würden andere Nutzer mit überflüssigen Fehlermeldungen belästigt. Um dieses Problem zu umgehen verwendet das hier vorgestellte Setup eine Replik der Benutzerdatenbank. Diese Replik wird mit Hilfe eines Shell-Skripts stündlich vom primären MX abgerufen. Wird eine Nachricht auf dem Backup-MX eingeliefert prüft dieser mit Hilfe der SQL Query MYSQL_Q_RELAY im Router smarthost ob der Benutzer überhaupt existiert. Diese Überprüfung findet bereits nach dem RCPT TO Schritt statt. Verantwortlich dafür ist die Option verify = recipient im accept  domains = +relay_to_domains Check.

Der Router dnslookup wird nur für die Überprüfung des Absenders benötigt. Zunächst werden wieder die Macros für die SQL-Abfragen definiert.

MYSQL_SERVER=localhost
MYSQL_USER=DBUSER
MYSQL_PASSWORD=DBPASS
MYSQL_DB=DBNAME
MYSQL_EMAILTABLE=mail_accounts
MYSQL_DOMAINTABLE=domains
MYSQL_HOSTTABLE = hosts
BL_WARN=zen.spamhaus.org
BL_DENY=zen.spamhaus.org

Diese Abfrage stellt fest ob wir diese Nachricht annehmen. D.h. ob sie für eine Domain bestimmt ist für die wir zuständig sind und ob das Postfach aktiv ist.

MYSQL_Q_RELAY=SELECT d.domain FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d WHERE d.domain='${quote_mysql:$domain}' AND ma.local_part='${quote_mysql:$local_part}' AND ma.is_enabled = 'yes' AND ma.domain = d.id

Diese Abfrage stellt fest ob die Nachricht für eine Domain bestimmt ist, für die wir Mails annehmen (Relay).

MYSQL_Q_RDOMAIN=SELECT domain FROM MYSQL_DOMAINTABLE WHERE domain='${quote_mysql:$domain}' AND is_enabled='yes'

Mit dieser Abfrage werden Nachrichten an deaktivierte Postfächer gleich zu Beginn verworfen.

MYSQL_Q_DISABLED=SELECT d.domain FROM MYSQL_EMAILTABLE AS ma, MYSQL_DOMAINTABLE AS d WHERE d.domain='${quote_mysql:$domain}' AND ma.local_part='${quote_mysql:$local_part}' AND ma.is_enabled = 'no' AND ma.domain = d.id

hide mysql_servers = "MYSQL_SERVER/MYSQL_DB/MYSQL_USER/MYSQL_PASSWORD"

primary_hostname = backupmx.domain.tld
domainlist      relay_to_domains = mysql;MYSQL_Q_RDOMAIN
hostlist        relay_from_hosts = 127.0.0.1
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_mail = acl_check_from
qualify_domain = domain.tld
never_users = root
host_lookup = *
trusted_users = mail
untrusted_set_sender = *
local_from_check = false
rfc1413_hosts = *
rfc1413_query_timeout = 15s
check_spool_space = 50M
check_log_space = 20M
return_size_limit = 20k
message_size_limit = 20M
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d
deliver_queue_load_max = 8
queue_only_load = 10
remote_max_parallel = 15
tls_certificate = /etc/ssl/certs/server.domain.tld.crt
tls_privatekey = /etc/ssl/private/server.domain.tld.key
tls_advertise_hosts = *

Über die Option local_interfaces können die Adresse definiert werden auf denen Exim auf Verbindungen wartet. In diesem Fall ist dies sowohl localhost, als auch eine öffentliche IPv4 sowie eine IPv6 Adresse.

local_interfaces = <; 127.0.0.1 ; 192.168.0.2 ; \
                        2001:0DB8::2

Der Port ist im Falle des Backup-MX nur Port 25, da er nicht dafür ausgelegt ist Nachrichten von einliefernden Benutzern anzunehmen.

daemon_smtp_port = 25
begin acl
acl_check_from:
accept
acl_check_rcpt:
accept  hosts = :
deny    domains       = +relay_to_domains
                local_parts   = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
accept  local_parts   = postmaster
                domains       = +relay_to_domains
require verify        = sender
accept  authenticated = *
warn      message       = X-blacklisted-at: $dnslist_domain
        dnslists        = BL_WARN
deny      dnslists      = BL_DENY
defer
        message = $sender_host_address is not yet authorized to \
                        deliver mail from <$sender_address> to <$local_part@$domain>. \
                        Please try later.
        log_message     = greylisted.
        domains = +relay_to_domains
        !senders        = : postmaster@*
        !hosts         = : +relay_from_hosts : \
                                        ${if exists {/etc/greylistd/whitelist-hosts}\
                                                                {net-lsearch;/etc/greylistd/whitelist-hosts}{}} : \
                                        ${if exists {/var/lib/greylistd/whitelist-hosts}\
                                                                {net-lsearch;/var/lib/greylistd/whitelist-hosts}{}}
        set acl_m9      = $sender_host_address $sender_address $local_part@$domain
        set acl_m9      = ${readsocket{/var/run/greylistd/socket}{$acl_m9}{5s}{}{}}
        condition       = ${if eq {$acl_m9}{grey}{true}{false}}
accept  domains       = +relay_to_domains
                endpass
                verify        = recipient
accept  hosts         = +relay_from_hosts
deny    message       = relay not permitted
# Routers
begin routers

Der Fail Router sortiert alle Nachrichten an deaktivierte Postfächer aus.

# Fail Router
fail_router:
  driver = redirect
  domains = ${lookup mysql {MYSQL_Q_DISABLED}{$value}}
  data = ":fail:"
  allow_fail

Der Smarthost Router kümmert sich um die Zustellung zum primären Mailserver. Bei Router List sollte anstelle eines Hostnamens die IP-Adresse eingetragen werden.

# Smarthost Router
smarthost:
  driver = manualroute
  domains = ${lookup mysql {MYSQL_Q_RELAY}{$value}}
  transport = remote_smtp
  route_list = * 192.168.0.2 byname
  host_find_failed = defer
  same_domain_copy_routing = yes
  no_more

Der DNS-Lookup Router wird für Sender Verify benötigt.

# DNS Lookup Router - required for Sender Verify
dnslookup:
  driver = dnslookup
  domains = ! +relay_to_domains
  transport = remote_smtp
  ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
  no_more
# Transports
begin transports
remote_smtp:
  driver = smtp
  interface = 192.168.0.2
  devnull_delivery:
  driver = appendfile
  file = /dev/null
  group = mail
disabled_bounce:
  driver = autoreply
  from = ${local_part}@${domain}
  to = ${sender_address}
  user = mail
  subject = "Re $h_Subject:"
  text = "Your message to ${local_part}@${domain} was rejected due to a\n\
        disabled account. Please try again later\n"
devnull_transport:
  driver = appendfile
  file = /dev/null
  user = mail

begin retry
*                      *           F,2h,15m; G,16h,1h,1.5; F,4d,6h

begin rewrite
*@domain.tld    ${lookup{$1}lsearch{/etc/email-addresses}\
                                                {$value}fail} frFs
begin authenticators

Das Skript zur Synchronisation der Benutzerdatenbank ist simpel gehalten.

Die Idee hier ist, dass das Script mit mysqldump, am besten über eine gesicherte VPN Verbindung, die Systemdatenbank vom primären Mailserver ausliest und anschließend auf dem Backup-MX einspielt.

#!/bin/sh
mysqldump -hDBHOST -uDBUSER -pDBPASS DBNAME | mysql -uDBUSER -pDBPASS DBNAME

Bei DBHOST muss hier die Adresse des primären Mailserver eingetragen werden.

Fehlerbehebung

Alternativen

Wer mit meinem Tutorial gar nicht glücklich wird, dem helfen vielleicht die folgenden Links weiter.

Download

Hier können Sie die gesamte Konfiguration als Paket herunterladen: Exim Konfiguration.

FAQ

F: Was wird genau gesperrt wenn man über das Backend eine Mailadresse oder eine Domain sperrt?

A: Die Adresse oder Domain wird auf "is_enabled=no" gesetzt, d.h. Nachrichten für die entsprechenden Accounts werden abgewiesen und ein einloggen von außen ist nicht mehr möglich.

F: Muss man die Maildir-Verzeichnisse selbst erstellen oder geschieht dies automatisch?

A: Die Maildir-Verzeichnisse werden automatisch von deliver beim ausliefern der ersten Nachricht angelegt.

F: Kann man POP3 und POP3S parallel verwenden?

A: Ja, das ist möglich in der vorgestellten Konfiguration aktiviert.

F: Ist es möglich ClamAV direkt einzubinden?

A: Ja, in der exim Konfiguration gibt es im Abschnitt routers einen kommentierten Block mit dem Namen amavis_director. Über Amavis ist es relativ problemlos möglich ClamAV einzubinden. Näher Details dazu muss ich allerdings für den Moment schuldig bleiben. Die Angaben dazu liefere ich bei Gelegenheit nach.

F: Ist es möglich mit dem vorgestellten Setup einen Mailserver für ein LAN mit fetchmail (o.ä.) zu betreiben?

A: Ja, das ist möglich aber nicht sinnvoll. Dafür gibt es sicher bessere Anleitungen. Dieses Tutorial zielt auf den Betrieb eines (eigenständigen) Mailservers im Internet (root Server, o.ä.) ab.

F: Benötigt man einen Backup-MX?

A: Nein, nicht unbedingt. Es gibt sogar Meinungen, dass ein Backup-MX mehr Probleme als Nutzen bereitet. Das SMTP Protokoll legt fest, dass ein Mailserver für ca. 3 Tage versuchen soll eine Nachricht auszuliefern, falls ein Server nicht erreichbar ist.

F: Wo befinden sich die benötigten Dateien, z.B. exim4.conf?

A: Die exim Konfiguration liegt unter Debian unter /etc/exim4. Die Datei exim4.conf exisiert auf einem Debian System normalerweise nicht und muss angelegt werden. Ein Beispiel in im verlinken Archiv zu finden.

F: Wie lässt sich eine Catchall-Adresse realisieren?

A: Die Catchall-Funktionalität ist bereits im Setup enthalten. Durch den virtual_wclocal_redirect Router werden andernfalls nicht gematchte Mails an eine Domain umgeleitet wenn in der Datenbank ein Alias mit dem local_part * existiert.

Danksagung

Mein Linux Blog | Rezepte mit Bild | Software Projects | Hosted by id-schulz

Valid XHTML 1.0 Transitional CSS ist valide!