85.1.20. pepsi-stage-list-post

moderate, transform and fan out a post to a mailing list

Section du manuel:

1

85.1.20.1.1. Nom

pepsi-stage-list-post - l’étape d’envoi aux listes de diffusion du pipeline Pepsi.

85.1.20.1.2. Synopsis

pepsi-stage-list-post [OPTIONS-GLOBALES] worker

85.1.20.1.3. Description

pepsi-stage-list-post est un programme d’étape exécuté par pepsi-dispatch(1). Un message que pepsi-stage-list(1) a reconnu comme un message destiné à une liste arrive ici, et deux choses lui arrivent, dans cet ordre et sous ces noms :

  1. une chaîne de règles décide si le message est accepté, retenu, rejeté avec avis ou rejeté sans avis ;

  2. un pipeline de gestionnaires décide comment un message accepté est transformé et remis.

La modération et le traitement sont des étapes différentes avec des vocabulaires différents, et les confondre produit quelque chose qui n’est pas Mailman. Les noms des règles, chaînes, gestionnaires et pipelines sont un identifiant de compatibilité : l’API REST les rapporte, et le posting_chain et le posting_pipeline d’une liste les nomment.

L’étape lit la base de données une fois, au début ; calcule tout en mémoire ; et écrit une fois, dans une transaction unique qui porte aussi le terminal du message lui-même. Un plantage n’importe où au milieu rejoue le tout.

85.1.20.1.4. Chaînes et règles

La chaîne d’envoi par défaut exécute, dans l’ordre : le contrôle d’atténuation DMARC, le contrôle d’absence d’expéditeur, le contournement par mot de passe de modérateur (approved), les règles de boucle et d’adresse interdite, les correspondances d’en-têtes du site et de la liste, l’indicateur d’urgence, et les règles de modération des membres et des non-membres. Ensuite, huit règles ne font que consigner ce qu’elles trouvent — administrivia, destination implicite, nombre maximal de destinataires, taille maximale, modération des news, objet vide, résumés et en-tête suspect — et un any final retient le message si l’une d’elles a réagi. Il y a dix-huit règles et quatre chaînes terminales (accept, hold, reject, discard).

Un mot de passe de modérateur correct contourne la liste des interdictions. C’est le comportement d’amont et la compatibilité l’exige, mais c’est une arête vive, et elle est signalée ici plutôt que trois chapitres plus loin : un en-tête Approved: portant le mot de passe de modérateur de la liste saute directement à accept, par-dessus la liste des interdictions, les correspondances d’en-têtes, l’indicateur d’urgence et les deux règles de modération.

85.1.20.1.5. L’invariant du mot de passe d’approbation

Un modérateur peut approuver un message en plaçant le mot de passe de modérateur de la liste dans un en-tête Approved: ou dans la première ligne du corps. Sous quelque forme qu’il soit arrivé, il est retiré avant que le message ne soit archivé ou remis — dans l’en-tête, dans le corps en texte brut et dans l’alternative HTML qu’un client de messagerie a produite à côté. Un mot de passe qui survit jusque dans une archive publique est publié pour toujours, et purger le message archivé ne rappelle pas les copies déjà présentes dans cinq mille boîtes aux lettres.

La règle retire ce qu’elle a reconnu ; cleanse supprime ensuite inconditionnellement les quatre noms d’en-tête (Approve, Approved, X-Approve, X-Approved), ce qui est la seule chose qui retire un mauvais mot de passe.

85.1.20.1.6. Gestionnaires

Le pipeline est celui d’amont, dans l’ordre d’amont : validate-authenticity, mime-delete, tagger, member-recipients, avoid-duplicates, cleanse, cleanse-dkim, cook-headers, subject-prefix, rfc-2369, to-archive, to-digest, to-usenet, after-delivery, acknowledge, dmarc, to-outgoing.

decorate et arc-sign sont absents parce qu’amont les a lui-même commentés dans son pipeline par défaut, et pour les raisons d’amont : toute la décoration se fait à la remise, et décorer à la remise casserait une signature apposée ici. Les deux appartiennent à pepsi-stage-list-deliver(1) et à la queue de signature.

to-usenet est une opération vide explicite et journalisée : gateway_to_news reste réglable parce que le retirer changerait la ressource que voit un client REST, mais Pepsi ne fait pas passerelle vers NNTP, et une opération vide silencieuse laisserait un opérateur croire que sa passerelle fonctionne.

