Solution de serveur de courrier électronique auto-hébergé (partie 5) : Aller plus loin avec Exim4 sous Debian.

Introduction:

Nous avons maintenant un MTA et un serveur IMAPS. Il nous reste plusieurs opérations à terminer sur Exim4. En effet, nous avons laissé le système dans un état certes stable mais où il manque de nombreuses fonctionnalités. Parmi celles-ci, nous allons étudier comment chiffrer les connexions par défaut ainsi que la gestion de l'authentification...

Gestion du chiffrement:

Par défaut, comme on peut le voir dans les indications précédentes, Exim4 n'est pas configuré pour gérer STARTLS. du moins, c'est vrai du côté serveur: en envoi, Exim4 se connecte préférentiellement par cette méthode pour déposer un message. Un coup d'oeil à la documentation officielle d'Exim4 sur le sujet nous présente quelques variables de configuration.

La méthode Debian est plus simple: jetez un coup d'oeil au fichier /etc/exim4/conf.d/main/03_exim4-config_tlsoptions et comptez les macros... Voici le résultat:

  • MAIN_TLS_ENABLE: indique qu'on gère les connexions TLS
  • MAIN_TLS_CERTIFICATE: nom du fichier de certificat du serveur
  • MAIN_TLS_PRIVATEKEY: nom du fichier de clef privée du serveur
  • MAIN_TLS_VERIFY_CERTIFICATES: emplacement des certificats d'AC pour la vérification des certificats des clients
  • MAIN_TLS_VERIFY_HOSTS: liste de machines qui doivent fournir un certificat vérifiable par les AC de la macro sus-citée
  • MAIN_TLS_TRY_VERIFY_HOSTS: liste de machines dont la vérification de certificat n'est pas bloquant pour la livraison des emails.

Pour notre action, il nous suffit de renseigner les 3 premières macros avec les bons fichiers. A noter qu'Exim4 doit pouvoir accéder en lecture à la clef privée. J'avais déjà une clef privée/publique pour ma machine et je l'ai réutilisée. Néanmoins, pour la clef privée, j'ai dû faire une copie accessible aux processus d'exim4 dans /etc/exim4:

chgrp Debian-exim medspx.fr.key
chmod 640 medspx.fr.key

Ensuite, il reste à renseigner nos trois macros et de redémarrer notre serveur.

# on active TLS:
MAIN_TLS_ENABLE = true

# Emplacements des fichiers de clef pour TLS:
MAIN_TLS_CERTIFICATE = /etc/ssl/certs/medspx.fr.crt
MAIN_TLS_PRIVATEKEY = /etc/exim4/medspx.fr.key

Si tout se passe bien, vous pouvez faire un test de connexion (telnet ou nc via le port 25) et vérifier que l'extension STARTTLS est bien activée. Ensuite, pour être vraiment sûr, lancez une procédure STARTTLS. En cas de problème, n'hésitez pas à consulter le fichier /var/log/exim4/mainlog qui remonte les erreurs TLS (bien souvent, exim n'arrive pas à lire la clef privée).

Mode Debug:

Parfois, lorsqu'on veut voir un peu ce qui cloche, il est bon de mettre exim4 en mode debug. Voici une ligne de commande adaptée pour voir ce qui se passe en dynamique:

exim4 -C /var/lib/exim4/config.autogenerated -d+all -bd -q30m

Pour l'utiliser, vous devez avoir arrêté le service exim4 (/etc/init.d/exim4 stop) Ainsi, vous pourrez mieux noter les problèmes de login/mot de passe lors de la partie suivante... en cas de problème bien sûr !

Gestion de l'envoi authentifié:

Par défaut, Exim4 ne permet pas l'envoi distant. Ainsi, il est impossible d'envoyer un message ayant comme adresse d'origine, celle d'un utilisateur valide du serveur sur lequel est installé Exim, depuis une machine distante par le protocole SMTP. En modifiant la configuration, il est toutefois possible de faire en sorte qu'Exim mette en oeuvre des mécanismes de vérification (authentification) qui permettent cette fonctionnalité bien intéressante. S'il est possible d'envoyer un message par SSH en direct sur la machine, c'est une tâche assez pénible et, bien souvent, on souhaite pouvoir envoyer des courriers électroniques depuis son MUA préféré (qui bien souvent est distant).

Les mécanismes de vérification sont gérés dans la section authenticators du fichier de configuration. Ils bénéficient d'éléments de configuration bien particuliers qui sont très différents et très nombreux. N'oublions pas que cette partie d'authentification est une extension du protocole SMTP (extension AUTH) et que cette dernière décrit plusieurs méthodes distinctes. On peut souhaiter s'authentifier par login/mot de passe en clair, ou bien en utilisant des fonctions de hashage (MD5) ou bien encore SASL. Tout dépend du contexte, du niveau de sécurité, de la capacité de gestion de la complexité, etc...

Dans notre cas qui est appliqué à mon environnement, nous souhaitons:

  • Mettre en oeuvre une authentification d'un utilisateur factice (celui utilisé pour Dovecot dans notre dernier article serait bien).
  • Si l'authentification ne se fait pas sur un canal chiffré, elle doit échouer.
  • L'authentification, même chiffrée, se basera sur au moins une fonction de hashage.
  • L'authentification ne se fera que sur un réseau local (celui de votre maison ou de votre structure de travail par exemple).

Généralité sur la configuration:

Nous ne devons pas oublier qu'Exim est également un client SMTP qui envoie (des emails) vers d'autres serveurs SMTP. La gestion de l'authentification joue donc dans les deux sens:

  • lorsqu'Exim est un serveur qui attend un client SMTP qui désire s'authentifier
  • lorsqu'Exim est un client qui tente d'envoyer un courriel vers un autre serveur imposant l'authentification (c'est le cas si vous utilisez par exemple Exim pour relayer des emails vers le service de courrier électronique de votre FAI.

Dans la suite, nous ne gèrerons que la partie "Exim serveur".

Restriction de l'authentification au réseau local:

Nous voulons restreindre l'authentification aux machines de notre réseau local (192.168.0.0/24). Ainsi, avec cette configuration, il sera impossible d'envoyer des emails en dehors de votre réseau local, même si votre serveur Exim est présent sur Internet. Pour mettre en place cette règle, il suffit de modifier la variable auth_advertise_hosts dans la partie générale de la configuration. Attention, n'oublions pas que nous souhaitons également masquer l'extension AUTH lorsqu'on est en dehors d'un canal chiffré.

En tenant compte du système de configuration éclaté de Debian, le plus simple consiste à ajouter un fichier /etc/exim4/conf.d/main/10_exim4-config_auth contenant:

auth_advertise_hosts = ${if eq{$tls_cipher}{}{}{192.168.0.0/24}}

La directive auth_advertise_hosts permet de définir quelles sont les machines qui se verrons présenter l'extension AUTH. La valeur de la directive indique que seules les machines dont l'adresse IP est dans le réseau local (192.168.0.0/24) et qui communiquent sur un canal chiffré (tls_cipher) se verrons présenter l'extension AUTH lors du dialogue SMTP avec le serveur Exim4.

Authentification serveur par CRAM-MD5:

On veut ici mettre en place un mécanisme d'authentification par CRAM-MD5 en réutilisant le hash du mot de passe contenu dans le fichier de mot de passe de Dovecot... Avant de lire plus avant et pour faire simple, je tiens à vous dire que c'est impossible ! En effet, le pilote cram-md5 d'Exim fonctionne de la manière suivante:

  • le client indique AUTH CRAM-MD5 pour lancer la procédure d'authentification par CRAM-MD5.
  • le serveur retourne une châine (le challenge).
  • le client compute le mot de passe donné, ajoute le challenge et redonne un hash MD5 codé en Base64.
  • le serveur récupère le mot de passe en suivant les commandes indiquées dans la variable server_secret.
  • ce mot de passe est un vrai mot de passe: ce n'est pas un hash !
  • ensuite, le serveur utilise le challenge, le mot de passe, fabrique un hash avec tout ça et compare avec le résultat renvoyé par le client.

Dans ce mécanisme, Exim a donc besoin du mot de passe en clair. On ne peut PAS le stocker sous forme de hash ! Ce n'est pas ce que nous voulons...

Il va nous falloir choisir entre stocker un mot de passe en clair et utiliser un autre mécanisme d'authentification. Les autres solutions qui restent simples sont les authentifications PLAIN et LOGIN. Elles ont le défaut de montrer les mots de passe en clair sur le réseau. Mais on peut s'assurer qu'elles passent sur un tunnel chiffré. Pour "améliorer" un peu notre solution et faire en sorte que rien ne soit stocké en clair, nous pouvons utiliser un hash MD5 et le comparer avec le Hash du mot de passe récupéré en clair. Dans ces conditions, il n'y a, à priori, rien qui est exposé en clair quelque part.

Nous devons également gérer le fait que c'est un compte non système qui doit servir à l'étape d'authentificaton. Il faut donc ne pas oublier cet élément dans notre configuration. Pour ma part, voici ce que j'ai ajouté en créant le fichier /etc/exim4/conf.d/auth/20_exim4-config_virtual et qui reprend ces critères:

Pour cela, vous devez

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_cipher}{}{}{*}}

La directive server_condition fait tout le travail: elle compare le hash md5 du mot de passe de la session SMTP du client (stocké dans la variable $auth3) avec la chaîne correspondant au compte de l'utilisateur ($auth2) renseignée dans le fichier CONFDIR/passwd. Ce dernier est un alias pour /etc/exim4/passwd et contiendra une ligne du type: login_utilisateur:hash_md5_mot_de_passe. Pour gérer l'alias, il suffit d'ajouter une ligne dans le fichier /etc/aliases qui reprend votre nom d'utilisateur non système et qui le lie à vrai compte système.

