utilisateurs postfix virtuels : ajouter un répondeur « vacation »

Avec mes précédents articles ci-dessous, vous avez de quoi monter une architecture complète de mails avec utilisateurs virtuels. Pour faire simple.

Il manque cependant un morceau : la possibilité de faire appel à des « procmail » personnalisés pour ces utilisateurs virtuels.

Lorsqu’on n’est pas avec des utilisateurs virtuels, chaque utilisateur réel (ayant un compte utilisateur, donc) utilise généralement son ~/.procmailrc pour trier un peu ou – cas qui m’intéresse particulièrement – activer son répondeur d’absence, « vacation«  pour ne pas le nommer.
Dans le cas d’utilisateurs virtuels, c’est sensiblement différent. C’est ce que je décrirai dans cet article. Ce n’est au final pas compliqué, mais il faut comprendre le rôle de chaque composant et les limites de fonctionnement de chaque outil.
En effet, je n’ai pas trouvé de doc ultra-claire sur le sujet sur le web, surtout les bricoles de chacun. Avec un peu de recul, et, voyant ma propre solution, j’ai l’impression qu’il y a tellement de configurations possibles (maildrop et pas procmail, mes utilisateurs virtuels comme ci, pas comme ça, il utilise postfixadmin et pas moi etc) qu’il est impossible d’écrire un truc qui n’est pas spécifique.
Le plus dur est donc de comprendre ce qu’on fait pour transposer à sa conf. Je tâcherai donc, comme d’hab, d’expliquer ce que je fais plutôt que de copier-coller les fichiers de conf.

Allez, let’s go pour la mise en place sur la base d’une archi postfix & co comme décrite dans les docs mentionnées en début de cet article.

Ah, dernier point : à la base, et tel que je le décris, l’utilisateur ne pourra pas mettre lui même son répondeur en place. Dans mon cas, ce n’est pas important. En effet, j’utilise des utilisateurs virtuels car il s’agit d’un serveur frontal de mails (qui trie le spam, en gros), livre les messages dans des arborescences virtuelles en attendant d’être POPées depuis un backend de mails quelconque (un vilain Exchange :)). Donc, le répondeur d’absence des utilisateurs « physiques » est géré par eux-mêmes dans Outlook « normalement », et je n’applique ce principe de répondeur qu’exceptionnellement, à quelques boîtes mails IMAP génériques, qui n’ont pas vraiment de notion de « vacances ». Des boîtes partagées entre plusieurs personnes, si vous voulez, mais stockées sur le frontal et pas dans le backend, pour diverses raisons dont on se moque ici. Après vous pourrez toujours bricoler un truc pour que tout un chacun accède à son procmail ou son message d’asbence pour le mettre, l’enlever etc.

Le principe

On veut activer le répondeur de toto@domaine.fr en utilisant l’outil « vacation » qui gère très bien les réponses. « vacation » ne joue qu’avec des comptes utilisateurs existants. Premier problème.

Pour y arriver, on va créer des alias pour ce compte virtuel « toto@domaine.fr » afin que postfix « forward » (du nom de la table « forwardings » gérant les alias des utilisateurs virtuels) tout mail à destination de toto@domaine.fr vers 2 endroits :

  • le réel toto@domaine.fr, histoire de délivrer le mail quand même, au final
  • et en plus vers un certain toto@vacation.domaine.fr qui n’existe pas


Ce dernier nom « vacation.domaine.fr » n’a pas besoin d’exister au sens DNS et ne correspond pas non plus à un MX réel. On va juste créer un « transport » (du nom de la table MySQL qu’on a créée avec les docs précédentes) particulier afin de router les messages à destination du domaine « vacation.domaine.fr » vers un « pipe » particulier de postfix qu’on va créer pour l’occasion.
Ce « pipe » UNIX, composant qu’on ajoute à la chaîne postfix, gèrera en fait l’appel à procmail configuré d’une manière globale (un gros procmailrc générique, si vous voulez).
Ce procmail fera ensuite un tri sur le nom du destinaire et appellera « vacation » lorsqu’on le veut avec un message personnalisé.

