Deploy a personal email server

Si les emails sont quelque chose de courant sur internet, la mise en place d'un serveur d'emails n'est pas quelque chose de trivial. On trouve donc de nombreux tutoriaux sur le net, plus ou moins fiable, plus ou moins complets et plus ou moins obsolètes. Chacun ayant une manière de faire et ses logiciels préférés, voici la mienne.

Les pré-requis

Ce guide suppose que vous :

  • ayez les droits d'administration sur une machine tournant sous un OS de type UNIX ;
  • possédiez un nom de domaine ;
  • sachiez modifier les entrées DNS associées à ce nom de domaine ;
  • sachiez ce que sont des certificats X.509 (improprement appelés certificats SSL) ;
  • sachiez utiliser votre OS (en particulier, savoir gérer les démons, paquets, ports, …) ;
  • compreniez qu'en fonction de votre distribution l'emplacement exact des fichiers peut changer ;
  • soyez en mesure de faire des recherches par vous même ;
  • ne recopiez pas bêtement ce que vous voyez.

Préparer le terrain : les DNS

Afin d'avoir un serveur email accessible, il est important d'ajouter un champ MX à notre nom de domaine. On va le faire pointer sur un sous domaine qui, lui, disposera des entrées A et AAAA.

example.org.      IN  MX 10 mx1.example.org.
mx1.example.org.  IN  A     203.0.113.42
mx1.example.org.  IN  AAAA  2001:db8::42

Avant d'aller plus loin, vous devez vérifier si votre nom de domaine dispose d'un FCrDNS valide. Pour faire court, il faut que les requêtes de recherche inverse du DNS retournent le nom de domaine associé.

$ dig +short -x "203.0.113.42"
mx1.example.org.
$ dig +short -x "2001:db8::42"
mx1.example.org.

Si ce n'est pas le cas, corriger votre configuration DNS.

SPF et DMARC

Afin de luter contre l'usurpation d'identité il existe plusieurs système. Deux d'entre eux sont SPF et DMARC. Pour ceci, il suffit d'ajouter des entrées TXT dont le contenu définit la politique de protection des emails dont la provenance est votre domaine. Exemple:

example.org.             IN  TXT  "v=spf1 a mx ip6:2001:db8::/32 -all"
_dmarc.example.org.      IN  TXT  "v=DMARC1;adkim=s;aspf=s;p=reject;sp=reject;ruf=mailto:postmaster@example.org;fo=1"

Le MTA

Le MTA (Mail Transfer Agent) est le logiciel qui va vous permettre d'envoyer et recevoir des emails. Il en existe beaucoup (qmail, exim, postfix, sendmail, …), ici nous nous focaliserons sur un MTA qui a l'avantage d'être fiable, complet et surtout extrêmement simple à configurer : OpenSMTPD. Attention toutefois, ce guide nécessite la version 6.4 d'OpenSMTPD. Cette version n'est pas très répandue sur GNU/Linux à cause de sa dépendance à LibreSSL, contrairement aux versions antérieurs qui acceptaient OpenSSL 1.0.

Une fois installé, la configuration s'effectue dans /etc/mail/smtpd.conf. Un simple man 5 smtpd.conf est là pour nous aider. Pour avoir un serveur email capable de recevoir des emails pour le domaine example.org, nous n'avons besoin que de ceci :

listen on 127.0.0.1
listen on ::1

listen on 203.0.113.42
listen on 2001:db8::42

table aliases "/etc/smtpd/aliases"

action local_deliver maildir alias <aliases>

match action local_deliver
match from any for domain "example.org" action local_deliver

Nous allons maintenant la rendre un peu plus robuste et fonctionnelle en utilisant TLS, et en autorisant l'envoie d'emails pour les utilisateurs.

pki example.org cert "/etc/ssl/private/example.org.pem"
pki example.org key  "/etc/ssl/private/example.org.key"

# SMTP port 25 from localhost
listen on 127.0.0.1    tls pki example.org hostname mx1.example.org
listen on ::1          tls pki example.org hostname mx1.example.org

# SMTP port 25 from external sources
listen on 203.0.113.42 tls pki example.org hostname mx1.example.org
listen on 2001:db8::42 tls pki example.org hostname mx1.example.org

# SMTPS port 465 from authenticated users
listen on 203.0.113.42 smtps pki example.org auth hostname mx1.example.org mask-src
listen on 2001:db8::42 smtps pki example.org auth hostname mx1.example.org mask-src

table aliases "/etc/smtpd/aliases"

action local_deliver maildir alias <aliases>
action relay_out relay helo example.org

match action local_deliver
match from any for domain "example.org" action local_deliver
match from any auth for any action relay_out

La clé privée ne doit pas être accessible (lecture, écriture, exécution), sauf pour le propriétaire. Le mieux est de lui mettre un chmod 600 ou chmod 400.

Ports

Pensez à accepter les nouvelles connexions TCP entrantes sur les ports 25 (SMTP) et 465 (SMTPS).

Les alias

Dans notre cas, seuls les utilisateurs de la machine peuvent recevoir des emails. Quel que soit votre MTA, vous voudrez donc certainement configurer des alias. Au minimum, on redirige les emails de root sur un compte utilisateur et on configure également certains alias connus. Pour ceci, éditons /etc/smtpd/aliases :

#
# Please run `smtpctl update table <table_name>` after any change to this file.
#

# Person who should get root's mail. Don't receive mail as root!
root:           your_username

# Basic system aliases
MAILER-DAEMON:  postmaster
postmaster:     root

# General redirections for pseudo accounts
bin:            root
daemon:         root
nobody:         root
decode:         root

# Well-known aliases
manager:        root
dumper:         root
operator:       root
abuse:          root
admin:          root
webmaster:      root
hostmaster:     root
spam:           root

# Personal aliases

Après chaque modification de ce fichier, on doit exécuter la commande smtpctl update table aliases.

Un petit point sur la distribution d'emails

Il est possible de stocker les emails de quelqu'un sous un grand nombre de formes. Les plus fréquentes sont :

  • maildir: les emails sont stockés dans le répertoire personnel d'un utilisateur, en général ~/Maildir
  • mbox: les emails sont stockés dans le répertoire /var/mail

Dans le cas présent, nous avons utilisé maildir et alons continuer ainsi. Ce dossier sera automatiquement créé s'il n'existe pas.

Shitlist

Certains administrateurs sans scrupules n'hésitent pas à envoyer des emails en masse sans demander l'avis des destinataires et ne prennent pas en compte les demandes de retrait des listes d'envoi. Sans parler du spam malicieux, il peut s'agir de publicités ou autre démarchage commercial envoyés par des entreprises légitimes. Les domaines de provenance ne changeant pas beaucoup, il est aisé de créer une liste (opensmtpd-shitlist) et de refuser les emails provenant de ces domaines. Une fois le fichier récupéré, l'intégration à OpenSMTPD est simple :

table shitlist "/etc/smtpd/shitlist"
match from any mail-from <shitlist> for any reject

Récupérer ses emails

Pour le moment, les emails sont placés dans notre répertoire ~/Maildir. Afin de récupérer les emails depuis notre MUA (Mail User Agent, par exemple ThunderBird ou mutt) il nous faut mettre en place un serveur POP ou IMAP. Nous utiliseront ici Dovecot. Pour la configuration, pensez à lancer doveconf -nP afin d'obtenir tous les paramètre qui sont définis à autre chose que la valeur par défaut. Dans la configuration suivante j'ai :

  • activé IMAP mais pas POP ;
  • renforcé la sécurité en paramétrant SSL/TLS correctement ;
  • ajouté le support de sieve.
