Changelog

2014-11-10
Publication initiale
2014-12-03
Correction à la variable mail_location dans /etc/devocot/10-mail.conf.
2014-12-26
Ajout d’un lien vers mxtoolbox.com.

Introduction

Voici un guide pour l’installation et la configuration d’un serveur SMTP personnel.

Je crois qu’Internet doit demeurer un réseau libre, neutre et décentralisé. Cet article représente une contribution concrète en ce sens. J’ai pris en charge un service que j’utilise plutôt que nourir les forces de la mercatique, de la surveillance de masse et de la propagande. Je veux propager mon expérience afin d’encourager le plus grand nombre à faire vivre l’essence d’Internet : chaque humain connecté contribue à la valeur du réseau pour tous, à l’opposé d’être de simples consommateurs.

Ce guide se veut un aperçu des composantes requis pour installer, configurer et gérer son propre service de messagerie électronique, ainsi qu’une collection d’astuces. L’opinion que gérer un serveur SMTP soit difficile et chronophage est répendue. J’ai trouvé pourtant que la plus grande part du temps consacré au projet était investi dans la recherche de documentation, qui est très éparpillée. Après des semaines de recherches, j’avais un plan mental de tous les éléments requis et j’ai pu écrire une liste d’actions. La mise en pratique s’est faite en une fin de semaine, avec des ajustements mineurs et en continu par la suite. J’espère que ce document vous permettra d’économiser beaucoup de temps.

Dans cet esprit de partage de connaissances, si vous avez des questions ou des suggestions, n’hésitez pas à m’écrire à alexandre@deverteuil.net.

Vue d'ensemble du setup

Deux machines composent mon infrastructure de messagerie. Un serveur léger qui consomme peu d’énergie et une machine principale qui n’est pas allumée en tout temps.

Le serveur

On va utiliser Exim comme serveur mail. Le format de stockage par défaut mbox répond à nos besoins. Le serveur POP3 Dovecot servira à récupérer nos messages reçus sur le serveur vers notre machine principale. Pour pouvoir communiquer par courriel lorsque je ne suis pas à la maison, j’ai installé SSH et Mutt. Je peux donc lire le courrier reçu récemment et envoyer des messages. Pour accéder à mes messages archivés, j’allume mon ordinateur principal avec Wake-on-LAN à partir de mon serveur.

Notre machine principale

Cette machine roule sa propre instance d’Exim. Celle-ci reçoit les messages récupérés par Fetchmail et les livre dans mon maildir, et transmet au serveur les messages envoyés à l’aide du MUA — le serveur les relaye à son tour à mon FAI. Nous allons également installer Spam Assassin pour le filtrage de pourriels. Le format de stockage utilisé est maildir. Je donnerai quelques informations spécifiques à Mutt, mais le MUA n'est pas le sujet central de cet article.

Concept prérequis

Assurez-vous de distinguer les concepts suivants :

Je vous recommande de vous familiariser avec le protocole SMTP en envoyant des courriels manuellement avec telnet <hôte> 25<hôte> est l’adresse du relai SMTP de votre FAI. L’article Wikipedia Simple Mail Transfer Protocol contient un bon exemple à imiter. Consultez aussi mon article Sending email with openssl and smtp.gmail.com.

Ajoutez ces pages à vos marques-pages :

Le matériel et l’accès à Internet

On a besoin du port 25 entrant et sortant. Si vous êtes avec Vidéotron, et probablement si vous êtes avec Bell aussi, le port 25 entrant est bloqué et le port 25 sortant est bloqué sauf à destination du relais SMTP du FAI. Si vous êtes au Québec, je vous recommande AEI Internet car avec eux, tous les ports sont débloqués (même le 80) et leur prix est juste.

Comme alternative pour expérimenter, vous pouvez utiliser une machine virtuelle Linode. Mon serveur roule sur un Raspberry Pi modèle B qui consomme 1,5 W en idle, 3 W lorsque le CPU est sollicité à 100 %. Vu la capacité de stockage est limitée sur le Pi, je télécharge mes messages sur ma machine principale avec POP3.

Note concernant l’uptime : le RFC 5321 stipule qu’il est attendu d’une application SMTP considérée pleinement capable (fully-capable) qu’elle supporte la mise en file et la retransmission des messages. Si un serveur SMTP n’est pas joignable, l’émetteur doit réessayer après un délai d’attente (habituellement 30 minutes). Les tentatives continuent jusqu’à la transmission du message, ou l’abandon, typiquement au bout de 4 ou 5 jours. En cas d’échec, le serveur doit retourner un avis de courrier non remis, sauf si le message était lui-même un message d’erreur. En conclusion, l’uptime n’est pas un enjeu critique justifiant un serveur redondant et un UPS.

DNS dynamique

Pour annoncer au Monde comment acheminer du courrier vers votre serveur, vous devez créer un enregistrement MX auprès de votre registraire DNS. Dans la plupart des cas, vous n’aurez pas d’adresse IP fixe à votre domicile, ce qui complique la configuration DNS. La solution s’appelle le DNS dynamique. Voici les éléments requis :

Un compte chez un fournisseur de DNS dynamique gratuit

Je recommande FreeDNS. Configurez un client DNS dynamique qui mettra à jour l’adresse IP de votre machine sur le serveur DNS périodiquement. Dans mon cas, j’utilise le client intégré à dd-wrt.

Un nom de domaine

Je vous recommande les services de NearlyFreeSpeech.net pour enregistrer votre nom de domaine. C’est avant tout un hébergeur web mais vous n’êtes pas obligé d’y avoir un site web. J’aime leur éthique commerciale et leur politique de facturation « à l’usage ».

Créez un enregistrement MX qui pointe vers le serveur dynamique. Par exemple, si vous tapez la commande dig deverteuil.net mx, vous verrez la réponse 10 alexdev.chickenkiller.com.. La commande dig alexdev.chickenkiller.com donnera mon adresse IP du moment assignée par mon FAI.

Bonus httpd : si vous voulez héberger votre propre site web, au lieu d’un enregistremen A, créez un enregistrement CNAME qui pointe sur votre nom de domaine dynamique.

Le serveur mail

Les instructions données ici sont spécifiques à Arch Linux et Arch Linux ARM mais vous pourrez probablement transposer ça assez facilement dans une autre distribution GNU/Linux.

J’ai décidé d’utiliser Exim pour mon serveur SMTP. C’est vraiment bien documenté, fonctionnel et stable.

pacman -S exim

Machine allumée 24/7 qui reçoit le courrier externe

Je vous donne ici le résumé des éditions que j’ai faites au fichier de configuration. Les commentaires présument une familiarité avec la structure du fichier de configuration Exim et avec ses concepts de router, transports, etc. Lisez les commentaires fournis dans le fichier de configuration par les développeurs d’Exim.

# Pour pouvoir utiliser use_envelope_from dans mutt.
untrusted_set_sender = *

primary_hostname = <mon_hostname>
domainlist local_domains = @:deverteuil.net:alexdev.chickenkiller.com:localhost:localhost.localdomain
hostlist relay_from_hosts = <; 127.0.0.1 ; ::1 ; 192.168.0.0/16
# Mon préfixe de routage est de 16 bits (/16) et non 24 parce que j’ai
# plusieurs sous-réseaux bridgés par VPN. Pour accepter l’adresse IPv6,
# le séparateur est changé pour le point-virgule avec « <; »
# en début de liste (manuel Exim §6.20).


# Commenter le router dnslookup et décommenter le router smarthost.
# Il n’est plus pratique dans l’internet d’aujourd’hui de se connecter
# au serveur de destination sans passer par un relai. Vous pouvez essayer
# le routeur dnslookup mais vos messages risquent d’être retardés par des
# graylists, ou refusés.
smarthost:
  driver = manualroute
  domains = ! +local_domains
  transport = remote_smtp
  route_data = mail.aei.ca
  ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1
  no_more

Modifiez le fichier /etc/mail/aliases :

# Person who should get root's mail
root: alex
# Mon adresse de courriel est différente de mon nom d’utilisateur Linux :
alexandre:alex

Validation

Vous pouvez utiliser le site mxtoolbox.com pour tester votre serveur et votre configuration DNS.

Machine principale

Ici, Exim est appelé par Mutt pour l’envoi de messages et par Fetchmail pour la livraison du courrier dans mon maildir.

Voici les modifications apportées au fichier /etc/mail/exim.conf :

# Pour pouvoir utiliser use_envelope_from dans mutt.
untrusted_set_sender = *


primary_hostname = <mon_hostname>
domainlist local_domains = @:localhost:localhost.localdomain
hostlist relay_from_hosts = <; 127.0.0.1 ; ::1 ; 192.168.0.0/16


# Dans la configuration du router userforward, décommenter la ligne :
  allow_filter
# …pour autoriser l’interprétation du fichier .forward
# sous la forme « Exim » (manuel Exim §22.3).
# Ajoutez également la ligne :
  directory_transport = address_maildir
# Le transport address_maildir est défini plus bas.


# Commenter le router dnslookup et décommenter le router smarthost.
# Cette machine envoie les message au serveur décrit dans la section
# précédente. Ainsi, s’il y a un changement à apporter dans la façon
# de relayer les messages par Internet, il n’y aura qu’une configuration
# à modifier.
smarthost:
  driver = manualroute
  domains = ! +local_domains
  transport = remote_smtp
  route_data = <mon_serveur_allumé_24/7>
  ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1
  no_more