C’est ce procmail global qu’il conviendrait de découper (avec des INCLUDE), ainsi que les fichiers de messages d’absence qui vont avec, si on voulait donner la main d’une manière pas super pratique (FTP ? samba ?) à des utilisateurs réels sur leur répondeur de compte virtuel…

Je me suis basé sur pas mal de docs bizarroïdes, et surtout sur les quelques explications trouvées ici. Le cheminement y est, mais c’est pas détaillé sur le principe et le type propose son propre script de répondeur ; script qui peut générer une belle boucle infinie entre répondeurs, à mon humble avis. Ce risque n’existe pas avec « vacation« , car il garde une liste des gens « déjà répondus », sauf si vous forcez à répondre à chaque fois.

Mise en place

Paquets, utilisateur etc

Au besoin, suivant l’état de votre installation :

aptitude install procmail vacation

Ensuite, j’ai expliqué que « vacation » ne travaillait que sur un compte réel. Je lui dédie donc un compte :

useradd -m vacation -s /bin/nologin -c "Utilisateur vacation"

Création du pipe UNIX

On crée le processus qui sera en charge de router (de manière interne à postfix) les messages arrivant dans un pipe qu’on nomme « repondeur » vers procmail (lui même appelant le cas échéant le programme de répondeur « vacation« ).
Dans /etc/postfix/master.cf, à la fin (peu importe), on définit ce pipe comme suit :

#ajout d'un appel à procmail pour gérer certains 'vacation' sur les boites IMAP
#attention, l'appel à ce pipe est géré dans la table des transports virtuels + des forwardings qui vont bien
repondeur   unix    -       n       n       -       10      pipe
  flags=Rq user=vacation argv=/usr/bin/procmail -Y -m /etc/postfix/procmail-global-repondeur.rc ${sender} ${recipient}

Les espaces avant « flags » sont importants, c’est ce qui explique à postfix qu’il s’agit d’option du pipe nommé « repondeur ».
Je décrirai le fichier /etc/postfix/procmail-global-repondeur.rc plus tard. C’est lui qui contiendra les appels aux « vacation » de tels et tels utilisateurs.
J’avoue ne pas être super sûr des flags utilisés, voyez man pipe au besoin.

transport virtuel

Si votre base MySQL est exactement celle que je décris dans mes précédentes documentations, insérez une ligne dans la table « transport » afin d’avoir :

mysql> select * from transport;
+---------------------+-----------+
| domain              | transport |
+---------------------+-----------+
| vacation.domaine.fr | repondeur |
+---------------------+-----------+
1 row in set (0.00 sec)

Pour bien relier cette table à votre configuration « virtuelle », la table « transport » dont je parle est celle décrite comme ça dans postfix :

[extraits de fichiers]
main.cf:transport_maps = proxy:mysql:/etc/postfix/mysql-virtual_transports.cf
mysql-virtual_transports.cf:query = SELECT transport FROM transport WHERE domain='%s'

Création des alias pour router vers le « pipe » « repondeur »

Pour chaque utilisateur virtuel qui devra avoir un répondeur actif (ou potentiellement actif), il faudra ajouter 2 alias le concernant dans la table des alias (« forwardings » dans mon cas, là encore, ça dépend de votre conf de « postfix virtuel »). Ajoutez donc les lignes pour avoir :

mysql> select * from forwardings where source like 'j%doma%';
+--------------------+-----------------------------+
| source             | destination                 |
+--------------------+-----------------------------+
| jacques@domaine.fr | jacques@domaine.fr          |
| jacques@domaine.fr | jacques@vacation.domaine.fr |
+--------------------+-----------------------------+
2 rows in set (0.00 sec)

J’ai eu un souci pour insérer ces 2 lignes, car il y a une contrainte de clef primaire sur « source », ce qui semble logique, mais qui est en fait bloquant pour nous et inutile dans un cas normal d’alias d’utilisateurs virtuels (on pourrait faire une liste de diffusion en créant plusieurs lignes pour une même source car postfix gère très bien le fait d’avoir plusieurs destination pour une même source).
Bref, j’ai fait sauté la contrainte PK que j’avais mise dans mes docs précédents :

ALTER TABLE forwardings
DROP PRIMARY KEY;

Rassurez-vous, ça ne fait pas sauter les entrées déjà stockées dans la table si vous en avez.

procmailrc et vacation

On y est presque.
Reste à définir le procmail global appelant vacation :
Dans mon « pipe« , j’appelle ce fichier :

servr:/etc/postfix# cat /etc/postfix/procmail-global-repondeur.rc
SHELL=/bin/sh

:0c
* ^(To|Cc).*jacques@domaine.fr
| vacation -f /home/vacation/jacques@domaine.fr.db -m /home/vacation/jacques@domaine.fr.msg -a jacques@domaine.fr vacation

:0c
* ^(To|Cc).*autre_type@domaine.fr
| vacation -f /home/vacation/autre_type@domaine.fr.db -m /home/vacation/autre_type@domaine.fr.msg -a autre_type@domaine.fr vacation

:0
/dev/null

Alors, c’est presque le plus compliqué car ce fichier est plein de petites ruses. Il vous montre comment procéder lorsque vous aurez plusieurs boîtes concernées par un vacation.
Je rappelle que de cette manière, c’est un peu pénible à maintenir car c’est l’admin qui gère. Là il s’agit de boîte IMAP partagées et peu nombreuses, donc c’est gérable.

Le détail est donc :

  • Je stocke tous mes messages de « vacances » dans ~vacation/
  • -f : pour indiquer dans quelle « base de données » on stocke les gens à qui on a déjà répondu
  • -m : pour indiquer le mail de réponse d’un utilisateur donné, je le détaille plus bas
  • -a : hyper important : en effet, vacation ne travaille que sur un utilisateur réel. Donc obligé de spécifier un utilisateur réel en fin de commande vacation, c’est l’utilisateur « vacation » himself qui figure en dernier argument de la ligne de commande. Donc, pour que vacation travaille, j’explique aussi que cet utilisateur réel peut être connu sous l’alias (-a) jacques@domaine.fr (ou autre_type@domaine.fr dans la 2è règle). Sinon aucune règle ne s’appliquera jamais.
  • « :0c » : OUI : il faut activer le « carbon copy ». J’ai constaté ça à force de tests. En effet, un message arrivant dans postfix a un ID, il est dédoublé avec le même ID, l’un part vers l’utilisateur réel pour livraison, l’autre vers le « pipe« , puis procmail, puis vacation. « vacation » arrête les règles (si on ne met pas le « c » de carbon copy) dès que ça « match ». Si un expéditeur envoi un unique mail à en même temps jacques@domaine.fr et autre_type@domaine.fr, la première règle va marcher, donc procmail arrêtera de bosser. Résultat : pas de répondeur pour « autre_type ». On ne sait pas qu’il est en vacances.
  • Et enfin, le piège des « carbon copy » : si on ne fait pas une dernière règle virant vers /dev/null tout message au final, un message légitime auquel on aura répondu par « vacation » sortira de procmail et continuera sa route : il sera donc livré. Mais c’est un utilisateur complètement bidon, untel@vacation.domaine.fr, domaine inexistant => erreur de livraison => log dans /var/log/mail.info et surtout, mail d’erreur bien crade informant l’expéditeur…

fichier .msg de vacation

Voici à quoi doit ressembler un fichier de réponse minimaliste :

servr:/etc/postfix# cat ~vacation/jacques\@domaine.fr.msg
From: jacques@domaine.fr (Jacques)
Subject: Re: $SUBJECT
Precedence: Bulk

Votre mail a bien été reçu, blablablabla
-- Jacques

reload postfix et tests

A la fin, /etc/init.d/postfix reload et zou, testez des envois de mails, de réenvois de mails (pour n’avoir qu’une fois la réponse etc).
Le plus risqué dans cette manip’ sur un serveur en production, c’est que tout les adresses normales commencent à merdouiller. Le plus rapide pour annuler tout ça le temps de réfléchir, est de commenter les 2 lignes définissant le pipe et de rechargez postfix.

Autres remarques

Réinit d’un répondeur

A une époque, il me semblait qu’en modifiant le fichier .msg, vacation détectait le changement et donc ne tenait plus compte des expéditeurs « déjà répondus ». Après quelques tests, je n’en suis plus si sûr. Le plus simple pour relancer un répondeur « pour tous » : supprimez le fichier .db correspondant.

Permissions

Attention, j’ai interdit le login pour l’utilisateur vacation. Donc tout ce que vous créerez dans ~vacation/*msg sera sûrement au nom de « root », par exemple. Pensez à changer les droits.
Si vous avez raté un truc, les logs sont très explicites dans /var/log/mail.info.
Je crois aussi me rappeler que si vacation ne voit pas le fichier .msg, il ne fera rien. A vérifier, mais c’est une méthode simple (et crade car ça va faire du log à coup sûr) pour activer/désactiver un répondeur 🙂

un log complet pour illustrer

Si tout se passe bien, vous avez ça :
Connexion du serveur expéditeur :

Jun 14 17:10:21 servr postfix/smtpd[20107]: connect from expediteur.org[xx.yy.zz.tt]
Jun 14 17:10:21 servr postgrey[12166]: action=pass, reason=client AWL, client_name=expediteur.org, client_address=xx.yy.zz.tt, sender=exped@expediteur.org, recipient=jacques@domaine.fr
Jun 14 17:10:21 servr postfix/smtpd[20107]: C2B829C4081: client=expediteur.org[xx.yy.zz.tt]
Jun 14 17:10:21 servr postfix/cleanup[20065]: C2B829C4081: message-id=<20100614151021.AF0F0EEC57C@mail.expediteur.org>
Jun 14 17:10:21 servr postfix/qmgr[19547]: C2B829C4081: from=, size=515, nrcpt=2 (queue active)
Jun 14 17:10:21 servr postfix/smtpd[20107]: disconnect from expediteur.org[xx.yy.zz.tt]
Jun 14 17:10:23 servr postfix/smtpd[20208]: connect from localhost.localdomain[127.0.0.1]
Jun 14 17:10:23 servr postfix/smtpd[20208]: 8A0E69C4085: client=localhost.localdomain[127.0.0.1]
Jun 14 17:10:23 servr postfix/cleanup[20065]: 8A0E69C4085: message-id=<20100614151021.AF0F0EEC57C@mail.expediteur.org>
Jun 14 17:10:23 servr postfix/smtpd[20208]: disconnect from localhost.localdomain[127.0.0.1]
Jun 14 17:10:23 servr postfix/qmgr[19547]: 8A0E69C4085: from=, size=1045, nrcpt=3 (queue active)

Pas de greylisting, car c’est un pote. Analyse par amavis OK, on dédouble le message vers jacques@domaine.fr et jacques@vacation.domaine.fr.
Chaque file suivra alors sa route :

Jun 14 17:10:23 servr amavis[18918]: (18918-18) Passed CLEAN, [xx.yy.zz.tt] [xx.yy.zz.tt]  -> ,, Message-ID: <20100614151021.AF0F0EEC57C@mail.expediteur.org>, mail_id: uuW4XqNAoH7S, Hits: 1.714, size: 515, queued_as: 8A0E69C4085, 1902 ms
Jun 14 17:10:23 servr postfix/smtp[20190]: C2B829C4081: to=, relay=127.0.0.1[127.0.0.1]:10024, delay=1.9, delays=0.03/0/0/1.9, dsn=2.0.0, status=sent (250 2.0.0 Ok, id=18918-18, from MTA([127.0.0.1]:10025): 250 2.0.0 Ok: queued as 8A0E69C4085)
Jun 14 17:10:23 servr postfix/smtp[20190]: C2B829C4081: to=, orig_to=, relay=127.0.0.1[127.0.0.1]:10024, delay=1.9, delays=0.03/0/0/1.9, dsn=2.0.0, status=sent (250 2.0.0 Ok, id=18918-18, from MTA([127.0.0.1]:10025): 250 2.0.0 Ok: queued as 8A0E69C4085)
Jun 14 17:10:23 servr postfix/qmgr[19547]: C2B829C4081: removed

La partie « utilisateur virtuel de postfix » traite le « mail normal » => « delivered to maildir ». Ca roule :

Jun 14 17:10:23 servr postfix/virtual[20212]: 8A0E69C4085: to=, relay=virtual, delay=0.3, delays=0.15/0.01/0/0.14, dsn=2.0.0, status=sent (delivered to maildir)

La partie « vacation » (pipe repondeur) route vers le « relay=repondeur ». Le job s’accomplit correctement « (delivered via repondeur service) ». Là on n’a pas d’autre erreur, sinon on pourrait voir vacation ou procmail pas content :

Jun 14 17:10:23 servr postfix/pickup[19550]: ECF179C408A: uid=5001 from=
Jun 14 17:10:23 servr postfix/pipe[20209]: 8A0E69C4085: to=, orig_to=, relay=repondeur, delay=0.41, delays=0.15/0/0/0.25, dsn=2.0.0, status=sent (delivered via repondeur service)
Jun 14 17:10:23 servr postfix/pipe[20209]: 8A0E69C4085: to=, relay=repondeur, delay=0.41, delays=0.15/0/0/0.25, dsn=2.0.0, status=sent (delivered via repondeur service)
Jun 14 17:10:23 servr postfix/cleanup[20065]: ECF179C408A: message-id=<20100614151023.ECF179C408A@domaine.fr>
Jun 14 17:10:23 servr postfix/qmgr[19547]: 8A0E69C4085: removed
Jun 14 17:10:24 servr postfix/qmgr[19547]: ECF179C408A: from=, size=343, nrcpt=1 (queue active)

Deux secondes plus tard, « vacation » a détecté qu’il fallait répondre à « exped@expediteur.org », donc on envoit un mail :
(le champ From sera jacques@domaine.fr, seul Return-Path sera à vacation@domaine.fr)

Jun 14 17:10:26 servr postfix/smtpd[20208]: connect from localhost.localdomain[127.0.0.1]
Jun 14 17:10:26 servr postfix/smtpd[20208]: 174A99C4081: client=localhost.localdomain[127.0.0.1]
Jun 14 17:10:26 servr postfix/cleanup[20065]: 174A99C4081: message-id=<20100614151023.ECF179C408A@domaine.fr>
Jun 14 17:10:26 servr postfix/qmgr[19547]: 174A99C4081: from=, size=770, nrcpt=1 (queue active)
Jun 14 17:10:26 servr amavis[20202]: (20202-01) Passed CLEAN,  -> , Message-ID: <20100614151023.ECF179C408A@domaine.fr>, mail_id: xw6dpgfdz+b5, Hits: -0.001, size: 343, queued_as: 174A99C4081, 2197 ms
Jun 14 17:10:26 servr postfix/smtp[20190]: ECF179C408A: to=, relay=127.0.0.1[127.0.0.1]:10024, delay=2.3, delays=0.14/0/0/2.2, dsn=2.0.0, status=sent (250 2.0.0 Ok, id=20202-01, from MTA([127.0.0.1]:10025): 250 2.0.0 Ok: queued as 174A99C4081)
Jun 14 17:10:26 servr postfix/qmgr[19547]: ECF179C408A: removed
Jun 14 17:10:26 servr postfix/smtp[20219]: 174A99C4081: to=, relay=mail.expediteur.org[xx.yy.zz.tt]:25, delay=0.21, delays=0.14/0/0.02/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 4337FEEC560)
Jun 14 17:10:26 servr postfix/qmgr[19547]: 174A99C4081: removed

5 comments

  1. Bonjour !

    Merci pour cette doc, très pratique !

    Pour le problème de clé dans la table forwardings, pourquoi ne pas ne garder qu’une ligne avec comme destinataire les deux adresses séparées par une virgule ? Ça fonctionne.

    Mais s’il y a un traitement manuel, c’est clair qu’avoir deux entrées dans la base de données, c’est plus simple à gérer.

    Encore merci !

    1. En effet, je ne sais pas pourquoi je suis parti là-dedans, d’autant que j’utilise le « destination » avec des adresses séparées par des virgules…. 🙂

  2. Merci pour ce tuto, juste parfait (comme d’habitude 😉

    J’ai utilisé une entrée pour la table alias avec les deux emails séparés par une virgule comme indiqué par Frédéric.

Laisser un commentaire

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

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.