57. pepsi-stage-milter¶
Faire passer un message par un filtre de courrier milter sendmail/Postfix.
57.1. Rôle¶
pepsi-stage-milter parle le protocole milter — l’interface de filtrage de courrier introduite par sendmail et adoptée par Postfix — de sorte que les filtres qu’un opérateur exploite déjà fonctionnent ici sans modification : opendkim, rspamd, clamav-milter, spamass-milter, milter-greylist. Pour chaque message, l’étape se connecte à la socket du filtre, rejoue le message sous forme de session SMTP, applique toutes les modifications que le filtre demande, et achemine le message selon son verdict. Référence : pepsi-stage-milter(1).
L’étape est un client et rien d’autre. Le filtre est un démon de longue durée existant, avec son propre paquet, sa propre unité systemd et son propre compte utilisateur, exactement comme sous sendmail et Postfix — Pepsi ne le démarre ni ne le confine, car confiner un démon relève de l’unité de ce démon et, sous Debian, c’est déjà fait là. L’étape elle-même ne porte donc aucun privilège : pas de bit setuid, pas de bit setgid, pas de helper.
57.2. Après la file, et ce que cela coûte¶
Avertissement
Un milter est conçu comme un filtre pré-file, et Pepsi l’exécute après la mise en file. C’est le seul point où la prise en charge des milters par Pepsi n’est pas équivalente à celle d’un MTA.
Dans un MTA, les rappels s’exécutent à l’intérieur de la session SMTP, avant que le serveur n’ait accepté le message — ce qui permet à un milter de répondre 550 à un expéditeur falsifié sans engendrer le moindre rebond. Le pipeline de Pepsi s’exécute entièrement après la mise en file du message : au moment où une étape voit une ligne, pepsi-ingress a déjà dit 250. Un REJECT ici coûte donc un rebond vers l’adresse que l’enveloppe nommait, ce qui, pour du spam falsifié, est un tiers innocent.
Un milter de greylisting ou de DNSBL repris de Postfix filtre toujours correctement, mais au prix du backscatter. Pointez REJECT_STAGE vers une pepsi-stage-discard plutôt que vers une pepsi-stage-bounce si cela importe. Les filtres qui ne font qu”annoter — un signataire, ou un évaluateur qui ajoute un en-tête sur lequel une pepsi-stage-if ultérieure branchera — ne sont pas concernés.
57.3. Fonctionnalités¶
Les deux familles de sockets :
SOCKETprend la grammaireS=propre à sendmail, que Postfix accepte également —unix:/path,inet:host:port,inet:port@host,inet6:[addr]:port— de sorte qu’une valeur reprise telle quelle de l’une ou l’autre configuration fonctionne sans modification.La session est reconstruite, non inventée : les phases connect, helo et mail proviennent de ce que
pepsi-ingressa noté sousstate.origin— l’adresse du pair et son nom PTR, le nomHELO, la version TLS et le chiffrement, le listener (l’adresse locale, exportée sous{daemon_addr}dans la liste de macros de connexion par défaut de Postfixj {daemon_name} {daemon_addr} v _), les paramètresBODY=/SMTPUTF8, et la façon dont la session s’est authentifiée (les macros RFC 4954{auth_type}/{auth_authen}/{auth_author}/{auth_ssf}). Un message injecté localement (un rebond, une réponse automatique, une soumissionpepsi-sendmail) est présenté comme une session en boucle locale, ce que fait sendmail pour la soumission locale.Une liste d’autorisation d’actions :
ALLOW_ACTIONSdécide quelles modifications le filtre peut faire. La valeur par défaut (addhdrs chgbody chghdrs) couvre tout filtre de contenu ; réécrire l”enveloppe — ajouter ou supprimer des destinataires (addrcpt/delrcpt), changer l’expéditeur (chgfrom) — etquarantinedoivent être accordés explicitement.alletnonesont des abréviations, et un nom inconnu est une erreur plutôt qu’une inaction silencieuse. En l’absence de bac à sable autour du filtre, c’est le seul levier disponible sur le rayon d’action.Une exigence non satisfaite est signalée, non ignorée : un filtre qui a besoin d’une action qui ne lui a pas été offerte est nommé dans le journal et le message prend le chemin
ON_FAILURE. Postfix abandonne une telle demande en silence, et c’est ainsi qu’un opérateur découvre six mois plus tard qu’un milter de signature n’a jamais rien signé.La sémantique d’échec de Postfix :
ON_FAILUREreflètemilter_default_actionet a la même valeur par défaut (tempfail), tout comme les trois timeouts (30 s / 30 s / 300 s), de sorte qu’un filtre en panne retarde le courrier plutôt que de le perdre ou de le laisser passer sans filtrage.Le rejet par destinataire s’éclate : un destinataire que le filtre écarte durant la phase
RCPTest détaché sur sa propre ligne sœur àREJECT_STAGEet le reste du message poursuit sa route, avecstate.dsn.rcptdécoupé de concert avecrcpt_tosur chaque ligne obtenue. Un destinataire que le filtre ne fait que différer (4xx) est détaché sur une ligne qui attend à l’étape milter et est filtrée à nouveau plus tard, jusqu’àMAX_LIFETIME— puis fait l’objet d’un rebond, jamais d’un rejet.L’injection d’en-tête est impossible : une valeur d’en-tête contenant un saut de ligne est repliée plutôt que de pouvoir devenir un second champ, et un nom de champ invalide est refusé d’emblée.
57.4. Placement¶
À l’entrée, placez-la après pepsi-stage-decrypt afin que le filtre voie du texte clair, et avant les barrières de liste blanche et de spam afin qu’elles puissent brancher sur ce qu’il a étiqueté. À la sortie, placez-la juste avant pepsi-stage-dkim-sign afin que notre propre signature couvre ses modifications.
pepsi-setup --wizard le fait pour vous pour les filtres qu’il sait trouver. Il parcourt l’hôte à la recherche de sept démons connus, confirme chacun en menant à bien une véritable négociation d’options avec sa socket, et émet une étape par filtre accepté — ordonnées selon ce que fait le filtre, avec ALLOW_ACTIONS réglé sur exactement ce que ce filtre a demandé pendant la négociation, et avec les rejets acheminés vers une étape de discard engendrée plutôt que renvoyés en rebond. Les filtres dont Pepsi fait déjà le travail (opendkim, openarc, opendmarc, démons SPF, postsrsd) sont nommés et sautés. Voir le balayage dans pepsi-setup(1).
Un filtre qui réécrit le corps casse nécessairement le hachage de corps DKIM de l’auteur et l”AMS ARC de ce déploiement — la même mise en garde inévitable que porte pepsi-stage-vacation pour son étiquette de sujet. C’est sans conséquence sur une branche de remise locale ; sur une branche qui relaie le message plus loin, préférez un filtre qui n’ajoute que des en-têtes.
57.5. Verdicts¶
|
|
|
|
|
|
|
mis en pause pour réessai, puis |
|
supprimé, silencieusement |
|
|
Un tempfail qui survit à MAX_LIFETIME n’est pas un verdict sur le message, il ne prend donc jamais REJECT_STAGE (qui peut abandonner du courrier silencieusement) : il va vers BOUNCE_STAGE, ou est laissé en échec pour pepsi-failure-bouncer s’il n’y en a pas.
ACCEPT_STAGE existe parce que Postfix applique une liste de milters, où accepter signifie « sauter les suivants ». Un filtre par étape n’a pas cette notion : sans cette option, accepter et continuer seraient la même chose ; pointez-la au-delà du reste d’une chaîne pour rétablir la distinction.
57.6. Configuration¶
[stage-<name>] : PROGRAM = pepsi-stage-milter, SOCKET (obligatoire), NEXT_STAGE (obligatoire — ``CONTINUE`` est le verdict par défaut et y mène, et ``ACCEPT_STAGE`` ne couvre que ``ACCEPT``), les cibles de routage ACCEPT_STAGE/REJECT_STAGE/QUARANTINE_STAGE, BOUNCE_STAGE (sur lequel REJECT_STAGE se replie), ON_FAILURE (tempfail — la valeur par défaut — accept, reject ou discard), ALLOW_ACTIONS, MILTER_NAME (la macro {daemon_name} ; prend par défaut le label propre de l’étape), PROTOCOL_VERSION (de 2 à 6, par défaut 6), les trois timeouts CONNECT_TIMEOUT/COMMAND_TIMEOUT/CONTENT_TIMEOUT, les options de réessai partagées (RETRY_INITIAL/RETRY_MAX_INTERVAL/RETRY_FACTOR/ MAX_LIFETIME) et les sept listes MACROS_* (MACROS_CONNECT, MACROS_HELO, MACROS_MAIL, MACROS_RCPT, MACROS_DATA, MACROS_EOH, MACROS_EOM, chacune prenant par défaut la liste propre de Postfix, avec la sentinelle none pour n’exporter rien à cette phase — une valeur vide se lit comme absente et réapplique la valeur par défaut). pepsi-setup exige NEXT_STAGE et vérifie que chaque cible de routage se résout vers une étape réelle. Voir pepsi-stage-milter(1).
57.7. État¶
Entrées :
state.origin(la session notée, rejouée pour le filtre) etstate.dsn(maintenu parallèle à toute réécriture de destinataires).Sorties :
state.milter— le verdict, la version de protocole, le temps écoulé et les modifications appliquées, plus le code de réponse du filtre et le motif de mise en quarantaine lorsqu’ils sont présents. Un message rejeté porte égalementstate.bounce, avec le code SMTP du filtre, l’état étendu RFC 3463 et le texte dans leurs propres champs.
57.8. Voir aussi¶
pepsi-stage-if, pepsi-stage-discard, pepsi-stage-bounce, pepsi-stage-dkim-sign, Fonctionnalités prises en charge, pepsi-stage-milter(1).