Autre solution: il existe un mécanisme d'authentification basé sur Dovecot. C'est un mécanisme SASL et il implique une petite modification de la configuration de Dovecot. De plus, sous Debian, il n'est disponible qu'avec le démon "lourd" d'exim (exim4-daemon-heavy). Pour bénéficier de ce traitement, un petit coup d'aptitude install exim4-daemin-heavy fera l'affaire. A noter que ce démon n'est pas très lourd (500Ko de binaires en plus rapport au client léger). Mais je n'ai pas testé cette solution...

cram_md5_dovecot:
  driver = dovecot
  public_name = CRAM-MD5
  server_socket = /var/run/dovecot/auth-client
  # notre utilisateur est virtuel, 
  server_set_id = $auth1

Vérification des champs Sender:

Maintenant que nous pouvons envoyer un email depuis un MUA distant, il nous manque une dernière étape de vérification. Celle du champ Sender:. En effet, à ce stade, il n'y a aucune raison pour vous interdire, alors que vous êtes authentifié, de mettre n'importe quoi dans le champ Sender: (adresse email de l'émetteur). Vous pouvez donc vous faire passer pour n'importe qui...

Sur un serveur avec peu d'utilisateurs, ce n'est pas crucial ! Mais, dans de nombreuses situations, il faut veiller à respecter l'adéquation client autorisé-champ Sender:.

Tout ceci se passe dans une ACL, ce qui va nous permettre de parler un peu plus de ces éléments de configuration. Vous pouvez lire le chapitre 40 de la spécification d'Exim4 pour obtenir des informations complètes...

Pour faire simple, une ACL est un mécanisme qui intervient à des moments divers du processus de gestion des courriers électroniques. Par exemple, une ACL est déclenchée lorsqu'un client envoie une commande SMTP (genre AUTH ou MAIL FROM ou RCPT, etc...). Comme dans les autres morceaux de configuration d'Exim, les ACL sont définis par un nom (libre) et sont affectées manuellement aux commandes SMTP évoquées plus haut, dans la partie principale de la configuration (main). Vous pouvez par exemple créer une ACL nommée ma_super_acl et l'affecter à la commande AUTH en écrivant:

acl_smtp_auth = ma_super_acl

En ce qui concerne le contenu d'une ACL, voici quelques règles à retenir:

  • Une ACL est formée de plusieurs "instructions"
  • Chaque instruction est formée par un et un seul verbe
  • chaque verbe est soumis à une ou plusieurs conditions et à un ou plusieurs modificateurs

Exemple d'ACL complète:

# on commence par définir le nom de l'ACL
ma_super_acl:

  # Début de la première instruction:
  # dont le verbe est accept (accept si toutes les conditions sont vérifiées)
  accept 
      # condition n°1: la liste des hôtes est vide (donc l'email est envoyé en local)
      hosts = :
  # modificateur n° 1:
  control = dkim_disable_verify

  # Début de la deuxième instruction:
  # dont le verbe est également accept
  accept
      # condition n°1: l'hôte fait partie de la liste des clients autorisés
      hosts = +relay_from_hosts
      control = submission/sender_retain
      control = dkim_disable_verify

Dans notre cas, on souhaite faire échouer tous les envois de messages dont le champ Sender: contient un nom d'utilisateur différent du login authentifié. Si l'authentification s'est déroulée correctement, le login de l'utilisateur est présent dans la variable globale $authenticated_id. Si le champ FROM ou Sender contient autre chose que cette variable complétée du domaine, alors il y a tentative d'usurpation d'identité. Du coup on rejete...

Pour faire simple, il faut modifier le fichier /etc/exim4/conf.d/acl/30_exim4-config_check_rcpt et y ajouter l'instruction deny suivante (dans la partie qui contient déjà l'instruction accept authenticated = * ):

# on rejete lorsqu'il y a différence
deny
   message = Champ Sender différent du login authentifié
   authenticated = *
   !senders = ${authenticated_id}@my_domain.org

 # s'il n'y a pas eu rejet, on continue (principe du verbe deny)
 # et on procède à l'envoi normalement
 accept
   authenticated = *
   control = submission
   control = dkim_disable_verify

J'ai mis le domaine en dur mais on peut bricoler grâce aux options de gestion des chaînes sous Exim...

Conclusion:

Nous disposons maintenant d'une véritable configuration fonctionnelle d'Exim4 qui nous permet de faire les choses "classiques" du courrier électronique. Dans la pratique, on voit bien qu'Exim4 est assez complexe "à pénétrer": il vous faudra bien en comprendre les concepts. Je n'ai indiqué ici qu'une partie de ces concepts: seuls ceux qui nécéssitaient une configuration différente de celle offerte par Debian ont été abordés. Sachez qu'il en existe d'autres qui sont sans doute plus importants: les routers et les transports. Ces éléments gèrent toute la chaîne de distribution du courrier électronique et permettent un très haut niveau de "customisation". Pour tout cela, il n'y a pas le choix: il faut lire la documentation officielle d'Exim (alias /usr/share/doc/exim4-base/spec.txt.gz ) !

Pour la prochaine fois, il nous reste à mettre en place le webmail...