Blog hébergé par Yves et Iris :-)

Aller au contenu | Aller au menu | Aller à la recherche

Utilisateurs virtuels et réels dans le même domaine avec Exim et Courier

Mon serveur familial ne servait jusqu’à présent que des utilisateurs réels, c’est à dire que chacun y avait un compte, propriétaire de fichiers et autorisé à exécuter des commandes, planifier des tâches, etc.

Je viens d’étendre le cercle des utilisateurs sur mon serveur, pour la messagerie uniquement pour l’instant. Dans ce contexte, je ne voulais pas créer des comptes Linux, ce qui aurait augmenté la surface d’attaque du serveur sans nécessité. J’ai donc introduit des utilisateurs virtuels pour la messagerie.

Les exemples que j’ai trouvés sur Internet visaient principalement à gérer de multiples domaines virtuels, avec exclusivement des utilisateurs virtuels, ou bien à gérer un domaine avec des utilisateurs réels et des domaines avec des utilisateurs virtuels. Mon souhait était plutôt d’ajouter des utilisateurs virtuels aux domaines que je gère déjà et qui contennaient déjà des utilisateurs réels. En m’inspirant de ce que j’ai lu, ça n’a pas été très compliqué.

This article has been translated to English.

Dans ce qui suit, je prends pour hypothèse que mon nom de domaine est example.org.

Authentification

L’authentification se basait jusqu’à présent exclusivement sur PAM (le mécanisme d’authentification système de Linux) ; Courier (IMAP) le faisait via le service courierauthdaemon, Exim (SMTP) via le service saslauthd.

Pour les besoins de Courier, j’ai d’abord modifié la configuration dans /etc/courier/authdaemonrc ; la partie en gras ci-dessous correspond à ce qui a été ajouté :

authmodulelist="authpam authuserdb"
authmodulelistorig="authuserdb authpam authpgsql authldap authmysql authcustom authpipe"
daemons=2
authdaemonvar=/var/run/courier/authdaemon
DEBUG_LOGIN=0
DEFAULTOPTIONS=""
LOGGEROPTS=""

Puis il a fallu créer la base des utilisateurs ; par exemple, pour un utilisateur fictif nommé newuser :

userdb newuser set uid=8 gid=8 home=/var/mail/virtual/example.org/newuser mail=/var/mail/virtual/example.org/newuser
userdbpw -md5 | userdb newuser set systempw
makeuserdb
mkdir -p /var/mail/virtual/example.org/newuser/{cur,tmp,new}
chown -R mail:mail /var/mail/virtual

Les utilisateurs ainsi créés n’ayant pas d’existence du point de vue du système d’exploitation, j’ai choisi de les mettre sous le contrôle de l’utilisateur mail (uid=8, gid=8), créé par défaut par Debian.

Après avoir redémarré le service courierauthdaemon, il est possible de tester cette étape avec la commande authtest (ce que je tape est en gras) :

authtest 'newuser' 'good password'
Authentication succeeded.

Authenticated: newuser (uid 8, gid 8)
Home Directory: /var/mail/virtual/example.org/newuser
Maildir: /var/mail/virtual/example.org/newuser
Quota: (none)
Encrypted Password: $1$7zFoadsH$/BkKSkhoMrQ2yJ1GCi993.
Cleartext Password: good password
Options: (none)

authtest 'newuser' 'bad password'
Authentication FAILED: Operation not permitted

Au niveau d’Exim, l’idéal est de bénéficier de la même base d’utilisateurs. Cela est possible de deux manières : soit en ouvrant le fichier des utilisateurs de courierauthdaemon (/etc/courier/userdb.dat), soit en s’adressant directement au service courierauthdaemon via sa « socket ». J’ai choisi la seconde solution, tout d’abord parce que cela garantit la même authentification en IMAP et en SMTP même si je modifie plus tard la configuration de courierauthdaemon, mais aussi parce que l’accès direct au fichier me paraissait moins propre, moins pérenne et moins fiable. Dans /etc/exim4/conf.d/auth/30_exim4-config_examples , j’ai désactivé l’ancienne méthode d’authentification et j’ai activé celle-ci à la place :

