19 avril 2024

Tuto Messagerie, partie III : Dovecot

Tutoriel révisé en décembre 2022 / Debian 11

Troisième partie du tutoriel consacré à l’installation d’un serveur de messagerie.

Nous avons le Ldap, Postfix, et bien passons maintenant à Dovecot ! Pour rappel, nous l’avons déjà installé dans la seconde partie.

Et au cas où :

# apt install dovecot-core dovecot-imapd dovecot-ldap dovecot-managesieved dovecot-sieve dovecot-lmtpd

 

I – La base

A – Utilisateur Vmail

Nous allons créer l’utilisateur virtuel vmail ainsi que le répertoire qui stockera les mails :

# groupadd -g 11111 vmail
# useradd -g vmail -u 11111 vmail -d /stockage/mail/vmail -m
# chown vmail: /stockage/mail/vmail -R
# chmod 770 /stockage/mail/vmail

Dans mon cas, ce repertoire de stockage est un partage Ceph. Le tutoriel est en cours…

B – Du ménage

Concernant la configuration de Dovecot, celui ci à la mauvaise idée de tout éclater dans une foultitude de fichiers.
Avant tout, faisons donc du tri…

# cd /etc/dovecot/conf.d
# rm 10-director 10-tcpwrapper 90-acl auth-*

et dans le répertoire /etc/dovecot :

# cd /etc/dovecot
# rm dovecot-dict-* dovecot-sql.conf.ext dovecot-ldap.conf.ext

C – De la config

On passe maintenant à la configuration. On va éditer le fichier /etc/dovecot/dovecot.conf et tout remplacer par :

