Mittwoch, 14. Mai 2014

Email Archivierung - Teil 2 (Updated: BCC bleiben unsichtbar)

Der erste Teil meines Beitrags bezieht sich auf die Herausforderung, jede Mail zwecks Archivierung möglichst originalgetreu aus dem Mailverkehr auszuschleusen. Mittels dovecot und dessen sieve-Plugin gelingt auch eine halbwegs strukturierte Ablage der Mails.

Nachdem ich eine Weile auf diese Art und Weise Mails gesammelt habe, wurde jedoch schnell klar, dass von einer gesetzlich geforderten Archivierung keine Rede sein kann. Mindestens eine Verschlüsselung bzw. Signierung wäre noch erforderlich - und natürlich eine vernünftige Suche.

Beim Thema Email-Archivierung arbeiten viele Anbieter mit einem teils unpräzisen Bedrohungspotenzial, von dem sich ein Geschäftsführer oder Teilbereichverantwortlicher freizukaufen hofft. Während durchaus gesetzliche Vorgaben zu erfüllen sind, soll ein Archiv aber nicht nur eine Last sein, sondern vorrangig den Benutzern auch eine Hilfe!

Da ich grundsätzlich den Marketingaussagen über Software misstraue und wissen möchte, wie etwas funktioniert, habe ich mich im OpenSource Bereich nach einer passenden Lösung umgeschaut.
Ein wichtiges Hilfsmittel dazu war das Linux Magazin, insbesondere dessen Ausgabe 01/2013, die inzwischen auch Online einsehbar ist.

Bereits seit ein paar Jahren verfolge ich die Entwicklung von (Open)Benno Mailarchiv und MailArchiva. Neben der zunehmend kommerziellen Ausrichtung missfällt mir die Komplexität, die diese Lösungen mit sich bringen.

Ich habe mich schließlich für piler entschieden und möchte im folgenden ein wenig meine Begeisterung teilen.



Das Projekt schaut zwar nach einer One-Man show aus, aber derart prompte Antwort auf Probleme habe ich selten bekommen. Zusätzlich gibt es noch eine überschaubare Mailingliste, deren Posts man (zumindest am Anfang) mitlesen sollte.

Die Installation erfordert zunächst eine Reihe von externen Paketen, die sich in Debian aber schnell installieren lassen:

apt-get install openssl mysql-server-5.5 libapache2-mod-php5 \
      libmysqlclient-dev php5-gd php5-memcache php5-mysql php5-xcache \
      sphinxsearch libtre-dev libzip2 sysstat catdoc poppler-utils unrtf \
      tnef xlhtml memcached libpst clamav-daemon clamav-freshclam


Apache und MySQL sollten nach eigenem Gusto konfiguriert werden.
Zusätzlich muss der MemCache-Daemon aktiviert werden:

# /etc/default/memcached
#
ENABLE_MEMCACHED=yes

SphinxSearch wird extra von piler gestartet, also wird die Debian Start-Prozedur deaktiviert (Debian Re-Indiziert sonst täglich alles über einen Cron-Job, der die piler Daten korrumpiert).

# /etc/default/sphinxsearch
#
START=no

Auf einer geeigneten Partition (viel Platz!) bekommt der User piler:piler sein neues Homeverzeichnis

groupadd piler
useradd -g piler -s /bin/sh -d /opt/archiv piler
usermod -L piler
mkdir /opt/archiv/
chown piler:piler /opt/archiv
chmod 751 /opt/archiv 

Nun kann es ans Auspacken und Kompilieren gehen. Ich benutze die aktuelle Version 0.1.25rc2 (wird evtl. in 1.1 umbenannt), da es in vorherigen Versionen Unsauberkeiten bezüglich der Datumsinterpretation beim Erstimport gab (von wann stammt die Mail vs. wann wurde sie importiert).

tar zxvf piler-0.1.25-rc2.tar.gz
cd piler-0.1.25-rc2
./configure \
      --localstatedir=/opt \
      --sysconfdir=/etc \
      --enable-clamd \
      --enable-memcached \
      --enable-starttls \
      --with-database=mysql

make
make install 

Die Post-Install Routinen führe ich lieber von Hand durch:

make key
cp piler.key /etc
chgrp piler /etc/piler.key
chmod 640 /etc/piler.key

Der generierte Key ist dringend zu sichern, da damit die Nachrichten ver-/entschlüsselt werden!

mysql -u root -p
 create database piler character set 'utf8';
 grant all privileges on piler.* to piler@localhost identified by 'verystrongpassword';
 flush privileges;
 quit;

mysql -u piler -p piler < util/db-mysql.sql

