Mise en place de la RFC8314 sur ma chaîne de courrier électronique🔗

Posted by Médéric Ribreux 🗓 In blog/ Sysadmin/

#email #tls #auto-hébergement #mutt #rfc #security

Introduction

Cela fait maintenant de nombreuses années que je gère ma propre plate-forme publique Internet et par la même occasion ma propre plate-forme de courrier électronique. D'une manière générale, je suis assez content de ce que j'ai mis en place.

C'est souvent le cas sur le courrier électronique, au moins sur la partie serveur. Les logiciels sont "vieux" matures, ils ont fait leur preuve. Les protocoles évoluent peu. Une fois que la chaîne est en place, à part suivre les correctifs de sécurité et les évolutions majeures des logiciels serveurs, il n'y a pas grand-chose à faire et c'est tant mieux.

Ceci dit, le courrier électronique continue à évoluer dans le temps, notamment à l'initiative du renforcement de la sécurité et de la protection de la vie privée. C'est le cas avec la RFC8314 qui vient un peu bousculer la manière de gérer une plate-forme de courrier électronique. Cela faisait quelque temps que je l'avais vue passer et, après avoir trouvé un peu de temps et un prétexte pour revoir la configuration de mon serveur public, j'ai essayé de la mettre en œuvre.

Cet article a pour but d'illustrer les principaux changements que j'ai intégrés sur ma plate-forme de courrier électronique pour me conformer du mieux que possible à la RFC8314.

Qu'est-ce-qu'il y a dans cette RFC ?

Dans les faits, la RFC est plutôt courte en contenus spécifiques. Pour résumer sérieusement, elle indique qu'il faut faire en sorte de chiffrer au maximum tous les canaux d'accès clients à un serveur SMTP (un MTA).

Pour rappel un MTA peut faire grosso modo deux choses:

La RFC8314 s'attaque au deuxième point. Elle stipule qu'il est maintenant temps de passer au tout chiffré pour la communication entre tous les composants serveurs de la plate-forme (MTA/MDA comme Dovecot) et les clients. Pour ce faire, deux ports réseaux sont dédiés à ces communications:

Les accès non chiffrés sont à proscrire ainsi que les accès en STARTTLS. Cette dernière technique permettait d'initier une communication chiffrée à partir d'un port en clair. Le début de la conversation réseau était néanmoins en clair. C'est ce que j'avais mis en place sur ma plate-forme de courrier électronique car c'était plus simple pour moi d'avoir un seul port pour chaque service (143 pour IMAP+STARTTLS et 25 pour SMTP+STARTTLS).

De plus, cette RFC lève une grosse ambiguïté sur le port SMTP à utiliser pour les clients. Jusque-là, on avait le choix entre le port 25 (qui sert également pour le traffic MTA à MTA), le port 587 (pour les clients en clair et en STARTTLS) et le port 465 pour l'accès complet TLS. Ce dernier n'était qu'un standard de fait, pas un truc obligatoire et, à une époque donnée, déconseillé. Le port IANA 465 a même été affecté à un autre protocole même si de nombreux administrateurs de MTA continuaient à l'utiliser pour leurs clients.

Pour le coup, la RFC clarifie vraiment les choses. Néanmoins, elle permet de conserver certaines anciennes configurations, histoire de gagner du temps. Néanmoins, ce n'est pas l'approche que j'ai retenue. En effet, tant qu'à faire de faire une modification de configuration, autant y aller franco et se mettre dans les clous tout de suite. De plus, comme j'ai très peu de clients à gérer, la migration est assez simple.

La RFC8314 ne s'adresse qu'aux communications clients MTA et pas à celles entre MTA car, si en règle générale, un administrateur de MTA peut avoir un contrôle sur les clients de sa plate-forme, ce n'est pas vrai du tout pour les autres MTA. Certains peuvent très bien ne pas savoir utiliser de canal chiffré, ou ne pas vouloir (enfin en 2019, cette affirmation tient plus de la paresse que de l'écueil technique).

Le trafic de MTA continuera donc à se faire sur le port 25, que ce soit en clair ou en STARTTLS.

Résumé de ce que nous devons faire

Ouverture des ports au niveau du pare-feu

Nous allons commencer avec le pare-feu. En effet, ça ne sert à rien de configurer un truc si on ne peut pas le tester. Pour ma part, j'ai juste ajouté les ports TCP 465 et 993 et retiré le port TCP 143 étant donné que je vais supprimer l'accès IMAP en clair.

Voici mon fichier de configuration de nftables (/etc/nftables.conf):

#!/usr/sbin/nft -f

# Configuration NFT de serveur

# On vide toutes les règles
flush ruleset

# Table pour un serveur public un peu fermé
table inet public_server {
  # Chaîne de gestion de tout ce qui rentre
  chain entree {
	# Chaîne de type filtre, en entrée
	type filter hook input priority 0;

	# Accepter tout ce qui vient de l'interface loopback
	meta iif lo accept

	# Accepte le trafic qui vient de la machine
	ct state established,related accept

	# Réponse au ping IPv4
	ip protocol icmp accept

	# OUverture de i2pd
	meta skuid i2pd accept

	# Ouverture des ports publics
	tcp dport { 25,53,80,443,465,993 } accept
	udp dport { 53 } accept

	# Compter et journaliser ce qui est rejeté
	counter log prefix "Connexion entrante rejetée: " level warn drop
  }

  # Chaîne de gestion de tout ce qui sort
  chain sortie {
	type filter hook output priority 0;

	# On accepte les sorties vers l'interface loopback
	meta oif lo accept

	# Réponse au ping IPv4
	ip protocol icmp accept

	# Ouverture pour i2pd
	meta skuid i2pd accept

	# Ouverture des ports publics
	tcp sport { 25,53,80,443,465,993  } accept
	udp sport { 53 } accept

	# Ouverture des ports externes classiques utilisés par le système
	tcp dport { 25,53,80,143,443,465,993,995 } accept
	udp dport { 53 } accept

	# count and drop any other traffic
	counter log flags skuid prefix "Connexion sortante rejetée: "
	level warn drop
  }
}

Je vous invite à le mettre en service au moment où vous terminez les configurations des différents logiciels serveurs histoire de minimiser l'arrêt de service (lancé en l'état, le port 143 peut encore être utilisé par des clients).

Configuration d'Exim4

Introduction

Pour comprendre ce qu'il faut faire, mieux vaut d'abord lire la documentation à ce sujet.

Autre point à noter au cas où: la version d'Exim4. Pour ma part, j'utilise le backport de Debian Stretch, soit la version 4.92-8 qui est la même que celle de Debian Buster (la nouvelle distribution stable de Debian au moment de la rédaction de cet article). Je peux donc utiliser la documentation du dessus.

Autre chose à savoir, Exim4 sous Debian utilise GNUTls et non openssl. Cela a une incidence sur la configuration.

Enfin, comme si cela n'était pas déjà assez compliqué, Debian a une manière particulière de configurer Exim4. Pour ma part, j'ai choisi la configuration éclatée. Ce qui suit concerne ce type de configuration. Néanmoins, vous pouvez vous en inspirer pour faire vos modifications.

Préparation d'un fichier de paramètres Diffie-Hellman digne de ce nom

Les bonnes pratiques de sécurité recommandent d'utiliser une meilleure taille de clef dans les échanges Diffie-Hellman. En général, la taille des échanges de clef est limitée à 2048. Pour ma part, je préfère la monter à 4096 octets grâce à la commande suivante:

openssl dhparam 4096 > dh.pem

Attention, la génération de ce fichier prend pratiquement 1h sur ma station de travail (de 2012).

Ensuite, vous pouvez utiliser ce fichier dans Exim4 (/etc/exim4/dh.pem) et également dans Dovecot (remplacer /etc/dovecot/dh.pem). Ce fichier peut être public.

Configuration TLS d'Exim4

La première des choses, c'est de modifier la configuration principale d'Exim (le main). Sous Debian, on trouve essentiellement conf.d/main/03_exim4-config_tlsoptions pour gérer ces éléments de configuration.

### main/03_exim4-config_tlsoptions
#################################

# TLS/SSL configuration for exim as an SMTP server.
# See /usr/share/doc/exim4-base/README.Debian.gz for explanations.

.ifdef MAIN_TLS_ENABLE
# On affiche qu'on souhaite avertir tout le monde qu'on fait du TLS.
.ifndef MAIN_TLS_ADVERTISE_HOSTS
MAIN_TLS_ADVERTISE_HOSTS = *
.endif
tls_advertise_hosts = MAIN_TLS_ADVERTISE_HOSTS

# Configuration des clefs/certificats TLS
.ifdef MAIN_TLS_CERTKEY
tls_certificate = MAIN_TLS_CERTKEY
.else
.ifndef MAIN_TLS_CERTIFICATE
MAIN_TLS_CERTIFICATE = CONFDIR/exim.crt
.endif
tls_certificate = MAIN_TLS_CERTIFICATE

.ifndef MAIN_TLS_PRIVATEKEY
MAIN_TLS_PRIVATEKEY = CONFDIR/exim.key
.endif
tls_privatekey = MAIN_TLS_PRIVATEKEY
.endif

# On indique le fichier de paramétrage de Diffie-Hellman
.ifdef _HAVE_GNUTLS
tls_dhparam = /etc/exim4/dh.pem
tls_dh_max_bits = 4096
.endif

# On ajoute le port submissions
tls_on_connect_ports = 465

# On active le port submissions en plus du smtp simple
daemon_smtp_ports = 25 : 465

.else
# Si l'option MAIN_TLS_ENABLE n'est pas activée, on n'affiche rien.
tls_advertise_hosts =
.endif

Une fois rédigé, il reste à renseigner les valeurs des macros sus-citées (ce qui est en majuscule). Pour ma part, je place tout dans conf.d/main/000_localmacros:

# on active TLS:
MAIN_TLS_ENABLE = true

# Emplacements des fichiers de clef pour TLS:
MAIN_TLS_CERTIFICATE = /etc/exim4/keys/medspx.fr.cert.pem
MAIN_TLS_PRIVATEKEY = /etc/exim4/keys/medspx.fr.pkey.pem

Les fichiers de clefs mentionnés ci-dessus sont des liens symboliques vers une configuration Let's Encrypt.

Gestion de l'authentification

Pour ma part, je n'ai pas eu grand-chose à faire si ce n'est relire ma conf existante et considérer qu'elle était suffisante, à un petit détail près. En effet, pour l'authentification, je n'ai activé que le pilote d'authentification PLAIN. Depuis le début, je fais du STARTTLS et j'ai bien sûr indiqué que la commande SMTP AUTH ne serait présentée (et activée) uniquement lorsque la communication est chiffrée (par STARTTLS ou par TLS en direct).

Néanmoins, je souhaite aussi désactiver l'authentification en dehors du port dédié à ça (le port 465). Tout ce que j'ai trouvé de bon est d'utiliser la directive server_advertise_condition et de la faire tester que le port utilisé sur le serveur est le port 465. En pratique, ça donne ces quelques lignes dans le fichier conf.d/auth/20_exim4-config_virtual:

### auth/20_exim4-config_virtual
#################################

# Configuration de l'authentification avec un compte virtuel

plain_server:
  driver = plaintext
  public_name = PLAIN
  server_condition = "${if crypteq{$auth3}{\\\{md5\\\}${extract{1}{:}{${lookup{$auth2}lsearch{CONFDIR/passwd}{$value}{fail}}}}}{1}{0}}"
  server_set_id = $auth2
  server_prompts = :
  server_advertise_condition = ${if eq{$tls_in_cipher}{} {}{${if eq{$received_port}{465} {*}{}}} }

L'astuce consiste à indiquer dans la partie de vérification de la présentation de l'option AUTH une double condition: si la connexion est chiffrée (par TLS ou STARTTLS), alors on regarde si le port utilisé est 465, dans ce cas, on renvoie '*', dans les 2 autres cas, on ne renvoie rien, ce qui a pour effet de ne pas présenter l'option AUTH et donc d'invalider toute tentative d'authentification.

On aurait pu désactiver complètement STARTTLS sur le serveur mais c'est un mécanisme de chiffrement utilisé aussi par les serveurs MTA qui vous relaient un message (et sur lesquels, vous avez peu d'impact).

Si vous lisez la variable server_condition, vous vous rendrez compte que ma méthode de gestion des comptes authentifiés est pourrie (mais j'assume). En effet, le fichier d'authentification se nomme /etc/exim4/passwd et il contient un truc du genre:

nom_du_compte:hash_md5_du_mot_de_passe

Pour générer le hash MD5 du mot de passe, il suffit de faire:

$ echo -n the_harsh_password | md5sum

C'est la méthode de stockage de mots de passe que j'utilise depuis longtemps et elle a fait ses preuves, notamment parce qu'elle se passe de tout lien entre le compte utilisé dans Exim et le compte de l'utilisateur sur le système. Ok, MD5 en 2019… Oui, j'avais la flemme de changer tous les comptes !

Remise en service

Un simple systemctl restart exim4 devrait suffire. Pour vérifier que le service est fonctionnel sur le bon port, vous pouvez utiliser ss:

# ss -tlp

Configuration de Dovecot

Planification

Maintenant, passons à Dovecot. Nous voulons supprimer l'écoute sur le port 143, activer l'écoute sur le port 993 (imaps) et également indiquer d'utiliser TLSv1.2 ainsi que l'utilisation de Diffie-Hellman un peu partout.

Pour ma part, j'utilise la version 2.3 de Dovecot (il y a eu des évolutions entre la 2.2 et la 2.3 sur TLS notamment).

Gestion des ports

Pour cette partie, il suffit d'éditer le fichier /etc/dovecot/conf.d/10-master.conf:

# On désactive le protocole IMAP sur le port réservé
service imap-login {
  inet_listener imap {
    port = 0
  }

  # On active le service IMAPS
  inet_listener imaps {
    port = 993
    ssl = yes
  }

  # Nombre de connexions clientes simultanées max avant kill du processus.
  service_count = 10
}

# On désactive complètement POP3
service pop3-login {
  inet_listener pop3 {
    port = 0
  }
  inet_listener pop3s {
    port = 0
    #ssl = yes
  }
}

Implémentation de TLSv1.2

Tout se passe dans le fichier /etc/dovecot/conf.d/10-ssl.conf:

##
## SSL settings
##

# On force l'utilisation de TLS par défaut
ssl = required

# Le chemin vers les clefs/certificats pour le chiffrement
ssl_cert = </etc/letsencrypt/live/medspx.fr/fullchain.pem
ssl_key = </etc/letsencrypt/live/medspx.fr/privkey.pem

# Paramètres Diffie-Hellman
ssl_dh = </etc/dovecot/dh.pem

# Version minimum de TLS: v1.2
ssl_min_protocol = TLSv1.2

# Liste des ciphers autorisés
ssl_cipher_list = ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH

# On indique qu'il faut	suivre l'ordre des ciphers du serveur
ssl_prefer_server_ciphers = yes

# On supprime la compression TLS (qui peut poser problème)
ssl_options = no_compression

Remise en service

Un simple systemctl restart dovecot devrait suffire. Pour vérifier que le service est fonctionnel sur le bon port, vous pouvez utiliser ss:

# ss -tlp

Annonces DNS

Il reste à faire les annonces de services sur le DNS. Pour ma part, je n'ai eu à ajouter que peu de choses: les deux lignes qui suivent…

_imaps._tcp 84600 IN SRV 0 5 993 medspx.fr.
_submissions._tcp 84600 IN SRV 0 5 465 medspx.fr.

La première concerne imaps sur le port 993, la seconde le service de soumission client mail sur le port 465. Pour ce dernier, le nom du service est submissions avec un s pour indiquer que ce service est en TLS direct.

Pensez également à supprimer les anciennes déclarations des ports qui ne sont maintenant plus accessibles.

Configuration des clients

msmtp

J'utilise mstmp pour faire remonter les messages de courrier électroniques des serveurs vers mon compte (humain) afin d'avoir des informations en cas de problème.

Voici un fichier de configuration global pour msmtp (/etc/msmtprc):

# Configuration par défaut
defaults
aliases /etc/aliases
auth on
tls on
tls_starttls off

# Définition du compte
account medspx.fr
domain medspx.fr
host medspx.fr
port 465
from jerk@medspx.fr
user jerk
password password

# Activation du compte par défaut
account default : medspx.fr

La phrase importante ici c'est tls_starttls. En effet, par défaut, lorsqu'on active TLS dans msmtp, celui-ci essaye de faire du STARTTLS. Or, nous avons du TLS implicite (par défaut). Il faut donc désactiver STARTTLS et c'est ce que fait la variable tls_starttls à off.

Mutt

Pour Mutt, il suffit de remplacer les url du type imap: par imaps: et smtp par smtps, pardi !

Voici un exemple de configuration (~/.muttrc):

set smtp_url = "smtps://mederic.ribreux@medspx.fr/"
set spoolfile = "imaps://medspx.fr/INBOX"
set folder = "imaps://medspx.fr/"

Attention, Mutt utilise la libsasl2 pour gérer l'authentification SMTP (et bizarrement, pas pour IMAP). Or, par défaut cette dernière a besoin de modules pour gérer l'authentification en mode PLAIN. Vous devez donc installer le paquet libsasl2-modules.

Thunderbird

De ce côté, rien à signaler, il suffit de modifier les protocoles STARTTLS en SSL/TLS purs. Ça marche, y compris pour la version ESR de Thunderbird !

Roundcube

J'utilise Roundcube et j'ai dû modifier l'URL de connexion pour la partie IMAP. Néanmoins, ce n'est pas suffisant. En effet, je souhaite que le trafic entre Roundcube et le serveur IMAP se fasse sur localhost et non en sortant sur le réseau. Le problème de passer par localhost, c'est que le certificat qui est utilisé par Dovecot prend en compte un CN qui est le FQDN du serveur et non localhost.

Pour contourner le problème, on peut utiliser la directive de configuration 'imap_conn_options':

$config['default_host'] = 'imaps://localhost:993';
$config['imap_conn_options'] = array(
  'ssl' => array(
    'peer_name'  => 'FQDN of server'
  ),
);

Pareil pour la partie SMTP:

$config['smtp_server'] = 'ssl://localhost';
$config['smtp_conn_options'] = array(
  'ssl' => array(
	'peer_name'  => 'FQDN of server'
  ),
);

$config['smtp_port'] = 465;

Conclusions

Ouf, c'est fini et ça semble fonctionner. Encore une fois, Exim4 est riche de fonctionnalités mais reste complexe à configurer. J'ai dû passer environ 2 ou 3h de recherches pour vraiment comprendre et trouver comment faire. Et encore, je n'ai pas touché à la gestion des algos de chiffrement pour TLSv1.2. En même temps, je ne fais pas ça tous les jours…

Du côté de Dovecot, ça se passe plus facilement. Seule la génération de la conf TLS prend un peu de temps pour comprendre.

Du côté de la configuration des clients, j'ai eu aussi quelques surprises, notamment pour Mutt (à cause du SASL) et pour Roundcube (ce qui m'a valu quelques bannissements par fail2ban).

Cette RFC fait quand même du bien aux sysadmins de la chaîne de courrier électronique. En effet, elle clarifie avec précision ce qu'il faut désormais faire. Auparavant, il subsistait un problème avec le standard de fait (donc pas un standard) du port 465. J'ai toujours refusé de le mettre en place sur ma plate-forme de courrier électronique à cause de ce manque de clarification.

Maintenant, tout est (à peu près) correct et je suis reparti pour 2 ou 3 ans de paix avec mon courrier électronique qui arrive à destination et mes clients qui communiquent maintenant uniquement sur canal chiffré.

Références