85.1.20.1.7. En-têtes écrits

L’espace de noms X-Mailman-* de Mailman est renommé. Rien dans Postorius ni HyperKitty n’en lit quoi que ce soit, et le seul lecteur de l’un quelconque de ces en-têtes est un humain qui consulte un message retenu :

Mailman 3

Pepsi

X-Mailman-Version

X-Pepsi-List-Version

X-Mailman-Rule-Hits

X-Pepsi-List-Rule-Hits

X-Mailman-Rule-Misses

X-Pepsi-List-Rule-Misses

X-Mailman-Approved-At

X-Pepsi-List-Approved-At

X-Mailman-Hash-ID

X-Pepsi-List-Hash-ID

X-Mailman-Duplicate

X-Pepsi-List-Duplicate

X-Mailman-Copy

X-Pepsi-List-Copy

(pas d’équivalent)

X-Pepsi-List-Hops

X-Message-ID-Hash

X-Message-ID-Hash (inchangé)

Message-ID-Hash et son ancienne graphie X- gardent leurs noms : ils ne portent le nom d’aucun projet, c’est d’eux que dérive chaque URL d’archive, et ce sont ceux sur lesquels un tiers a le plus de chances d’avoir bâti quelque chose.

X-Pepsi-List-Hops est le seul que le logiciel relise. C’est le filet contre un cycle d’abonnements passant par deux listes ou plus — la règle loop ne repère qu’un message qui revient à la même liste — et il voyage dans un en-tête parce qu’un enregistrement frère part par SMTP et revient comme du courrier ordinaire, où l’état de l’enregistrement ne survit pas. Comme un en-tête est falsifiable, pepsi-ingress(1) retire une copie entrante provenant d’un expéditeur non authentifié.

85.1.20.1.8. L’éclatement

Chaque membre ordinaire dont la remise est activée et qui n’est pas en résumé obtient un enregistrement, inconditionnellement. Chacun porte son unique destinataire et son propre expéditeur d’enveloppe VERP, <list>-bounces+<local>=<domain>@<host>, ce qui permet au chemin des rejets d’attribuer un échec à exactement un abonnement sans analyser le rejet.

Ce qui traverse le réseau est la liste des destinataires, et non le message : les en-têtes sont copiés à l’intérieur de la base de données et le corps est partagé par référence, de sorte qu’un message envoyé à cinq mille membres envoie à PostgreSQL un message et cinq mille petits objets JSON plutôt que cinq mille messages.

L’éclatement se fait par lots : au plus [pepsi-list] LIST_FANOUT_BATCH lignes (256 par défaut) sont matérialisées à la fois, et le message se met en pause entre les lots avec les destinataires restants dans son propre state. Chaque ligne sœur référence une unique copie stockée du corps (préparé) dans pepsi.workqueue_body au lieu de porter la sienne, de sorte qu’une contribution à 5 000 membres avec une pièce jointe de 5 Mo stocke la pièce jointe une fois, et non 5 000 fois ; le traitement par lots borne le nombre de lignes en vol, et pepsi-queue(1) montre un éclatement en cours plutôt qu’une file d’attente qui aurait soudain grossi.

L’éclatement obéit aussi aux limites d’admission dans la file d’attente ([pepsi] MAX_QUEUE_ROWS, MAX_QUEUE_BYTES, MIN_FREE_SPACE ; voir pepsi.conf(5)). Lorsque la file est à l’une d’elles, l’étape émet un lot vide — tout le reste demeure dans state — et se met en pause pendant [pepsi] QUEUE_THROTTLE_DELAY (60 s par défaut) avant de regarder à nouveau, de sorte qu’une contribution à une grande liste ne peut pas continuer à remplir une file pour laquelle pepsi-ingress(1) refuse déjà du courrier. Un premier lot retenu archive tout de même la contribution et fait évoluer ses compteurs, exactement une fois, comme d’habitude. La liste des destinataires est figée au premier lot, de sorte qu’un désabonnement au milieu d’un gros envoi ne peut ni sauter ni doubler un voisin, et la ligne source n’est supprimée que par le lot qui termine la liste, de sorte qu’un plantage rejoue ce lot et rien de plus.

85.1.20.1.9. Le répondeur automatique des contributions

autorespond_postings est le troisième des trois répondeurs automatiques, sur le même mécanisme de délai de grâce que -request et -owner, et celui qui mérite qu’on s’y attarde : il se déclenche sur l’adresse d”envoi, de sorte qu’un répondeur mal configuré répond à tous ceux qui écrivent à la liste.