plain_courier_authdaemon:
driver = plaintext
public_name = PLAIN
server_prompts = :
server_condition = \
${extract {ADDRESS} \
{${readsocket{/var/run/courier/authdaemon/socket} \
{AUTH ${strlen:exim\nlogin\n$auth2\n$auth3\n}\nexim\nlogin\n$auth2\n$auth3\n} }} \
{yes} \
fail}
server_set_id = $auth2
.ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS
server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}
.endif

Attention : la ligne en gras ci-dessus, qui définit server_prompts, est importante pour permettre l’authentification de la plupart des clients, bien que cette ligne soit absente du fichier par défaut de Debian.

Pour que cette méthode d’authentification fonctionne, il faut qu’Exim soit autorisé à accéder à la « socket » de courierauthdaemon. Après vérification, il a suffit d’exécuter la commande suivante :

chmod o+x /run/courier/authdaemon

Néanmoins, pour que ce changement persiste après un redémarrage du serveur, il a aussi fallu appliquer au script de démarrage /etc/init.d/courier-authdaemon le petit changement que voici :

ancienne ligne : mkdir -m 0750 $rundir
nouvelle ligne : mkdir -m 0751 $rundir

Le service Exim a pu alors être redémarré.

Pour tester cette configuration, il faut accéder au serveur en SMTP depuis un client quelconque d’Internet, mais pas un client de confiance (pour lequel aucune authentification ne serait exigée). Depuis un compte SSH dont je dispose sur Internet, j’ai donc effectué le test suivant avec base64 et gnutls-cli (ce que je tape est en gras) :

printf 'newuser\0newuser\0good password\0' | base64
bmV3dXNlcgBuZXd1c2VyAGdvb2QgcGFzc3dvcmQA

gnutls-cli -s example.org -p 25 --insecure
[…]
220 example_host ESMTP Exim 4.84 Fri, 05 Jun 2015 23:44:37 +0200
EHLO client.org
250-example_host Hello you at client.org [111.222.33.44]
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-STARTTLS
250 HELP
STARTTLS
220 TLS go ahead
[Control]+[D]
*** Starting TLS handshake
[…]
EHLO client.org
250-example_host Hello you at client.org [111.222.33.44]
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-AUTH PLAIN
250 HELP
AUTH PLAIN bmV3dXNlcgBuZXd1c2VyAGdvb2QgcGFzc3dvcmQA
235 Authentication succeeded
QUIT
221 example_host closing connection
- Peer has closed the GnuTLS connection

Il faut refaire ce test en mettant un mauvais nom d’utilisateur, puis en mettant un mauvais mot de passe ; ces deux tests-ci doivent échouer. Il faut également tester des utilisateurs déjà existants (réels) pour s’assurer de l’absence de régression.

Acheminement du courrier : Exim

L’acheminement du courrier se passe en trois phases dans Exim :

  1. des vérifications de type « ACL » sont d’abord réalisées au fur et à mesure du déroulement du protocole SMTP (EHLO, AUTH, MAIL FROM, RCPT TO) ;
  2. si le courrier a été accepté, il est routé, c’est à dire qu’il est acheminé vers son destinataire en prenant l’adresse inscrite dans l’en-tête comme point de départ et en y appliquant divers traitements (/etc/aliases, .forward, procmail…) ;
  3. enfin, le courrier est transporté à la destination déterminée, qui peut être une boîte mbox ou Maildir, mais aussi une commande qui va lire le courrier sur son entrée standard, ou un tube nommé (« pipe »), ou même un serveur distant…

La prise en charge des utilisateurs virtuels dans la première phase n’a nécessité aucune adaptation autre que celle de l’authentification (déjà vue) ; en effet, mes nouveaux utilisateurs font partie des mêmes domaines que les utilisateurs réels et sont soumis aux mêmes règles.

Dans la seconde phase, il a fallu créer un nouveau routage car les utilisateurs virtuels ne sont pas reconnus de la même manière que les utilisateurs réels et n’ont pas leurs données enregistrées selon le même schéma. Auparavant, les routages configurés étaient les suivants (ce que je tape est en gras) :

ls -1 /etc/exim4/conf.d/router
00_exim4-config_header
100_exim4-config_domain_literal
150_exim4-config_hubbed_hosts
200_exim4-config_primary
300_exim4-config_real_local
400_exim4-config_system_aliases
500_exim4-config_hubuser
600_exim4-config_userforward
700_exim4-config_procmail
800_exim4-config_maildrop
850_exim4-config_lowuid
900_exim4-config_local_user
mmm_mail4root

Ces routages sont exécutés séquentiellement, jusqu’à ce que l’un d’entre eux accepte le destinataire de manière finale. Mes nouveaux utilisateurs n’ayant pas de répertoire personnel, j’ai considéré que les outils comme procmail ne les concernaient pas ; par contre, je souhaite leur permettre de disposer d’alias (je n’ai cependant pas testé cet aspect). J’ai donc inséré mon nouveau fichier entre 400… et 500…, sous le nom /etc/exim4/conf.d/router/450_exim4-config_vusers :

vusers:
debug_print = "R: vusers for $local_part@$domain"
driver = accept
domains = dsearch;/var/mail/virtual
local_parts = dsearch;/var/mail/virtual/$domain
transport = maildir_vusers

Ceci donne pour instruction à Exim d’accepter les destinataires dont le domaine est une entrée existante du répertoire /var/mail/virtual et dont le nom d’utilisateur (théoriquement après prise en compte des alias) est une entrée de ce sous-répertoire ; ces destinataires doivent alors être transportés par maildir_vusers (troisième phase du traitement), qui reste à créer.

Auparavant, les transports configurés étaient les suivants (ce que je tape est en gras) :

ls -1 /etc/exim4/conf.d/transport
00_exim4-config_header
10_exim4-config_transport-macros
30_exim4-config_address_file
30_exim4-config_address_pipe
30_exim4-config_address_reply
30_exim4-config_maildir_home
30_exim4-config_maildrop_pipe
30_exim4-config_mail_spool
30_exim4-config_procmail_pipe
30_exim4-config_remote_smtp
30_exim4-config_remote_smtp_smarthost
30_exim4-config_remote_smtp_smarthost.rul
35_exim4-config_address_directory

Mon transport ressemble globalement à maildir_home ; je me suis donc inspiré de ce dernier pour créer mon nouveau fichier /etc/exim4/conf.d/transport/30_exim4-config_maildir_vusers :

maildir_vusers:
debug_print = "T: maildir_vusers for $local_part@$domain"
driver = appendfile
directory = /var/mail/virtual/$domain/$local_part
user = mail
group = mail
.ifdef MAILDIR_HOME_CREATE_DIRECTORY
create_directory
.endif
.ifdef MAILDIR_HOME_CREATE_FILE
create_file = MAILDIR_HOME_CREATE_FILE
.endif
delivery_date_add
envelope_to_add
return_path_add
maildir_format
.ifdef MAILDIR_HOME_DIRECTORY_MODE
directory_mode = MAILDIR_HOME_DIRECTORY_MODE
.else
directory_mode = 0700
.endif
.ifdef MAILDIR_HOME_MODE
mode = MAILDIR_HOME_MODE
.else
mode = 0600
.endif
mode_fail_narrower = false
# This transport always chdirs to $home before trying to deliver. If
# $home is not accessible, this chdir fails and prevents delivery.
# If you are in a setup where home directories might not be
# accessible, uncomment the current_directory line below.
current_directory = /var/mail/virtual

