Des chardons pour les moutons

Préambule

Ce petit tutoriel décrit la mise en place d'une plate-forme personnelle de mail :

  1. récupération du courrier depuis un serveur imap (fdm)
  2. traitement antispam (dspam + postgresql)
  3. consultation (mutt)

On est ici dans un cas purement “client”, on considère que le serveur source (imap, pop, …) ne gère pas comme on le veut notre courrier et que l'on veut être relativement indépendant de celui ci. Nous allons utiliser quelques outils qui sont peu connus à l'heure actuelle : fdm et dspam. Fdm se veut être un remplacement pour procmail et fetchmail/getmail. Dspam lui est une alternative à SpamAssassin, il se veut plus rapide que celui ci car écrit en C. Dspam utilise une base de donnée pour gérer les signatures des spams et diverses choses attenantes à ceux ci, j'ai choisi Postgresql pour diverses raisons.

Donc, au final on devrait avoir une plateforme *propre*, légère et efficace pour nos mails1).

Installation des outils

Mon OS de prédilection pour mon desktop est FreeBSD, je vais donc vous présenter la méthode d'installation que j'ai suivi pour avoir tous les outils dont on a besoin, je suppose que tout OS propre digne de ce nom est en mesure de vous fournir ces mêmes outils.

Il nous faut donc installer : fdm, procmail (déjà installé en général), dspam, et postgresql. Mon outil préféré pour les installations est 'portinstall'.

Fdm

$> sudo portinstall fdm

Il n'y a pas de configuration particulière à faire, méfiez vous il risque de se vautrer au cours de la compilation ou du configure et réclamer une ou deux libs, rien d'impossible à installer cependant.

Dspam

$> sudo portinstall dspam

Cette fois ci il y a un peu de configuration à faire, j'ai activé les options suivantes :

  • syslog
  • debug (optionnel)
  • verbose debug (optionnel)
  • bnr debug (optionnel)
  • clamav (au cas où)
  • clamav_local
  • MySQL 5.0
  • PostgreSQL
  • Hash
  • User homedir
  • virt users
  • procmail lba

En principe il devrait installer les ports PostgreSQL-client et MySQL-client, on n'utilisera que le premier cependant.

PostgreSQL

$> sudo portinstall postgresql-server

Personnellement j'ai choisi d'installer la version 8.1, simplement parce que c'est cette version de postgresql-client que dspam a installé.

Faites bien attention aux derniers messages qui vous permettront de finir la configuration de votre serveur.

Mutt

Mutt est un choix à nouveau personnel parce qu'il est simple, léger et qu'il fait ce qu'on lui demande de faire : être un client mail.

$> sudo portinstall mutt

Muttng est aussi disponible, et je me suis demandé s'il ne serait pas plus intéressant. Cependant, après quelques tests je me suis aperçu que Mutt répondait mieux à mes besoins.

Configuration de FDM

Fdm va faire le plus gros du boulot : se connecter au serveur de mail, récupérer les mails, les passer à dspam et les trier dans les bonnes boîtes selon ce que Dspam dit de chaque mail.

La configuration de Fdm n'est pas forcément triviale mais elle me paraît un peu plus simple et intuitive que celle de Procmail. Gardez l'adresse : http://fdm.sourceforge.net/ dans un coin, ça peut servir (le man aussi).

# Set the maximum size of mail.
	set maximum-size 128M
# set the lock file in a user writable place
	set lock-file "/home/ange/.fdmlock"

# An action to save to the maildir ~/mail/inbox.
	action "inbox" maildir "/home/ange/mail/inbox"
	action "spam"  maildir "/home/ange/mail/spamed"

# setup an account to fetch from, an imap server
	account "imap" imap server "imap.you.rs" 
	user "your@login" pass "something" new-only keep 
	#user "my-username" pass "my-password" folder "my-folder"

# spam filtering
	match all action rewrite "/usr/local/bin/dspam --client --user ange --mode=toe --deliver=innocent,spam --stdout" continue
# spam trashing
	match "^X-DSPAM-Result: spam" in headers action "spam"
	match "^Subject: SPAM" in headers action "spam"

# Match all other mail and deliver using the 'inbox' action.
	match all action "inbox"
  1. la première ligne permet de spécifier une taille max de mail.
  2. la deuxième permet de dire à Fdm d'utiliser un fichier dans le home de notre user, ce qui sera pratique dans le cadre de l'appel par crontab.
  3. on spécifie une action inbox permet de déplacer des messages dans le répertoire inbox
  4. on spécifie une actionspam permet de déplacer des messages dans le répertoire des spams, notez le spamed.
  5. on spécifie un compte imap sur lequel Fdm va aller taper (nom de compte, type de connection 2) et l'adresse du serveur)
  6. on spécifie login, password, l'option “new-only” qui va récupérer les mails nouveaux seulement
  7. l'option keep lui dit de tout garder sur le serveur (ça permet de ne pas perdre un des intérêts de l'imap)
  8. on pipe tous les mails (match all) dans une action rewrite qui va appeller Dspam 3).
  9. on récupère donc nos mails et on les trie : les spam à gauche (appel de l'action spam), et les innocents à droite (appel de l'action inbox)

Voilà c'est fini en principe.

Configuration de Dspam

Voila la grosse configuration bien prise de tête, enfin en théorie mais comme je suis passé par là ça devrait être plus simple pour vous. Avant d'aller plus loin vous devez avoir un PostgreSQL qui tourne.

On va commencer par créer une base :

$> psql -Upgsql template1
template1=# create database dspam;
CREATE DATABASE
template1=# create user dspam with unencrypted password 'dspamrulez';
CREATE USER
template1=# grant all on database dspam to dspam;
GRANT
template1=#\q
$>

Bon là on va faire le deuxième truc prise de tête : la création des tables. Mais au préalable il faut faire un truc : ajouter plpgsql pour la base dspam.

$> createlang -U pgsql plpgsql dspam
$> createlang -U pgsql -l dspam
Procedural Languages
  Name   | Trusted? 
 plpgsql  | yes

Maintenant ajoutons les tables :

$> psql -U pgsql dspam < /usr/local/share/examples/dspam/pgsql/virtual_users.sql
CREATE SEQUENCE
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "dspam_virtual_uids_pkey" for table "dspam_virtual_uids"
CREATE TABLE
CREATE INDEX
CREATE INDEX
$> psql -U pgsql dspam < /usr/local/share/examples/dspam/pgsql/pgsql_objects.sql
...

Dans le dernier cas, j'ai eu des erreurs, j'ai même du supprimer une ligne de commentaire qui était mal terminée.

On peut faire la même chose avec Mysql (comme expliqué dans le numéro 100 de Gnu/Linux Mag France dans l'excellent article sur la mise en oeuvre d'une plate-forme mail FreeBSD Exim) :

$> mysql -u root -p
mysql> use mysql;
mysql> create database dspam;
mysql> grant all on dspam.* to 'dspam'@'localhost' IDENTIFIED BY 'dspamrulez';
mysql> flush privileges;
mysql>quit
$> mysql -udspam -p dspam /usr/local/share/examples/dspam/mysql/virtual_users.sql
$> mysql -udspam -p dspam /usr/local/share/examples/dspam/mysql/mysql_objects-speed.sql

On passe ensuite à la configuration de Dspam en lui même :

Home /var/db/dspam
StorageDriver /usr/local/lib/libpgsql_drv.so
TrustedDeliveryAgent "/usr/local/bin/procmail"
OnFail error
Trust root
Trust mail
Trust mailnull 
Trust smmsp
Trust daemon
Trust ange
TrainingMode toe
TestConditionalTraining on
Feature chained
Feature whitelist
Algorithm graham burton
PValue graham
Preference "spamAction=tag"
Preference "signatureLocation=message"  # 'message' or 'headers'
Preference "showFactors=off"
Preference "spamSubject=SPAM"
AllowOverride trainingMode
AllowOverride spamAction spamSubject
AllowOverride statisticalSedation
AllowOverride enableBNR
AllowOverride enableWhitelist
AllowOverride signatureLocation
AllowOverride showFactors
AllowOverride optIn optOut
AllowOverride whitelistThreshold
MySQLServer     /tmp/mysql.sock
MySQLUser               dspam
MySQLPass               dspamrulez
MySQLDb                 dspam
MySQLCompress           true
PgSQLServer     127.0.0.1
PgSQLPort       5432
PgSQLUser       dspam
PgSQLPass       dspamrulez
PgSQLDb         dspam
HashRecMax              98317
HashAutoExtend          on  
HashMaxExtents          0
HashExtentSize          49157
HashMaxSeek             100
HashConnectionCache     10
Notifications   off
PurgeSignatures 14          # Stale signatures
PurgeNeutral    90          # Tokens with neutralish probabilities
PurgeUnused     90          # Unused tokens
PurgeHapaxes    30          # Tokens with less than 5 hits (hapaxes)
PurgeHits1S     15          # Tokens with only 1 spam hit
PurgeHits1I     15          # Tokens with only 1 innocent hit
LocalMX 127.0.0.1
SystemLog on
UserLog   on
Opt out
MaxMessageSize 307200
ProcessorBias on

Vous pouvez voir ici que les logins pour MySQL et PostgreSQL sont configurés. Si vous utilisez MySQL pensez à modifier “StorageDriver /usr/local/lib/libpgsql_drv.so” en “StorageDriver /usr/local/lib/libmysql_drv.so”

Rappellez vous que l'on va lancer Dspam au coup par coup, donc pas en mode daemon.

Muttrc

La configuration de Mutt se fait par le fichier Muttrc, je suis parti du fichier de Sam (sam muttrc dans un bon moteur de recherche). Voici en gros ce que donne le mien (inspiré par le sien donc) :

set folder = ~/mail
set tmpdir = ~/tmp
set spoolfile = ~/mail/inbox
set mbox_type=maildir 
set timeout=5
set record="=Sent"
set postponed="=Drafts"
source ~/.mutt/colors.rc          # colors definitions
source ~/.mutt/gpg.rc             # gpg support
source ~/.mutt/auto_views.rc      # auto views
source ~/.mutt/lists.rc           # mailing lists
source ~/.mutt/mailboxes.rc
set hostname=librium.org
set hidden_host
set use_domain
set nomove
set realname="Thomas Riboulet"
set signature=~/.signature
set alias_file=~/.mutt/aliases.rc
folder-hook . set sort=threads
set sort_aux=last-date-received
folder-hook "=Sent" set sort=date-received
set delete=ask-yes
set nomark_old
set reverse_alias
set strict_threads
set nopipe_decode
set mailcap_path=~/.mutt/mailcap.rc:/etc/mailcap
set mailcap_sanitize=yes
macro index d "s=Trash" "move message to trash"
macro pager d "s=Trash" "move message to trash"
macro index X "s=spam" "file as Spam"
macro pager X "s=spam" "file as Spam"
macro index B "s=unspam" "file as UnSpam"
macro pager B "s=unspam" "file as UnSpam"
macro index I "c=inbox<enter>" "go to Inbox"
macro pager I "c=inbox<enter>" "go to Inbox"
set confirmappend=no
set followup_to=yes
set index_format="%4C %Z %{%b %d} %-15.15F (%4l) %s"
set pager_stop
set pager_index_lines=9
set pager_context=3
ignore *
unignore From From: To Cc Subject Date Reply-To X-Mailer User-Agent Bcc
unhdr_order
hdr_order From: To: Cc: Subject: Reply-To: Date:
set quote_regexp="^( {0,4}[>|:%]| {0,4}[a-z0-9]+[>|]+)+"
alternative_order text/enriched text/plain text/html
set charset="iso-8859-1"
set send_charset="iso-8859-1:iso-8859-15:us-ascii:utf8"
set include
set edit_headers
unset write_bcc
set attribution="* %n <%a> [%{%Y-%m-%d %H:%M:%S}]:\n"
set mime_forward
folder-hook . my_hdr From: your@somewh.ere
folder-hook . my_hdr Reply-To: your@somewh.ere
set allow_8bit=yes
push <show-version>

Remarquez en particulier les 4 première lignes ainsi que les macros :

  1. set folder permet de désigner le Maildir
  2. set tmpdir permet de désigner où mettre les fleurs
  3. set spoolfile désigne l'inbox dans lequel mutt va piocher par défaut
  4. set mbox_type=maildir pour dire à mutt d'utiliser le format maildir et non mbox
  5. macro index X “s=spam” permet de déplacer des Spams non détectés par Dspam dans le maildir spam
  6. macro index B “s=unspam” permet de déplacer des mails innocents d'une boîte vers le maildir unspam

Créez votre répertoire mail (ici ~/mail) et ses sous répertoires indispensables :

  • inbox
  • spam
  • unspam
  • spamed

Dans chaque cas pensez à créer les 3 sous répertoires new, cur et tmp. Enfin faites un chmod -R 700 ~/mail pour que tout soit bien propre.

Crontab et entraînement de Dspam

En principe voilà comme cela devrait fonctionner : Fdm récupère les mails et les passe à Dspam celui ci les trie et les repasse à Fdm. Fdm place les mails considérés comme “propres” par Dspam dans le maildir inbox, et les “spams” dans le maildir spamed. Dans le cas où vous trouveriez un spam dans votre inbox (donc un spam qui serait passé au travers) envoyez le dans le maildir spam. Dans le cas où vous trouveriez un innocent au milieu de la décharge (dans le maildir spamed) déplacez le dans le maildir unspam. Nous allons alors utiliser un script pour corriger Dspam et lui dire quand il s'est trompé. Voici mon script “train_dspam.sh”.