In der /etc/piler.conf muss die so erzeugte Datenbank hinterlegt werden und bei der Gelegenheit noch ein paar weitere Anpassungen vorgenommen werden:

# /etc/piler.conf (Auszug)
#

; write pid file
pidfile=/var/run/piler/piler.pid

; piler will listen here
listen_addr=127.0.0.1
listen_port=10026

clamd_socket=/var/run/clamav/clamd.ctl

workdir=/opt/piler/tmp

; extra header field to treat as To:
extra_to_field=X-Envelope-To:

; if piler detects this line in the mail header, then it will assume
; the message is a spam. You should include your own antispam solution's
; specific line.
spam_header_line=X-Spam-Status: Yes

; memcached server to use. Currently piler supports only 1 memcached server
memcached_servers=127.0.0.1

Wie man sieht benutze ich den zusätzlichen Header X-Envelope-To, wie im ersten Teil beschrieben.
Im Beispiel läuft auf dem Piler Host ein vorgeschalteter Postfix, der die Konformität der eingehenden Mails sicherstellt und über eine transport_map an piler auf Port 10026 zustellt.

# /etc/postfix/main.cf
#
relay_domains = archiv.local
transport_maps = hash:/etc/postfix/transport

# /etc/postfix/transport
#
archiv.local     :[127.0.0.1]:10026

Das SphinxSearch Beispiel Konfigurationsfile muss noch umkopiert werden bevor es angepasst werden kann:

mv /etc/sphinx.conf.dist /etc/sphinxsearch/sphinx.conf

Anzupassen sind die Platzhalter MYSQL_HOSTNAME, MYSQL_DATABASE, MYSQL_USERNAME und MYSQL_PASSWORD. Zusätzlich müssen die Pfade der Index-Dateien von "/var/piler/sphinx/" auf "/opt/archiv/sphinx/" geändert werden. Dann kann der Index erstellt werden.

su - piler
indexer --all
exit

Gestartet wird Piler und der Indexer über die Init-Scripte:

/etc/init.d/rc.searchd start
/etc/init.d/rc.piler start


Als letzter Schritt wird die Weboberfläche auf einen eigenen Virtual Host kopiert.

cp -r webui /var/www/piler
chown -R webmaster:www-data /var/www/piler
chmod -R o-rwx,g-w /var/www/piler
chmod g+w /var/www/piler/tmp

Um bei einem Update nicht immer die gesamte Konfiguration durchführen zu müssen, wird eine "config-site.conf" Datei angelegt:

# /var/www/piler/config-site.conf
#
<?php

$config['SITE_NAME'] = 'Mein Piler Mailarchiv';
$config['SITE_URL'] = 'http://archiv.local/';
$config['DIR_BASE'] = '/var/www/piler/';

$config['ENABLE_AUDIT'] = 1;
$config['MEMCACHED_ENABLED'] = 1;
$config['ENABLE_ON_THE_FLY_VERIFICATION'] = 1;
$config['ENABLE_SYSLOG'] = 1;
$config['PILER_HOST'] = '127.0.0.1';
$config['PILER_PORT'] = 10026;
$config['SMARTHOST'] = 'interner_exchange_server.local';
$config['SMARTHOST_PORT'] = 25;
$config['SMTP_DOMAIN'] = 'example.com';
$config['SMTP_FROMADDR'] = 'no-reply@example.com';
$config['ADMIN_EMAIL'] = 'alex@example.com';

$config['DEFAULT_LANG'] = 'de';
$config['TIMEZONE'] = 'Europe/Berlin';

$config['INDEXER_BEACON'] = '/opt/piler/stat/indexer';
$config['PURGE_BEACON'] = '/opt/piler/stat/purge';
$config['PILER_HEADER_FIELD'] = 'X-piler-id: ';

$config['DIR_SPHINX'] = '/opt/piler/sphinx/';
$config['DIR_STAT'] = '/opt/piler/stat';
$config['DIR_IMAP'] = '/opt/piler/imap';
$config['DIR_TMP'] = '/opt/piler/tmp';

$config['DB_DRIVER'] = 'mysql';
$config['DB_PREFIX'] = '';
$config['DB_HOSTNAME'] = 'localhost';
$config['DB_USERNAME'] = 'piler';
$config['DB_PASSWORD'] = 'verystrongpassword';
$config['DB_DATABASE'] = 'piler';

$partitions_to_monitor = array('/', '/home', '/var', '/opt', '/tmp');
$config['DATA_PARTITION'] = '/opt';

?> 

Damit die .htaccess funktioniert, muss in der Virtual Host Konfiguration die "AllowOverride All" Direktive auf dem Verzeichnis gesetzt sein.

Die Cron-Jobs fehlen noch:

crontab -e -u piler

*/15 * * * * /usr/local/bin/indexer --quiet delta1 --rotate && sleep 2 && /usr/local/bin/indexer --quiet --merge main1 delta1 --merge-dst-range deleted 0 0 --rotate
*/15 * * * * /usr/local/bin/indexer --quiet tag1 --rotate
*/15 * * * * /usr/local/bin/indexer --quiet note1 --rotate
30   7 * * * /usr/bin/php /usr/local/libexec/piler/daily-report.php /var/www/piler


Nun kann die Piler Oberfläche unter "archiv.local" (bzw. entsprechender Servername) im Browser aufgerufen werden.

admin@local:pilerrocks

Nachdem in der Benutzerverwaltung die Passwörter für "admin" und "auditor" geändert wurden, gilt es die Domänen zu konfigurieren, für die wir archivieren.
Ich fasse dabei organisatorisch zusammengehörige Domains unter der Hauptdomäne zusammen, also z.B.

Domänen:      example.com
              example.org

Domänenalias: example.com

Nun kommt der langersehnte Moment, Ordnung ins Archiv zu bekommen. Dazu importiere ich sämtliche bereits archivierte Mails erneut in piler:

cd /tmp
# Anzahl zu importierender Nachrichten (rein informativ)
ls -1 /opt/oldarchive/userarchive/*/*/*/*/*/new | wc -l
pilerimport -d /opt/oldarchive/userarchive
# oder
screen pilerimport -d /opt/oldarchive/userarchive


(Da es sich dabei normalerweise um einen sehr langwierigen Prozess handelt, benutze ich screen, um den Import im Hintergrund laufen zu lassen.)

Nach einem Import sollte das Accounting noch schön gemacht werden, indem die alten Mails nachträglich in der Statistik erfasst werden: (Start/Stop entsprechend anpassen)

su - www-data
usr/bin/php /usr/local/libexec/piler/generate_stats.php \
     --webui="/var/www/piler" \
     --start=2010-01-01 \
     --stop=2014-05-14
exit

FERTIG!

Bisher kann sich nur der Admin und ein Auditor am Server anmelden. Sofern man die Anbindung ans Active Directory vorgenommen hat, kann sich jeder Benutzer mit einer seiner Email-Adressen anmelden - oder gar per SSO. Aber das kommt in einem späteren Post...

Update (10.07.2014):
Es hatte mich etwas gewurmt, dass nun sämtliche Mails mit Klartext X-Envelope-To Headern versehen werden und so auch sämtliche BCC Empfänger sichtbar wurden. Daher habe ich dem piler-Entwickler die Implementierung von Sub-Adressierung (siehe Teil 1) vorgeschlagen.
Binnen eines Tages war das Feature eingebaut (VERP-Adressierung) und funktioniert inzwischen fehlerfrei. Exzellenter Support!!!

Dafür verwende ich eine "recipient_bcc_maps" mit folgendem Inhalt:
# /etc/postfix/bcc_map
#
/^([^+]+).*@(.*)$/      archiv+$1=$2@example.org

Update 2 (18.05.2015):
Auf der Piler-Mailingliste hat ein Anwender vorgeschlagen, den X-Envelope-To Header selektiv zu entfernen. Das funktioniert indem man den Standard SMTP/LMTP Transporten in der /etc/postfix/master.cf einen zusätzlichen Header-Check mitgibt in der Form:

# /etc/postfix/master.cf
#
...
smtp      unix  -       -       -       -       -       smtp
          -o smtp_header_checks=pcre:/etc/postfix/x-remove-envelope-to
lmtp      unix  -       -       -       -       -       lmtp
          -o smtp_header_checks=pcre:/etc/postfix/x-remove-envelope-to
...

# /etc/postfix/x-remove-envelope-to
#

/^X-Envelope-To/        IGNORE



Für die Archiv-Domain muss analog dazu ein uneingeschränkter Transport angelegt sein, auf den in der /etc/postfix/transport verwiesen wird:

# /etc/postfix/master.cf
#
...
piler_smtp unix  -       -       -       -       -       smtp
...

# /etc/postfix/transport
#
archiv.local     piler_smtp:[127.0.0.1]:10026

Alle Mails im Archiv enthalten weiterhin den X-Envelope-To Header. Soll dieser in der Webansicht versteckt werden, so müssen folgende Einträge vorgenommen werden:

# model/search/message.php
# add the following line right after "$has_journal = $this->remove_journal($msg);"
#
if(Registry::get('auditor_user') == 0) {
   $msg = preg_replace("/" . HEADER_LINE_TO_HIDE . ".{1,}\n/i", "", $msg);
}

# config.php
#
$config['HEADER_LINE_TO_HIDE'] = 'X-Envelope-To:';