# 2.3.6 (7eab80676): /etc/dovecot/dovecot.conf
# Pigeonhole version 0.5.6 (92dc263a)
# OS: Linux 4.19.53-1-lts x86_64
# Hostname: localhost.localdomain
auth_verbose = yes
listen = 203.0.113.42, 2001:db8::42
mail_debug = yes
mail_location = maildir:~/Maildir
managesieve_notify_capability = mailto
managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext editheader imapflags notify
namespace inbox {
  inbox = yes
  location =
  mailbox Drafts {
    special_use = \Drafts
  }
  mailbox Junk {
    special_use = \Junk
  }
  mailbox Sent {
    special_use = \Sent
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
  mailbox Trash {
    special_use = \Trash
  }
  prefix =
  separator = .
}
passdb {
  driver = pam
}
plugin {
  sieve = ~/.dovecot.sieve
  sieve_dir = ~/sieve
  sieve_extensions = +notify +imap4flags +imapflags +editheader
}
postmaster_address = postmaster@example.org
protocols = imap sieve lmtp
ssl = required
ssl_cert = </etc/ssl/private/example.org.pem
ssl_cipher_list = HIGH:!aNULL:!eNULL:!LOW:!MEDIUM:!EXP:!RC4:!3DES:!MD5:!SHA1:!PSK:!kRSA:!SRP:-AES128:-DH:+ECDH
ssl_dh = </etc/dovecot/dh.pem
ssl_key = </etc/ssl/private/example.org.key
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yes
userdb {
  driver = passwd
}
protocol lmtp {
  mail_plugins = " sieve"
}
protocol lda {
  mail_plugins = " sieve"
}

Attention au certificat, pensez à utiliser quelque chose de mieux que ceux qui sont auto-générés. Remarquez que nous n'accepterons qu'au minimum TLSv1.2, les versions antérieures n'étant pas recommandées. TLS 1.2 n'est lui-même plus trop recommandé, dans l'idéal, utilisez uniquement TLS 1.3. Malheureusement, Dovecot ne permet pas actuellement de spécifier TLS 1.3 comme version minimale.

Notez également que le paramétrage TLS interdit les échange de clés Diffie-Hellman traditionnels mais autorise cependant la variante basée sur les courbes elliptiques. Il n'y a donc pas besoin de s'inquiéter des paramètres par défaut qui sont en général très faibles. En revanche, si vous autorisez à nouveau le Diffie-Hellman traditionnel, vous devriez générer des paramètres corrects à l'aide la commande openssl dhparam -outform PEM -out "/etc/dovecot/dh.pem" 4096.

Ports

Pensez à accepter les nouvelles connexions TCP entrantes sur le port 993 (IMAPS).

Le MDA

Si un MTA est capable d'envoyer et de recevoir des emails, le MDA (Mail Delivery Agent) est le logiciel qui distribue les emails reçus à son destinataire. La plupart des serveurs SMTP font à la fois MTA et MDA, ce qui est le cas d'OpenSMTPD. Nous lui avons d'ailleurs indiqué de délivrer les emails à leur destinataire dans son répertoire au format maildir.

Un autre MDA

Utiliser le MDA intégré à notre serveur SMTPD fonctionne, mais ne nous donne malheureusement pas de possibilités de configuration. À la place, nous allons donc utiliser celui fourni par dovecot grâce à LMTP. Ce MDA a l'avantage de gérer sieve.

OpenSMTPD

Du côté d'OpenSMTPD, il suffit d'éditer notre action local_deliver afin de lui donner l'adresse du socket utilisé par LMTP :

action local_deliver lmtp "/run/dovecot/lmtp" alias <aliases>

L'adresse du socket peut varier suivant les systèmes.

Filtrer ses emails

Nous pouvons maintenant utiliser sieve pour filtrer nos emails. La configuration se fait dans le fichier ~/.dovecot.sieve de chaque utilisateur. Un exemple de configuration :

require ["fileinto", "imapflags"];

if anyof(header :contains "x-spam-level" "****", header :contains "x-fose-spam" "This message appears to be spam.") {
  setflag "\\Seen";
  fileinto "Junk";
}
elsif address :is ["From", "To", "Cc", "Reply-to"] "nantes@lists.afpy.org" {
  fileinto "INBOX.Python.Nantes";
}
elsif address :is ["From", "To", "Cc", "Reply-to"] "python@example.org" {
  fileinto "INBOX.Python";
}

Cet exemple permet de marquer le spam comme lu et de le placer dans le dossier qui leur est dédié. Il y a ensuite des filtres pour placer automatiquement des emails dans certains dossiers.

DKIM

DomainKeys Identified Mail (DKIM) est une autre technique pour luter contre l'usurpation d'identité. Le principe est de signer (au niveau du serveur) les emails envoyés et de mettre la clé publique associée dans une entrée DNS. Par exemple, à la réception d'un email provenant de whatever@example.org, une requête DNS sur un sous domaine de example.org permet de récupérer la clé publique permettant de vérifier la signature de l'email. Si tout concorde, l'email à bien été envoyé par un serveur possédant la clé privée associée.

OpenDKIM est certainement l'implémentation la plus connue et utilisée de DKIM. Malheureusement, elle s'interface avec les MTAs en utilisant un milter, ce qui n'est pas supporté par OpenSMTPD. Nous utiliserons donc une autre solution, moins connue : dkimproxy

Signature des emails envoyés

Comme son nom l'indique, dkimproxy agit comme un proxy SMTP. On lui envoie un email en SMTP par un port et il le retourne, signé, sur un autre port. Pour sa configuration, nous supposerons que la configuration de dkimproxy se trouve dans le répertoire /etc/dkimproxy au lieux de celui par défaut.

# mkdir -p "/etc/dkimproxy/private"
# chown dkimproxy:root "/etc/dkimproxy/private"
# chmod 700 "/etc/dkimproxy/private"
# cd "/etc/dkimproxy/private"
# openssl genrsa -out "private.key" 2048
# openssl rsa -in "private.key" -pubout -out "public.key"
# chown dkimproxy:root "private.key" "public.key"
# chmod 600 "private.key"

Et dans notre /etc/dkimproxy/dkimproxy_out.conf :

# specify what address/port DKIMproxy should listen on
listen    127.0.0.1:10027

# specify what address/port DKIMproxy forwards mail to
relay     127.0.0.1:10028

# specify what domains DKIMproxy can sign for (comma-separated, no spaces)
domain    example.org

# specify what signatures to add
signature dkim(c=relaxed)
signature domainkeys(c=nofws)

# specify location of the private key
keyfile   /etc/dkimproxy/private/private.key

# specify the selector (i.e. the name of the key record put in DNS)
selector  pubkey

Ajoutez ensuite une entrée TXT pour pubkey._domainkey.example.org dont la valeur est v=DKIM1; k=rsa; t=s; p=contenu_de_la_clef_publique (la liste des options est définie dans la RFC 6376).

Attention, pour les clés RSA de 2048 bits ou plus la clé publique dépassera la longueur maximale d'une une chaîne de caractères (255 bytes) dans les entrées TXT et SPF. Comme indiqué dans la RFC 4408, il faut alors utiliser plusieurs chaînes de caractères qui seront concaténées.

pubkey._domainkey.example.org.  IN  TXT  "v=DKIM1; k=rsa; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEjPmxQHTJeBoHSJPuPMl0ry0q31TqoC2OuqUiVHSk3hM6x6oDat4pIischVYi/3ODsjBC6wg1BPgtfdVfzboAF/bQK+Kh0rBaQlv2vloP96mu6dmUGMJSo5BrtoAMdx6DbMe7s7TP58uAUknQNEL6s9w7iNF9gD/+yBiEB+yDYwIDAQAB"

Enfin, configurons OpenSMTPD pour lui faire utiliser dkimproxy.

listen on lo port 10028 tag DkimOut

action to_dkimproxy relay host smtp://127.0.0.1:10027
action relay_out relay helo example.org

match from any tagged DkimOut for any action relay_out
match for any action to_dkimproxy
match from any auth for any action to_dkimproxy

Anti-spam

Comme anti-spam, j'utilise pour le moment Amavis. Ce dernier écoute sur le port 10024 et retourne l'email sur le port 10025 après ajout des headers relatif au spam.

listen on lo port 10025 tag Clean

action to_amavisd relay host smtp://127.0.0.1:10024

match from any tag Clean for domain <domains> action local_deliver
match from any for domain <domains> action to_amavisd

Le MUA

La configuration de votre client de messagerie s'effectue comme tel :

Serveur entrant

  • Nom/adresse: votre nom de domaine
  • Protocole: IMAP
  • Sécurité: SSL/TLS
  • Port: 993
  • Méthode d'authentification: mot de passe
  • Nom d'utilisateur: votre nom d'utilisateur (pas d'alias ni email complet)
  • Mot de passe: celui de votre compte utilisateur

Serveur sortant

  • Nom/adresse: votre nom de domaine
  • Sécurité: SSL/TLS
  • Port 465
  • Méthode d'authentification: mot de passe
  • Nom d'utilisateur: votre nom d'utilisateur (pas d'alias ni email complet)
  • Mot de passe: celui de votre compte utilisateur

Récapitulatif

Réception d'un email

Réception d'un email

  1. OpenSMTPD accepte l'email venant de Bob ;
  2. OpenSMTPD passe l'email à Amavis afin de détecter les emails frauduleux ;
  3. Amavis rend l'email, à OpenSMTPD ;
  4. OpenSMTPD donne l'email à Dovecot via LMTP ;
  5. Dovecot dépose l'email dans le dossier ~/Maildir de Jean (non illustré : il utilise sieve) ;
  6. Jean consulte ses emails ;
  7. Dovecot va chercher le nouvel email en provenance de Bob ;
  8. Dovecot passe son email à Jean.

Émission d'un email

Émission d'un email

  1. Jean envoie son email à OpenSMTPD ;
  2. OpenSMTPD passe l'email à DkimProxy ;
  3. DkimProxy renvoie l'email à OpenSMTPD après l'avoir signé ;
  4. OpenSMTPD envoie l'email signé au destinataire.

Configuration d'OpenSMTPD

# This is the smtpd server system-wide configuration file.
# See smtpd.conf(5) for more information.


#
# Encryption
#

pki example.org cert "/etc/ssl/private/example.org.pem"
pki example.org key  "/etc/ssl/private/example.org.key"


#
# Listening
#

# SMTP port 25 from localhost
listen on 127.0.0.1 tls pki example.org hostname mx1.example.org
listen on ::1       tls pki example.org hostname mx1.example.org

# SMTP port 25 from external sources
listen on 203.0.113.42 tls pki example.org hostname mx1.example.org
listen on 2001:db8::42 tls pki example.org hostname mx1.example.org

# SMTPS port 465 from authenticated users
listen on 203.0.113.42 smtps pki example.org auth hostname mx1.example.org mask-src
listen on 2001:db8::42 smtps pki example.org auth hostname mx1.example.org mask-src

# signed by dkimproxy
listen on lo port 10028 tag DkimOut

# filtered by amavisd
listen on lo port 10025 tag Clean


#
# Tables
# If you edit a file, you have to run "smtpctl update table <table_name>"
#

table aliases "/etc/smtpd/aliases"
table shitlist "/etc/smtpd/shitlist"


#
# Actions
#

action local_deliver lmtp "/run/dovecot/lmtp" alias <aliases>
action to_amavisd    relay host smtp://127.0.0.1:10024
action to_dkimproxy  relay host smtp://127.0.0.1:10027
action relay_out     relay helo example.org


#
# Matches
#

# Reject emails from banned domains
match from any mail-from <shitlist> for any reject

# Deliver local emails
match action local_deliver
match for domain "example.org" action local_deliver
match from any auth for domain "example.org" action local_deliver

# Deliver filtered emails
match from any tag Clean for domain "example.org" action local_deliver

# Send unfiltered emails to amavisd
match from any for domain "example.org" action to_amavisd

# Relay outgoing emails when signed
match from any tag DkimOut for any action relay_out

# Send outgoing unsigned emails to dkimproxy
match for any action to_dkimproxy
match from any auth for any action to_dkimproxy

Tests

Certains outils sont là pour vérifier certaines parties de votre configuration :

Autres emails avec des auto-réponses pour tester DKIM et SPF

Pour vérifier que les emails que vous envoyez soient bien signés et que vous avez correctement défini votre SPF, il suffit d'envoyer un email à check-auth@verifier.port25.com (IPv6 + IPv4).

Support de l'IPv6

Pour vérifier si votre serveur supporte bien l'IPv6, envoyez un email à test@doesnotwork.eu et attendez la réponse automatique.

Liens