Après avoir redémarré Exim, cette configuration a pu être testée avec la commande exim4 (ce que je tape est en gras) :

exim4 -d+route -bt me@example.org 2>&1 | less
[…]
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Testing me@example.org
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[…]
--------> vusers router <--------
local_part=me domain=example.org
checking domains
search_open: dsearch "/var/mail/virtual"
search_find: file="/var/mail/virtual"
key="example.org" partial=-1 affix=NULL starflags=0
[…]
lookup yielded: example.org
example.org in "dsearch;/var/mail/virtual"? yes (matched "dsearch;/var/mail/virtual")
checking local_parts
search_open: dsearch "/var/mail/virtual/example.org"
search_find: file="/var/mail/virtual/example.org"
key="me" partial=-1 affix=NULL starflags=0
[…]
lookup failed
me in "dsearch;/var/mail/virtual/example.org"? no (end of list)
vusers router skipped: local_parts mismatch
[…]
--------> local_user router <--------
local_part=me domain=example.org
checking domains
cached yes match for +local_domains
cached lookup data = NULL
example.org in "+local_domains"? yes (matched "+local_domains" - cached)
checking local_parts
me in "! root"? yes (end of list)
[…]
routed by local_user router
envelope to: me@example.org
transport: maildir_home
search_tidyup called
>>>>>>>>>>>>>>>> Exim pid=13811 terminating with rc=0 >>>>>>>>>>>>>>>>
me@example.org
router = local_user, transport = maildir_home

exim4 -d+route -bt newuser@example.org 2>&1 | less
[…]
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Testing newuser@example.org
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[…]
--------> vusers router <--------
local_part=newuser domain=example.org
checking domains
search_open: dsearch "/var/mail/virtual"
search_find: file="/var/mail/virtual"
key="example.org" partial=-1 affix=NULL starflags=0
[…]
lookup yielded: example.org
example.org in "dsearch;/var/mail/virtual"? yes (matched "dsearch;/var/mail/virtual")
checking local_parts
search_open: dsearch "/var/mail/virtual/example.org"
search_find: file="/var/mail/virtual/example.org"
key="newuser" partial=-1 affix=NULL starflags=0
[…]
lookup yielded: newuser
newuser in "dsearch;/var/mail/virtual/example.org"? yes (matched "dsearch;/var/mail/virtual/example.org")
[…]
routed by vusers router
envelope to: newuser@example.org
transport: maildir_vusers
search_tidyup called
>>>>>>>>>>>>>>>> Exim pid=13861 terminating with rc=0 >>>>>>>>>>>>>>>>
newuser@example.org
router = vusers, transport = maildir_vusers

C’est tout pour Exim, et il n’y a rien d’autre à faire pour Courier, qui récupère les informations dont il a besoin depuis courierauthdaemon (notamment le répertoire contenant le courrier).

Attention : le fait de router les utilisateurs virtuels avant de router les utilisateurs réels, pour le même domaine, implique qu’il faut bien veiller à ne pas créer un répertoire /var/mail/virtual/example.org/me (donc pour un utilisateur réel). Si cela arrivait, cet utilisateur serait considéré comme virtuel bien qu’il existe sur le système.

Voici les articles qui m’ont aidé à comprendre et progresser :

Mises à jour :

  • 2015-06-14 — J’ai finalement supprimé l’authentification par saslauthd puisque courierauthdaemon est de toutes façons indépendant des services IMAP de Courier. Par ailleurs, la vérification de la valeur retournée par la « socket » est maintenant plus sure, grâce à une astuce que m’a rapportée henk sur IRC (#exim@freenode.net). Enfin, assurer l'accès à la « socket » était en fait moins simple que prévu.
  • 2015-10-07 — Correction de l’authentification au niveau des « prompts » pour améliorer la compatibilité.

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

La discussion continue ailleurs

URL de rétrolien : http://yalis.fr/cms/index.php/trackback/75

Fil des commentaires de ce billet