# Dans la configuration du transport local_delivery,
# modifiez cette ligne :
  directory = /var/mail/$local_part
# …et ajoutez celle-ci :
  maildir_format


# Quelquepart dans la section transports, ajoutez :
address_maildir:
  driver = appendfile
  maildir_format
  delivery_date_add
  envelope_to_add
  return_path_add

Modifiez le fichier /etc/mail/aliases (comme pour le serveur) :

# Person who should get root's mail
root: alex
# Mon adresse de courriel est différente de mon nom d’utilisateur Linux :
alexandre:alex

Selon le principe de convention plutôt que configuration, les courriels vont dans /var/mail/username. Mais moi je préfère garder mes courriels dans des sous-répertoires de ~/var/mail parce que /home est sur une différente partition. Pour concilier ces deux systèmes, un lien symbolique suffit :

ln -s /var/mail/username ~/var/mail/INBOX

Dans mon .bashrc, je déclare la variable d’environnement MAILDIR=/home/alex/var/mail/INBOX pour Mutt. Voici les options de configuration concernant le stockage des messages dans mon fichier .muttrc :

set mbox_type        = maildir
set folder           = "/home/<votre_username>/var/mail"
set postponed        = "+brouillons"
set record           = "+INBOX"
set header_cache     = "~/.mutt/hcache"
set message_cachedir = "~/.mutt/cache/bodies"
set certificate_file = "~/.mutt/certificates"
mailboxes            = "+INBOX" "+mailinglist"

Le filtre antipourriel

Je trouve que SpamAssassin avec filtrage bayésien fonctionne plutôt bien. La configuration est un peu déficiente et éparpillée. Je suis sûr qu’une meilleure alternative existe et je suis ouvert aux suggestions.

Le filtre antipourriel est installé sur ma machine principale parce que tous mes messages sont conservés sur celle-ci et le classement stratégique des messages (décrit plus bas) sert à entraîner le filtre bayésien.

Installer Spam Assassin

pacman -S spamassassin
sa-update
systemctl enable spamassassin
systemctl start spamassassin

Configurer Exim pour Spam Assassin

Dans /etc/mail/exim.conf, il suffit de décommenter les lignes suivantes dans la section acl :

    # Modifié par Alexandre 2014-08-09
    # Avec :true, toujours ajouter les entêtes.
    warn    spam       = nobody:true
            add_header = X-Spam_score: $spam_score\n\
                         X-Spam_score_int: $spam_score_int\n\
                         X-Spam_bar: $spam_bar\n\
                         X-Spam_report: $spam_report

Par défaut, Exim va faire passer les messages par le port 783 de la machine locale sur lequel écoute Spam Assassin. Avec le suffixe :true (manuel Exim, §43.3), je dis à Exim d’ajouter les entêtes du filtre antipourriel à tous les messages, même ceux qui ne sont pas considérés comme du spam. Je recommande ça, au moins pour une période de rodage.

Dire à Exim de relire sa configuration :

systemctl restart exim

Configurer Mutt

Afficher la valeur de l’entête X-Spam_score_int dans l’index des messages.

# .muttrc
spam "^X-Spam_score_int: (.*)" "%1"
set index_format      = "%4C %Z %D %-35.35L %?M?[%3M ]&(%4c)?*%4H* %s"
# La partie jaune place le score du filtre antipourriel entre astérisques dans
# l’index des messages. Exemple :
#   48     2014-11-04 16:36 DOUGLAS GREEN  (3.2K)* 407* High Piority Message.
# Ce message a un score de 40,7.

Activer le filtrage

Lorsque vous êtes prêts à activer le filtrage des pourriels, ajoutez ceci à votre fichier .forward :

# Exim filter  <-- Do not remove this line.

# Déplacer les pourriels dans ~/var/spam.
if
    "$h_X-Spam_score_int" is not below 30
then
    save /home/<votre_username>/var/mail/spam/
endif

J’ai également écris ce filtre qui place les messages provenant de listes de diffusion, si ça vous intéresse.

# Déplacer les messages de mailinglists dans ~/var/mailinglist.
if
    "$h_list-post $h_list-di $h_list-subscribe" matches "[^ ]"
then
    save /home/<votre_username>/var/mail/mailinglist/
endif

Filtrage bayésien

Le filtrage bayésien du spam est activé par défaut dans SpamAssassin. Cependant, il faut périodiquement « entraîner » Spam Assassin pour profiter de sa capacité d’adaptation. Cela se fait à l’aide de trois choses : la commande sa-learn, un script exécuté par cron, la séparation des messages ham et spam.

# crontab
30 * * * * /home/<votre_username>/bin/teach-spam
#!/usr/bin/bash
#
# ~/bin/teach-spam