Trois valeurs, comme partout : none, respond_and_continue et respond_and_discard. Sur une adresse d’envoi, la dernière signifie que la contribution reçoit une réponse et n’est pas distribuée — une liste qui avale chaque contribution tout en répondant poliment à chaque expéditeur. C’est parfois ce que veut un propriétaire (une liste retirée qui répond « nous avons déménagé ») et jamais ce qu’il veut par accident : la suppression est donc journalisée au niveau warn et la console du propriétaire étiquette clairement la valeur.

Deux règles qui ne vont pas de soi :

  • Une valeur non reconnue vaut ``none``. Une action de réponse que personne ne sait écrire n’autorise pas à envoyer du courrier à tout l’effectif.

  • Une contribution à laquelle il n’a pas pu être répondu est tout de même distribuée. Lorsque le répondeur automatique s’abstient — la RFC 3834 a dit non, le délai de grâce n’avait pas expiré, l’expéditeur d’enveloppe était nul — respond_and_discard ne supprime pas. Une liste qui avalerait silencieusement chaque message d’un répondeur automatique serait un échec pire qu’une réponse manquante.

Chaque réponse automatique passe d’abord par l’ensemble de règles RFC 3834 de pepsi_common::autoreply — l’expéditeur nul, un Auto-Submitted qui n’est pas no, Precedence: bulk|list|junk, tout List-*, un multipart/report, le courrier à soi-même, et une contribution qu’une étape en amont a qualifiée d’indésirable. Cet ensemble de règles est partagé plutôt que reformulé ici, car se tromper est la façon dont un répondeur automatique construit une boucle de courrier, et une boucle sur une adresse d’envoi est une boucle qui contient tout un effectif.

85.1.20.1.10. Configuration

DELIVERY_STAGE (obligatoire)

Où va la copie de chaque membre. Elle doit exécuter pepsi-stage-list-deliver(1), et pepsi-setup le vérifie.

BOUNCE_STAGE (obligatoire)

Où une contribution rejetée est transformée en DSN pour son expéditeur. Ici, cela signifie ce que cela signifie partout sauf dans pepsi-stage-list(1).

RESPONSE_STAGE (obligatoire)

Où les avis sont injectés : la notification de contribution retenue destinée au modérateur et l’accusé de réception destiné à l’auteur. La fin de chaîne de signature, et non l’étape de remise de liste : pepsi-stage-list-deliver(1) n’accepte que la copie d’un membre issue de l’éclatement et fait échouer tout le reste, de sorte qu’un avis envoyé là ne part jamais. pepsi-setup(1) refuse la mauvaise valeur.

REMOVE_DKIM_HEADERS (par défaut no)

Supprimer les DKIM-Signature, DomainKey-Signature et Authentication-Results de l’expéditeur d’origine. Le [mailman] remove_dkim_headers amont. Désactivé par défaut : une liste qui ne réécrit rien laisse une signature valide, et la supprimer jette une preuve réelle. Activez-le pour les listes qui ajoutent un pied de page ou un préfixe de sujet, où la signature restante échouerait — et une signature en échec est pire qu’aucune, car un échec est une preuve de falsification et une absence ne l’est pas.

SITE_HEADER_MATCH_CHAIN (par défaut hold)

La chaîne vers laquelle saute une correspondance d’en-tête à l’échelle du site ; le [antispam] jump_chain amont. À l’échelle du site parce que les correspondances auxquelles elle s’applique sont celles du site, et qu’un propriétaire de liste ne doit pas pouvoir transformer les règles anti-spam du site en suppression.

SENDER_POSTS_PER_HOUR (par défaut 30)

Le nombre de contributions d’un même expéditeur qu’une liste distribue par heure. Une contribution au-delà de la limite que la chaîne aurait acceptée est à la place retenue pour le modérateur, le motif l’indiquant ; une libération par un modérateur n’est jamais comptée. La fenêtre s’ouvre à la première contribution de l’expéditeur et dure une heure (pepsi.list_post_rate). À l’échelle du site plutôt qu’attribut de liste, parce que c’est une limite de disponibilité : une liste multiplie chaque contribution acceptée par son effectif. 0 la désactive.

MAX_HELD_MESSAGES (par défaut 1000)