#!/usr/local/bin/bash

# train dspam by pushing mail/spam maildir content
MYMAILDIR=/home/ange/mail
SPAMDIR=$MYMAILDIR/spam
UNSPAMDIR=$MYMAILDIR/unspam

# commands
CAT=/bin/cat
DSPAM=/usr/local/bin/dspam
RM=/bin/rm
MV=/bin/mv

# false positive
for mail in `ls ${SPAMDIR}/new/`; do
        ${CAT} ${SPAMDIR}/new/$mail | ${DSPAM} --client --user ${USER} --class=spam --source=error
        ${MV} ${SPAMDIR}/new/$mail ${MYMAILDIR}/spamed/new
done

for mail in `ls ${SPAMDIR}/cur/`; do
        ${CAT} ${SPAMDIR}/cur/$mail | ${DSPAM} --client --user ${USER} --class=spam --source=error
        ${MV} ${SPAMDIR}/cur/$mail ${MYMAILDIR}/spamed/cur
done

# false negative
for mail in `ls ${UNSPAMDIR}/new/`; do
        ${CAT} ${UNSPAMDIR}/new/$mail | ${DSPAM} --client --user ${USER} --class=innocent --source=error
        ${MV} ${UNSPAMDIR}/new/$mail ${MYMAILDIR}/inbox/new
done

for mail in `ls ${UNSPAMDIR}/cur/`; do
        ${CAT} ${UNSPAMDIR}/cur/$mail | ${DSPAM} --client --user ${USER} --class=innocent --source=error
        ${MV} ${UNSPAMDIR}/cur/$mail ${MYMAILDIR}/inbox/cur
done

Ce script va donc aller dans le maildir 'spam' et le maildir 'unspam' pour repasser ces mails à Dspam. Dans le cas des 'false positive' (un spam qui est passé pour innocent) on lui dit que ce sont des spams il va ensuite déplacer chaque spam dans le maildir 'spamed'. Dans le cas des 'false negative' (un innocent qui était pas au bon endroit au bon moment) on lui dit que ce sont des mails innocents et les déplacer dans votre inbox.

On peut maintenant rajouter les crontabs :

# clean dspam tables every afternoon
0       15      *       *       *       root    /usr/local/bin/mysql -udspam -pdspamrulez dspam < /usr/local/share/examples/dspam/mysql/purge.sql
# clean dspam pg tables every afternoon
0       15      *       *       *       pgsql   /usr/local/bin/psql -Udspam -pdspamrulez dspam < /usr/local/share/examples/dspam/pgsql/purge.sql

# train dspam
15,30,45,0      *       *       *       *       ange    /usr/local/bin/bash /home/ange/train_dspam.sh

# clean spam, twice a week
0       16      *       *       2,5       ange    /usr/local/bin/bash /home/ange/clean_spam.sh

# get mails every 5 minutes
10,20,30,40,50,0        *       *       *       *       ange    /usr/local/bin/fdm -f /home/ange/.fdm.conf -l -u ange fetch
5,15,25,35,45,55        *       *       *       *       ange    /usr/local/bin/fdm -f /home/ange/.fdm.conf -l -u ange fetch

Le script “clean_dspam.sh” ne fait que vider le maildir spamed.

Final

En principe, vous avez quelque chose de fonctionnel : fdm récupère les mails toutes les 5 minutes, dspam traite les mails, mutt nous permet de lire, écrire nos mails, de reclasser les mails mal classés et un script nous permet d'enseigner à Dspam à séparer l'ivraie du bon grain. Si vous n'êtes pas un habitué de Dspam vous constaterez qu'il est plutôt rapide au niveau de l'apprentissage (2 heures chez moi, une 20aine de spams et 4 ou 5 mails innocents à retrier).

1) A propos des questions de choix “religieux”, si vous avez quelque remarque ou commentaire à faire à ce sujet merci de les faire parvenir à votre /dev/null il en fera meilleur usage que moi. En clair : je vous propose une méthode, avec mes outils et ma vision des choses vous êtes nullement obligés de le lire et d'en accepter la totalité : vous êtes libres, tout comme je le suis de faire ce genre de choix. Merci de votre attention.
2) imap, imaps, pop, pops, nntp, …
3) avec le nom du user, le mode d'apprentissage, le fait qu'il doit nous retourner tous les mails : innocents et spam, et de faire ça en mode client et sur le stdout
freebsd/fdm_dspam_mutt.txt · Last modified: 2010/01/12 13:29 (external edit)