set -o errexit -o pipefail

MAILDIR=/home/<votre_username>/var/mail
SA_OPTS="--no-sync"

cd $MAILDIR

for d in *
do
    case $d in
        INBOX|brouillons)
            # These are not processed.
            ;;
        spam)
            /usr/bin/vendor_perl/sa-learn $SA_OPTS --spam "$d" > /dev/null
            ;;
        *)
            /usr/bin/vendor_perl/sa-learn $SA_OPTS --ham "$d" > /dev/null
            ;;
    esac
done

/usr/bin/vendor_perl/sa-learn --sync > /dev/null

Le script est exécuté toutes les demi-heures et utilise efficacement les ressources. Spam Assassin se souvient des messages ayant déjà été traités. Le répertoire INBOX n’est pas traité; les messages seront promptement archivés dans les répertoires parcourus avec l’option --ham, éventuellement dans le répertoire spam (avec --spam) en cas de faux négatif. Je vérifie le répertoire spam périodiquement. Si un faux positif se trouve parmi les nouveaux messages, il suffit de le reclasser pour que Spam Assassin ajuste son filtre lors du prochain teach-spam. Ensuite, je marque tous les messages comme lus pour simplifier la prochaine vérification (dans Mutt : Shift+t, ~N, W, n).

Dovecot

Le serveur SMTP de mon infrastructure de messagerie roule sur un Raspberry Pi. Sa consommation d’énergie négligeable (1 W) est une caractéristique idéale pour un serveur allumé 24/7. Par contre, ses ressources en mémoire et en espace de stockage sont limitées. Je télécharge donc tous mes messages sur ma machine principale par le protocole POP3 avec le client Fetchmail et le serveur Dovecot. J’ai suivi les instructions sur l’article Dovecot sur le wiki d’Arch Linux pour installer et configurer le logiciel.

pacman -S dovecot

/etc/dovecot/conf.d/10.mail.conf :

mail_location = mbox:~/mail:INBOX=/var/spool/mail/%u

Créez le fichier /etc/pam.d/dovecot :

auth    required        pam_unix.so nullok
account required        pam_unix.so

Le paquetage dovecot contient un script pour générer un certificat SSL pour le serveur.

Le certificat /etc/ssl/certs/dovecot.pem et la clé /etc/ssl/private/dovecot.pem seront créés.

systemctl enable dovecot
systemctl start dovecot

Gmail et fetchmail

Maintenant que vous avez votre propre service de courrier, vous avez l’entière liberté de vous affranchir de la « commodité » de Gmail. Toutefois, il se peut que vous désiriez garder votre compte actif mais rapatrier tous vos messages sur votre machine. Je vais vous donner la procédure que j’ai suivie. Même les spams seront téléchargés parce qu’on veut nourrir le filtre bayésien de Spam Assassin.

Configurer Gmail

Dans la section Transfert et POP/IMAP des paramètres de votre compte Gmail, sélectionnez « Activer le protocole POP pour tous les messages (même ceux qui ont déjà été téléchargés) ». Même avec cette option activée, les pourriels ne seront pas téléchargés par le client POP et resteront dans le dossier Spam. Pour que tous les messages soient téléchargés, créez un filtre pour les messages dont la taille est supérieure à 0 octets (c.-à-d. tous les messages), et sélectionnez l'action « Ne jamais envoyer dans le dossier Spam ».

Configurer fetchmail

# ~/.fetchmail
poll pop.gmail.com protocol pop3 username votre.nom@gmail.com password 'password1' options ssl
poll <serveur_allumé_24/7> protocol pop3 username alex password 'password1' sslfingerprint '01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF'
Le fingerprint du certificat SSL peut être obtenu en exécutant la commande suivante sur le serveur :

$ openssl x509 -in /etc/ssl/certs/dovecot.pem -noout -fingerprint -md5

Google recommande une intervalle de 5 minutes entre les sessions POP3. On ajoute une tâche cron qui invoque fetchmail toutes les 5 minutes :

# crontab
*/5 * * * * fetchmail --silent --batchlimit 10

L’option --batchlimit 10 dit à fetchmail de limiter à 10 le nombre de messages transférées à Exim par session SMTP et d’initier plus de sessions si nécessaire. Par défaut, Exim transmet automatiquement les messages reçus au cours d’une session SMTP jusqu’à concurrence de 10, et met en file les messages suivants, retardant leur livraison (via le paramètre smtp_accept_queue_per_connection).

Conclusion

Ai-je oublié quelque chose? Aimeriez-vous que j’élabore sur un sujet en particulier? Avez-vous des difficultés avec votre propre installation?

Licence Creative Commons
Le guide SMTP d’Alexandre de Verteuil est mis à disposition selon les termes de la licence Creative Commons Attribution 4.0 International.