Le nombre de messages qu’une liste peut retenir pour modération. Une contribution qui serait retenue au-delà est rejetée sans avis — non stockée, et sans notification — avec un avertissement dans le journal. Les contributions retenues sont conservées sous forme de leurs octets bruts complets jusqu’à ce qu’un modérateur agisse ou que le max_days_to_hold de la liste (par défaut : jamais) les fasse expirer, de sorte que, sans plafond, une liste ouverte stockerait tout ce que n’importe qui lui envoie. C’est la nouvelle contribution qui est abandonnée plutôt que la plus ancienne, afin qu’un flot ne puisse pas chasser les contributions qui attendent encore un modérateur. Des workers concurrents peuvent dépasser de quelques unités. 0 la désactive.

NEXT_STAGE n’a pas de sens ici et pepsi-setup refuse une section qui le définit : chaque chemin de sortie de cette étape est terminal.

85.1.20.1.11. État

Entrées : state.list.id (écrit par pepsi-stage-list(1)), state.auth (les verdicts SPF/DKIM/DMARC notés par pepsi-ingress(1)) et state.list.pending_rcpt sur un éclatement repris.

Sorties : un descripteur state.list sur chaque ligne sœur — l’identifiant du membre, sa langue, son numéro de série de désabonnement et l’indicateur de doublon.

Transitions : termine (le dernier lot d’un éclatement, une retenue, une suppression), termine avec un clone annexe (le DSN d’un rejet), ou se met en pause (un éclatement dont il reste des destinataires).

85.1.20.1.12. Commandes

worker

Exécuté comme un worker persistant de pepsi-dispatch(1), lisant les identifiants de message sur l’entrée standard.

85.1.20.1.13. Options globales

L’ensemble habituel — -c/–config, -L/–log, -v/–verbose, -h/–help et -V/–version — se comportant comme pour tout programme Pepsi ; voir pepsi-config(1).

85.1.20.1.14. Erreurs et réessais

Une contribution qui nomme une liste qui n’existe plus (elle a été supprimée après que le routeur a acheminé le message) est signalée à l’expéditeur : un DSN par destinataire via BOUNCE_STAGE, indiquant que la liste de diffusion n’existe plus, et la contribution disparaît. Un message que l’analyseur MIME refuse (imbriqué ou fragmenté trop profondément pour être traité) est mis en échec immédiatement. Toute autre erreur – la base de données, un modèle, l’archive – est une défaillance de l’hôte : la contribution est mise en pause et réessayée avec un back-off jusqu’à MAX_LIFETIME (voir pepsi-dispatch(1)). Une section qui ne s’analyse pas empêche le worker de démarrer du tout, de sorte que la file attend la correction au lieu de mettre en échec une contribution après l’autre.

Un réessai ne doit pas changer ce que fait la contribution. SENDER_POSTS_PER_HOUR et le délai de grâce du répondeur automatique de publication sont tous deux des réservations enregistrées avant la validation de la contribution ; ils sont donc tous deux indexés sur le jeton de file du message : une contribution réessayée après l’échec de sa validation obtient les réponses qu’a obtenues sa première tentative, et n’est ni comptée deux fois (et retenue à cause de notre propre réessai) ni informée qu’elle a déjà reçu une réponse aujourd’hui. La réponse automatique elle-même est validée dans la même transaction que la contribution – avec le premier lot de l’éclatement, ou avec la suppression d’une contribution respond_and_discard – de sorte qu’une contribution n’est jamais répondue sans être traitée, ni traitée sans sa réponse.

85.1.20.1.15. Code de sortie

0

La contribution a été acceptée, retenue, rejetée ou supprimée.

1

Le worker n’a pas pu s’exécuter (sa base de données n’a pas pu être ouverte, ou l’entrée/sortie standard a échoué). La raison est écrite dans le journal.

85.1.20.1.16. Exemples

[stage-list-post]
PROGRAM = pepsi-stage-list-post
DELIVERY_STAGE = list-deliver
BOUNCE_STAGE = bounce
RESPONSE_STAGE = dkim-sign
REMOVE_DKIM_HEADERS = no

85.1.20.1.17. Voir aussi

pepsi-list(1), pepsi-stage-list(1), pepsi-stage-list-deliver(1), pepsi-stage-dkim-sign(1), pepsi-ingress(1), pepsi-dispatch(1), pepsi.conf(5), pepsi.state(7), pepsi-setup(1)

85.1.20.1.18. Bogues

Signalez les bogues au gestionnaire de tickets de Pepsi.