!include_try /usr/share/dovecot/protocols.d/*.protocol
!include conf.d/*.conf

Oui, c’est concis, le reste sera dans le sous répertoire conf.d :

# cd /etc/dovecot/conf.d

Et la pour chaque fichier, remplacez tout son contenu par ce que je vous donne.

1 – Fichier 10-auth.conf
auth_cache_size = 0
auth_cache_ttl = 1 hour
auth_cache_negative_ttl = 1 hour
auth_mechanisms = plain
passdb {
  driver = ldap
  args = /etc/dovecot/dovecot-ldap-pass.conf.ext
}
userdb {
  driver = prefetch
}
userdb {
  driver = ldap
  args = /etc/dovecot/dovecot-ldap-user.conf.ext
}

Dans ce fichier, on déclare la façon dont Dovecot récupère les infos. On reviendra plus en détail dessus après…

2 – Fichier 10-logging.conf
#log_path = syslog
#debug_log_path =
#syslog_facility = mail
#auth_verbose = no
#auth_verbose_passwords = no
#auth_debug = no
#auth_debug_passwords = no
#mail_debug = no
#verbose_ssl = no

plugin {
  #mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
  # Available fields: uid, box, msgid, from, subject, size, vsize, flags
  # size and vsize are available only for expunge and copy events.
  #mail_log_fields = uid box msgid size
}

#log_timestamp = "%b %d %H:%M:%S "
#login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c
#login_log_format = %$: %s
#mail_log_prefix = "%s(%u): "

# %$ - Delivery status message (e.g. "saved to INBOX")
# %m - Message-ID
# %s - Subject
# %f - From address
# %p - Physical size
# %w - Virtual size
#deliver_log_format = msgid=%m: %$

Ici, auth_verbose et auth_debug sont bien pratiques en cas de soucis avec les connexions au LDAP, mail_debug pour les filtres Sieves (et autres, quota, etc…). Tout est commenté pour le moment, on décommentera si besoin.

3 – Fichier 10-mail.conf
mail_home = /stockage/mail/vmail/%d/%n
mail_location = maildir:~/mailbox

namespace inbox {
  separator = /
  inbox = yes
}

mail_uid = 11111
mail_gid = 11111
mail_privileged_group = vmail
first_valid_uid = 11111
last_valid_uid = 11111
first_valid_gid = 11111
last_valid_gid = 11111

mail_plugins = $mail_plugins

Emplacement des mails que je range en fonction du domaine et du nom. Ensuite,uig/gid de vmail… Rien de sorcier.

4 – Fichier 10-master.conf
mail_fsync = never

service imap-login {
  inet_listener imap {
    port = 143
  }
}
service imap {
  service_count = 64
  process_min_avail = 1
}
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    group = postfix
    mode = 0600
    user = postfix
  }
}
service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}
service auth-worker {
  user = vmail
}

Dans ce fichier, on déclare nos services : imap-login, imap, lmtp (utilisé pour le transfert de mails entre Postfix et Dovecot) et le service auth, qui service à Postfix pour authentifier les utilisateurs en SMTPS.

5 – Fichier 10-ssl.conf
ssl = required
ssl_cert = </etc/ssl/private/debugo.fr/fullcert.pem
ssl_cipher_list = ALL:!LOW:!SSLv2:!EXP:!aNULL
ssl_key = </etc/ssl/private/debugo.fr/key.pem
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yes

Le SSL, classique…

Note : Avant, la ligne était ssl_cert = </etc/ssl/private/debugo.fr/cert.pem, mais j’avais alors un UNABLE_TO_VERIFY_LEAF_SIGNATURE dans l’outil de gestion Sieve…

6 – Fichier 15-mailboxes.conf
namespace inbox {
  separator = /
  mailbox Drafts {
    auto = subscribe
    special_use = \Drafts
  }
  mailbox Junk {
    auto = subscribe
    special_use = \Junk
  }
  mailbox Trash {
    auto = subscribe
    special_use = \Trash
  }
  mailbox Sent {
    auto = subscribe
    special_use = \Sent
  }
  mailbox Archive {
    auto = subscribe
    special_use = \Archive
  }
}

La, on définit l’architecture de base de nos boites aux lettres. Création des répertoires spéciaux et auto souscription pour que l’utilisateur les voit de suite.

7 – Fichier 20-imap.conf
imap_idle_notify_interval = 30 mins

protocol imap {
  mail_max_userip_connections = 50
  mail_plugins = $mail_plugins imap_sieve
  postmaster_address = postmaster@debugo.fr
}

Configuration du protocole IMAP.

8 – Fichier 20-lmtp.conf
protocol lmtp {
  mail_fsync = optimized
  mail_plugins = $mail_plugins sieve
  postmaster_address = postmaster@debugo.fr
}

Configuration du protocole LMTP.

9 – Fichier 20-managesieve.conf
protocols = $protocols sieve

service managesieve-login {
  inet_listener sieve {
    port = 4190
  }
  service_count = 1
  #process_min_avail = 0
  #vsz_limit = 64M
}

Configuration de ManageSieve.

10 – Fichier 90-sieve.conf
plugin {
  sieve = file:~/sieve;active=~/.dovecot.sieve
  sieve_before = /etc/dovecot/sieve-global

  recipient_delimiter = +
  sieve_quota_max_storage = 50M
}

Configuration de Sieve.

Pfff, c’était long hein ? Et bien, on a pas fini…

 

II – Fichiers de mapping Ldap

A – Selon la Doc de Dovecot

Avant de faire nos fichiers pour le Ldap, on va regarder à quoi devrait ressembler un dovecot-ldap.conf.ext qu’on peut rencontrer sur le grand internet, un exemple souvent cité (bon, les filtres sont les miens, mais c’est l’idée) :

uris = ldap://ip.interne
dn = cn=viewer,ou=system,dc=debugo,dc=fr
dnpass = passview
debug_level = 0
auth_bind = no
ldap_version = 3
base = ou=people,dc=debugo,dc=fr
scope = subtree

user_attrs = mailaccountquota=quota_rule=*:bytes=%$
user_filter = (&(uid=%u)(objectClass=mailaccountdebugo)(mailaccountactif=YES))

pass_attrs = mail=user,userPassword=password
pass_filter = (&(uid=%u)(objectClass=mailaccountdebugo)(mailaccountactif=YES))

C’est la que le auth_debug et mail_debug sont utiles … Mais un peu de théorie avant.

B – Passdb et Userdb

Dovecot utilise deux bases : passdb et userdb, déclarées dans cond.f/10-auth.conf où j’utilise une petite astuce, le prefetch, pour éviter les doubles requêtes…

  • Lorsqu’un utilisateur se connecte en IMAP, passdb est utilisé pour vérifier l’utilisateur (login et mdp), puis userdb est appelé pour connaitre des infos supplémentaires sur l’user (d’où le prefetch).
  • Lorsqu’un mail arrive de Postfix à Dovecot via le protocole LMTP, c’est userdb qui est appelé pour connaitre l’utilisateur à qui remettre le mail d’où le second userdb car le premier, prefetch, ne contient rien pour le coup.

Il faut aussi savoir que Dovecot utilise des variables. Celles qui nous intéressent sont :

  • %u qui est le nom utilisateur complet fourni. Par exemple toto@debugo.fr via un appel en Imtp (mail entrant) ou toto via l’Imap (client qui se connecte)
  • %n qui correspond à la partie utilisateur, ici : toto
  • %d qui correspond au domaine, ici : debugo.fr

Ici, nous n’utiliserons que %u.

Au niveau du résultat ldap, l’ordre est :

attribut_ldap:attribut_dovecot

Et donc :

pass_attrs = mail=user,userPassword=password

indique que le champ mail de mon ldap correspond à l’utilisateur, du coup j’ai la bonne association.

C – Bind Ldap

Ensuite, au niveau du bind avec Ldap (la connexion avec Ldap), celui ci peut se faire de deux façons.

1 – auth_bind = yes

Cela permet de faire en sorte que le test du mot de passe se fasse directement avec le compte de l’utilisateur. Dovecot dans ce cas n’a pas besoin de lire le pass avec le compte viewer. En contre partie, c’est plus lent, Dovecot attendant que la connexion avec le Ldap soit terminée avant de passer à la suivante.

2 – auth_bind = no

La, il y a besoin que le compte viewer puisse lire le mot de passe (d’où les acls mises en place dans la partie LDAP). Ayant de toute façon besoin de ce compte pour d’autres applications, ça ne me dérange pas. De plus, c’est asynchrone, Dovecot peut lancer plusieurs requêtes concomitantes sans attendre apres.

Bref, on part sur le second choix.

D – Paf

Cependant, cette config me pose encore un problème… Lorsque l’on se connecte en IMAP on se présente donc avec un simple toto par contre, lors du LMTP, le %u présenté par Postfix est le mail complet, toto@debugo.fr et la, bah ça foire au niveau du filtre ldap, qui est pour mémoire :

(&(uid=%u)(objectClass=mailaccountdebugo)(mailaccountactif=YES))

Que faire ? Refaire nos utilisateurs pour que leur uid soit leur mail ? Heu, c’est pas l’idée à la base…

Donc, pour ça, j’avais trouvé une première solution :

user_filter = (&(|(mail=%u)(uid=%u))(objectClass=mailaccountdebugo)(mailaccountactif=YES))

Ha, les filtres LDAP et leur notation préfixée. Si vous avez eu une calculette Sharp dans les 90’s, vous devez connaître (et être vieux 😉 ). Bon, elles utilisaient la notation polonaise inverse où l’opérateur est à la fin, mais c’est la même idée… Si vous avez fait de l’algèbre booléenne ou bien joué avec les AND, OR, XOR, et NAND (du genre, comment faire un OR avec que des NAND, tables de vérités, etc..), et bien le + (ou) est remplacé par | et le . (et) par &.

On peut résumer :

(&(Bloc1)(Bloc2)(Bloc3)) -> On veut Bloc1 ET Bloc2 ET Bloc3
Bloc1=(|(A)(B)) -> A OU B

C’est beau hein ! Bah c’est balo car on en va pas l’utiliser. En effet, cela amène un effet de bord :  je peux désormais me connecter à ma bal avec comme login mon mail….
Après,  on aime ou  on n’aime pas. Perso, je ne préfère pas laisser cette possibilité, pour éviter les vilains qui tenteraient de se connecter en connaissant déjà le login…

Après tout, j’aurais pu en rester la et me dire tant pis, mais au final c’était ne pas vraiment comprendre Dovecot…

E – Solution

Il faut faire en sorte que les appels userdb et passdb soient distincts…

Pour la configuration, on aura donc deux fichiers :

/etc/dovecot/dovecot-ldap-pass.conf.ext :

uris = ldap://ip.ldap
dn = cn=viewer,ou=system,dc=debugo,dc=fr
dnpass = passview
debug_level = 0
auth_bind = no
ldap_version = 3
base = ou=people,dc=debugo,dc=fr
scope = subtree

pass_attrs = mail=user,userPassword=password,mailaccountquota=userdb_quota_rule=*:bytes=%$
pass_filter = (&(uid=%u)(objectClass=mailaccountdebugo)(mailaccountactif=YES))

La, on demande aussi au passage un attribut quota avec userdb userdb_quota_rule=*:bytes=%$ et qui sera utilisé dans le prefetch pour éviter de refaire une requête LDAP.

Et le fichier /etc/dovecot/dovecot-ldap-user.conf.ext :

uris = ldap://ip.ldap
dn = cn=viewer,ou=system,dc=debugo,dc=fr
dnpass = passview
debug_level = 0
auth_bind = no
ldap_version = 3
base = ou=people,dc=debugo,dc=fr
scope = subtree

user_attrs = mailaccountquota=quota_rule=*:bytes=%$
user_filter = (&(mail=%u)(objectClass=mailaccountdebugo)(mailaccountactif=YES))

La, on filtre sur le champ mail pour retrouver le compte correspondant.

On sécurise :

# chmod 600 /etc/dovecot/dovecot-ldap*

On va recharger Dovecot et regarder les logs voir si tout démarre bien :

# servive dovecot restart
# tail /var/log/mail.log -n 100

 

III – Sieve

A – La base

Dovecot permet donc de filtrer les messages en utilisant le protocole Sieve. Il les range à leur arrivée selon les règles, global et utilisateur.
Pour le global, on aura besoin que d’une seule règle : les messages marqués comme Spam sont dirigés dans le répertoire Spam.
Par la suite, chaque utilisateur pourra ajouter ses règles afin de filtrer comme il l’entend.

On a déjà activé Sieve au préalable. Il ne reste plus qu’à faire quelques opérations.

Pour le global :

mkdir /etc/dovecot/sieve-global
chown vmail /etc/dovecot/sieve-global

Nous rangerons nos scripts globaux dans ce répertoire, qui s’exécutera avant les filtres utilisateurs.

Si vous voulez faire les vérifications global après, dans le fichier 90-sieve.conf, il faudra indiquer :

sieve_after = /etc/dovecot/sieve-global

Et on peut bien sûr combiner.

Créez ensuite le fichier /etc/dovecot/sieve-global/global.sieve avec le contenu qui suit :

require ["variables", "envelope", "fileinto", "mailbox", "regex", "subaddress", "body"];

if header :contains "X-Spam" "Yes" {
  fileinto "Junk";
  stop;
}

Tout bête, si le header contient X-spam à Yes, on le déplace dans les indésirables ( Le X-spam sera rajouté par Rspamd).

Le stop indique d’arrêter le traitement. En effet, si d’autres filtres (users ou globaux) matchent également, le message sera dupliqué.

Puis  on change l’user et les droits :

# chown vmail: /etc/dovecot/sieve-global/global.sieve
# chmod 750 /etc/dovecot/sieve-global/global.sieve

On peut le compiler :

# sievec /etc/dovecot/sieve-global/global.sieve

qui donnera un fichier /etc/dovecot/sieve-global/global.svbin, version compilée de nos règles.

Cette opération est cependant facultative et elle sera de toute façon exécutée par Dovecot à la première occasion si vous le n’avez pas fait.

Si on le fait, on pense  à modifier les droits :

# chown vmail /etc/dovecot/sieve-global/global.svbin

Je serais même tenté de dire que la première fois, c’est mieux de laisser Dovecot le faire, en regardant les logs et avec l’option mail_debug = yes on voit très rapidement où se trouve le problème s’il y en a un (et généralement, ça chouine pour des histoires de permissions). Pour les filtres utilisateurs, c’est la même tambouille, Dovecot les compilant au moment ou il les charge, s’il n’existent pas ou sont obsolètes.

B – Niveau Utilisateur

Pour modifier les filtres d’un utilisateur, on peut utiliser un webmail préalablement configuré. Pour le moment, je ne couvre pas cette partie (ce sera pour plus tard), donc pour le moment, on va plutôt passer par un client tel que celui-ci.

Voila un exemple de règles :

require ["variables", "envelope", "fileinto", "mailbox", "regex", "subaddress", "body"];

if header :contains "subject" "Postfix SMTP server" {
    fileinto "Serveur";
}

A noter : Sieve permet d’avoir plusieurs fichiers de filtrage, mais un seul peut être actif à la fois.

Bien sur, Sieve permet de faire beaucoup de choses, avec gestion de conditions, variables, etc… Ce sera l’occasion de faire un article complet (ou presque) sur Sieve un peu plus tard.

C – Tests

Comme je l’ai dis, l’option mail_debug = yes permet de voir de suite dans le fichier de log /var/log/mail.log c e qui ne va pas.

 

IV – Quota

A – Configuration

Pour rajouter la gestion du quota, il suffit de quelques manipulations. Pour rappel, si le champ dans le ldap est égal à zéro, cela veut dire pas de quota. Notez également que le champ est exprimé en bytes.

Dans le fichier /etc/dovecot/conf.d/10-mail.conf on va modifier :

[...]
mail_plugins = $mail_plugins quota
[...]

Dans le fichier /etc/dovecot/conf.d/20-imap.conf  :

[...]
mail_plugins = $mail_plugins imap_sieve imap_quota
[...]

Puis dans le fichier 20-lmtp.conf :

[...]
mail_plugins = $mail_plugins sieve quota
[...]

Et pour terminer on remplace le contenu du fichier /etc/dovecot/conf.d/90-quota.conf par ce qui suit :

plugin {
  quota = maildir:User quota
  quota_warning = storage=90%% quota-warning 90 %u
}

service quota-warning {
  executable = script /etc/dovecot/quota.sh
  user = vmail
  unix_listener quota-warning {
    user = vmail
  }
}

Voila, c’est en place.

B – Avertissement automatique

On va rajouter le petit script qui va envoyer le mail d’alerte.

Dans un fichier /etc/dovecot/quota.sh mettez cela :

#!/usr/bin/env bash

PERCENT=${1}
USER=${2}

cat << EOF | /usr/lib/dovecot/dovecot-lda -d $USER -o "plugin/quota=maildir:User quota:noenforcing"
From: no-reply@debugo.fr
Subject: HOHOHO: Votre BAL est pleine a ${PERCENT}
Content-Type: text/plain; charset="utf-8"

HOHOHO
Votre BAL est pleine a ${PERCENT}. Faut faire du menage mon coco !
EOF

Rien de bien compliqué.

On s’occupe des permissions :

# chmod +x /etc/dovecot/quota.sh
# chown vmail /etc/dovecot/quota.sh

L’œil attentif remarquera que l’on fait appel à dovecot-lda pour ce mail. J’ai tenté avec le lmtp, où pourtant -d a la même utilité, mais je me mange une erreur. Tant pis, au final, lda est toujours dispo, et n’est invoqué qu’au besoin. Autant dire que ça ne va pas beaucoup tourner

On édite le fichier /etc/dovecot/conf.d/15-lda.conf pour tout remplacer par :

protocol lda {
  info_log_path =
  log_path =
  mail_plugins = sieve quota
  postmaster_address = postmaster@debugo.fr
  quota_full_tempfail = yes
}

On recharge Dovecot :

# dovecot reload

C – Tests

Si vous voulez ajouter un quota à un utilisateur pour tester, sur le ldap, un fichier mod_quota.ldif :

dn: uid=toto,ou=people,dc=debugo,dc=fr
changetype: modify
replace: mailaccountquota
mailaccountquota: 2147483648

Que vous injectez :

# ladd -f mod_quota.ldif

La, il est réglé à 2Go.

Ensuite, de retour sur le serveur mail, on va déjà regarder si le quota est fonctionnel :

# doveadm quota get -u toto@debugo.fr

doit retourner quelque chose qui ressemble à ça :

Quota name  Type     Value   Limit    %
User quota  STORAGE  125786  2097152  5
User quota  MESSAGE  2329    -

On va aussi tester le script :

./quota.sh 90 toto@debugo.fr

Et pour tester en conditions réelles, attention, il faut savoir que le mail n’est envoyé qu’une seule fois, au moment où le quota est dépassé.

Par exemple, si vous activez le quota avec une bal déjà hors quota vous n’aurez jamais le mail. Du coup, pour tester, c’est pas évident… faut jouer avec une toute petite bal. Mais si les deux commandes au préalable fonctionnent sans problèmes, ça doit marcher en production.

Pour voir le quota avec Thunderbird, je conseille Display Quota.

 

V – Conclusion

Voila pour cette troisième partie où nous avons fait connaissance avec Dovecot. Je pourrais vous parler en profondeur de la commande doveadm qui permet de faire pas mal de choses mais ce sera pour un tuto annexe… En attendant, rien ne vous empêche de vous documenter.

Vous voulez la bonne nouvelle ? Notre serveur de messagerie est opérationnel.

Pour tester, rien de tel qu’un client lourd. Thunderbird pour Windows, Evolution ou autres pour Linux.

 

Vous devez pouvoir recevoir et envoyer des mails.

Attention cependant, aucune protection contre le spam n’est encore en place à ce niveau. De la même façon,  vos mails envoyés vers certains domaines (gmail, hotmail) risquent fort de se retrouver classés comme spams (DKIM, SPF et DMARC pas encore en place).

Et oui, j’ai dis opérationnel, pas terminé ! Mais ne ne perdez pas espoir, on a fait le plus dur.

Maintenant, dans la partie IV, nous allons optimiser un peu Postfix.

 

4 réflexions sur « Tuto Messagerie, partie III : Dovecot »

  1. Hello,

    Un très grand merci pour ta doc, qui m’est d’une grande utilité. Petite remarque concernant l’étape 5, selon la doc https://wiki2.dovecot.org/Upgrading/2.3, les options suivants ont changé entre 2.2 et 2.3 :
    ssl_dh_parameters_length => n’est plus utile
    ssl_protocols ==> est remplacé par ssl_min_protocol (TLSv1 par défaut)

  2. Salut,

    J’ai un petit problème avec le « recipient_delimiter ».
    Je l’ai bien défini aussi bien dans le /etc/postfix/main.cf que le /etc/dovecot/conf.d/90-sieve.conf avec la même valeur (« _ »).
    /etc/postfix/main.fr :

    recipient_delimiter = _

    /etc/dovecot/conf.d/90-sieve :
    plugin {
    sieve = file:~/sieve;active=~/.dovecot.sieve
    sieve_before = /etc/dovecot/sieve-global

    recipient_delimiter = _
    sieve_quota_max_storage = 50M
    }

    Mais ça me jette dès que je tente de l’utiliser en « user unknown » !
    Pourtant il détecte bien l’alias et me traduit bien l’alias « plm_test@domaine » en « pierre.moi_test@domaine » via postfix mais plante sur le test dovecot :
    Jun 21 14:50:19 angelie postfix/lmtp[16433]: 2DD8A12AD7E5: to=, orig_to=, relay=Serveur.domaine[private/dovecot-lmtp], delay=2.3, delays=2.2/0/0.02/0.02, dsn=5.1.1, status=bounced (host Serveur.domaine[private/dovecot-lmtp] said: 550 5.1.1 User doesn’t exist: pierre.moi_tets@dev.teledetection.fr (in reply to RCPT TO command))

    Où que je me suis gourré ?

    1. Je me répond à moi-même, RTFM…

      Il faut bien déclarer le « recipient_delimiter » aussi dans Dovecot mais pas dans le « 90-sieve » car cela concerne également de LDA… Il faut le déclarer dans le « 15-lda.conf ».

      Ha! ce boulet…

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *