85.1.20. pepsi-stage-list-post

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

Handbuchabschnitt:

1

85.1.20.1.1. Name

pepsi-stage-list-post - die Mailinglisten-Beitrags-Stage der Pepsi-Pipeline.

85.1.20.1.2. Übersicht

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

85.1.20.1.3. Beschreibung

pepsi-stage-list-post ist ein Stage-Programm, das von pepsi-dispatch(1) ausgeführt wird. Eine Nachricht, die pepsi-stage-list(1) als Beitrag an eine Liste eingestuft hat, kommt hier an, und mit ihr geschehen zwei Dinge, in dieser Reihenfolge und unter diesen Namen:

  1. eine Chain aus Rules entscheidet, ob der Beitrag angenommen, zurückgehalten, abgelehnt oder verworfen wird;

  2. eine Pipeline aus Handlers entscheidet, wie ein angenommener Beitrag umgeformt und zugestellt wird.

Moderation und Verarbeitung sind verschiedene Stufen mit verschiedenem Vokabular, und wer sie vermengt, erhält etwas, das nicht Mailman ist. Die Namen der Rules, Chains, Handlers und Pipelines sind ein Kompatibilitätsbezeichner: Die REST-API meldet sie, und posting_chain und posting_pipeline einer Liste nennen sie.

Die Stage liest die Datenbank einmal, zu Beginn; berechnet alles im Speicher; und schreibt einmal, in einer einzigen Transaktion, die auch den eigenen Abschluss der Nachricht übernimmt. Ein Absturz irgendwo dazwischen spielt das Ganze erneut ab.

85.1.20.1.4. Chains und Rules

Die Standard-Posting-Chain führt der Reihe nach aus: die DMARC-Abschwächungsprüfung, die Prüfung auf fehlende Absender, die Umgehung per Moderatorpasswort (approved), die Schleifen- und Sperradressen-Rules, die standortweiten und listenbezogenen Header-Abgleiche, das Notfall-Flag sowie die Moderations-Rules für Mitglieder und Nichtmitglieder. Danach vermerken acht Rules nur, was sie finden — administrivia, implicit-destination, maximum-recipients, maximum-size, news-moderation, empty-subject, digests und suspicious-header —, und ein abschließendes any hält den Beitrag zurück, wenn eine davon angeschlagen hat. Es gibt achtzehn Rules und vier terminale Chains (accept, hold, reject, discard).

Ein korrektes Moderatorpasswort umgeht die Sperrliste. Das ist das Verhalten von Upstream, und die Kompatibilität verlangt es, aber es ist eine scharfe Kante, und es wird hier gesagt statt drei Kapitel weiter: Ein Approved:-Header mit dem Moderatorpasswort der Liste springt direkt zu accept, über die Sperrliste, die Header-Abgleiche, das Notfall-Flag und beide Moderations-Rules hinweg.

85.1.20.1.5. Die Invariante des Freigabepassworts

Ein Moderator kann einen Beitrag freigeben, indem er das Moderatorpasswort der Liste in einen Approved:-Header oder in die erste Zeile des Nachrichtentexts schreibt. In welcher Form es auch ankam, es wird entfernt, bevor die Nachricht archiviert oder zugestellt wird — im Header, im Klartext-Nachrichtentext und in der HTML-Alternative, die ein Mailprogramm daneben erzeugt hat. Ein Passwort, das bis in ein öffentliches Archiv überlebt, ist dauerhaft veröffentlicht, und das Löschen der archivierten Nachricht holt die Kopien, die bereits in fünftausend Postfächern liegen, nicht zurück.

Die Rule entfernt, was sie gefunden hat; cleanse löscht danach alle vier Header-Namen (Approve, Approved, X-Approve, X-Approved) bedingungslos, und nur das entfernt ein falsches Passwort.

85.1.20.1.6. Handlers

Die Pipeline ist die von Upstream, in der Reihenfolge von Upstream: 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 und arc-sign fehlen, weil Upstreams eigene Standard-Pipeline sie auskommentiert hat, mit Upstreams Begründung: Die gesamte Dekoration geschieht bei der Zustellung, und Dekoration bei der Zustellung würde eine hier angebrachte Signatur zerstören. Beides ist Sache von pepsi-stage-list-deliver(1) und des Signier-Endes der Pipeline.

to-usenet ist eine explizite, protokollierte Nulloperation: gateway_to_news bleibt setzbar, weil sein Entfernen die Ressource verändern würde, die ein REST-Client sieht, aber Pepsi leitet nicht an NNTP weiter, und eine stille Nulloperation ließe einen Operator glauben, sein Gateway funktioniere.

85.1.20.1.7. Geschriebene Header

Mailmans X-Mailman-*-Namensraum wird umbenannt. Nichts in Postorius oder HyperKitty liest etwas davon, und der einzige Konsument irgendeines davon ist ein Mensch, der einen zurückgehaltenen Beitrag liest:

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

(kein Gegenstück)

X-Pepsi-List-Hops

X-Message-ID-Hash

X-Message-ID-Hash (unverändert)

Message-ID-Hash und seine ältere X--Schreibweise behalten ihre Namen: Sie tragen den Namen keines Projekts, aus ihnen wird jede Archiv-URL abgeleitet, und auf ihnen hat ein Dritter am ehesten etwas aufgebaut.

X-Pepsi-List-Hops ist der einzige, den die Software je wieder einliest. Er ist die Rückfallsicherung gegen einen Abonnementzyklus über zwei oder mehr Listen — die loop-Rule erkennt nur eine Nachricht, die zur selben Liste zurückkehrt —, und er reist in einem Header, weil eine Geschwisterzeile per SMTP hinausgeht und als gewöhnliche Mail wieder hereinkommt, wobei der Zeilen-State nicht überlebt. Weil ein Header fälschbar ist, entfernt pepsi-ingress(1) eine eingehende Kopie davon von einem nicht authentifizierten Absender.

85.1.20.1.8. Das Fan-out

Jedes reguläre Mitglied mit aktivierter Zustellung, das keine Digests bezieht, erhält eine Zeile, bedingungslos. Jede trägt ihren eigenen einzigen Empfänger und ihren eigenen VERP-Umschlagabsender, <list>-bounces+<local>=<domain>@<host>, und genau das erlaubt dem Bounce-Pfad, einen Fehlschlag genau einem Abonnement zuzuordnen, ohne den Bounce zu parsen.

Über die Leitung geht die Empfängerliste, nicht die Nachricht: Die Header werden innerhalb der Datenbank kopiert und der Nachrichtentext per Referenz geteilt, sodass ein Beitrag an fünftausend Mitglieder eine Nachricht und fünftausend kleine JSON-Objekte an PostgreSQL sendet statt fünftausend Nachrichten.

Das Fan-out erfolgt in Stapeln: Es werden höchstens [pepsi-list] LIST_FANOUT_BATCH Zeilen (Standard 256) auf einmal angelegt, und die Nachricht pausiert zwischen den Stapeln, mit den verbleibenden Empfängern in ihrem eigenen state. Jede Geschwisterzeile verweist auf eine gespeicherte Kopie des (aufbereiteten) Nachrichtentexts in pepsi.workqueue_body, statt eine eigene zu tragen, sodass ein Beitrag an 5 000 Mitglieder mit einem 5-MB-Anhang den Anhang einmal speichert, nicht 5 000-mal; die Stapelung begrenzt die Zahl der gleichzeitig in Bearbeitung befindlichen Zeilen, und pepsi-queue(1) zeigt ein laufendes Fan-out statt einer Warteschlange, die plötzlich gewachsen ist.

Das Fan-out beachtet außerdem die Aufnahmegrenzen der Warteschlange ([pepsi] MAX_QUEUE_ROWS, MAX_QUEUE_BYTES, MIN_FREE_SPACE; siehe pepsi.conf(5)). Steht die Warteschlange an einer davon, gibt die Stage einen leeren Stapel aus — der ganze Rest bleibt in state — und pausiert für [pepsi] QUEUE_THROTTLE_DELAY (Standard 60 s), bevor sie erneut nachsieht, sodass ein Beitrag an eine große Liste nicht weiter eine Warteschlange füllen kann, für die pepsi-ingress(1) bereits Mail ablehnt. Ein erster Stapel, der zurückgehalten wird, archiviert den Beitrag trotzdem und bewegt seine Zähler, genau einmal, wie üblich. Die Empfängerliste wird beim ersten Stapel als Momentaufnahme festgehalten, sodass eine Abmeldung mitten in einem großen Versand keinen Nachbarn überspringen oder verdoppeln kann, und die Quellzeile wird erst von dem Stapel gelöscht, der die Liste abschließt, sodass ein Absturz diesen Stapel erneut abspielt und nicht mehr.

85.1.20.1.9. Der Beitrags-Autoresponder

autorespond_postings ist der dritte der drei Autoresponder, auf derselben Karenzzeit-Mechanik wie -request und -owner, und derjenige, über den es sich zu lesen lohnt: Er reagiert auf die Beitragsadresse, sodass ein falsch konfigurierter jedem antwortet, der an die Liste schreibt.

Drei Werte, wie überall: none, respond_and_continue und respond_and_discard. Auf einer Beitragsadresse bedeutet der letzte, dass der Beitrag beantwortet und nicht verteilt wird — eine Liste, die jeden Beitrag verschluckt und dabei jedem Absender höflich antwortet. Das will ein Eigentümer gelegentlich (eine stillgelegte Liste, die „wir sind umgezogen“ antwortet), aber nie aus Versehen, daher wird das Verwerfen auf warn protokolliert, und die Eigentümerkonsole beschriftet den Wert unmissverständlich.

Zwei Regeln, die nicht offensichtlich sind:

  • Ein unbekannter Wert ist ``none``. Eine Antwortaktion, die niemand buchstabieren kann, ist keine Erlaubnis, eine ganze Mitgliedschaft anzuschreiben.

  • Ein Beitrag, der nicht beantwortet werden konnte, wird trotzdem zugestellt. Wenn der Autoresponder ablehnt — RFC 3834 sagte nein, die Karenzzeit war nicht abgelaufen, der Umschlagabsender war leer —, verwirft respond_and_discard nicht. Eine Liste, die jede Nachricht eines Autoresponders stillschweigend verschluckt, wäre ein schlimmeres Versagen als eine fehlende Antwort.

Jede automatische Antwort durchläuft zuerst den RFC-3834-Regelsatz von pepsi_common::autoreply — der Null-Absender, ein Auto-Submitted, das nicht no ist, Precedence: bulk|list|junk, jedes List-*, ein multipart/report, Mail an sich selbst und ein Beitrag, den eine vorgelagerte Stage als Spam eingestuft hat. Dieser Regelsatz wird gemeinsam genutzt statt hier neu formuliert, weil ein Autoresponder genau durch Fehler darin eine Mailschleife baut, und eine Schleife auf einer Beitragsadresse ist eine Schleife mit einer ganzen Mitgliedschaft darin.

85.1.20.1.10. Konfiguration

DELIVERY_STAGE (erforderlich)

Wohin die Kopie jedes Mitglieds geht. Sie muss pepsi-stage-list-deliver(1) ausführen, und pepsi-setup prüft das.

BOUNCE_STAGE (erforderlich)

Wo ein abgelehnter Beitrag in eine DSN für seinen Absender umgewandelt wird. Hier bedeutet dies, was es überall außer bei pepsi-stage-list(1) bedeutet.

RESPONSE_STAGE (erforderlich)

Wohin Hinweise eingespeist werden: die Benachrichtigung des Moderators über einen zurückgehaltenen Beitrag und die Empfangsbestätigung an den Einsender. Das Signier-Ende der Pipeline, nicht die Listen-Zustell-Stage: pepsi-stage-list-deliver(1) nimmt nur die Kopie eines Mitglieds aus dem Fan-out an und lässt alles andere fehlschlagen, sodass ein dorthin gesendeter Hinweis nie hinausgeht. pepsi-setup(1) weist den falschen Wert zurück.

REMOVE_DKIM_HEADERS (Standard no)

Entfernt DKIM-Signature, DomainKey-Signature und Authentication-Results des Urhebers. Upstreams [mailman] remove_dkim_headers. Standardmäßig aus: Eine Liste, die nichts umschreibt, hinterlässt eine gültige Signatur, und sie zu entfernen verwirft echte Beweise. Schalten Sie es für Listen ein, die eine Fußzeile oder ein Betreff-Präfix hinzufügen, bei denen die verbleibende Signatur scheitern würde — und eine scheiternde Signatur ist schlimmer als keine, weil ein Fehlschlag ein Beweis für Manipulation ist und ein Fehlen nicht.

SITE_HEADER_MATCH_CHAIN (Standard hold)

Zu welcher Chain ein standortweiter Header-Abgleich springt; Upstreams [antispam] jump_chain. Standortweit, weil die Abgleiche, für die er gilt, die des Standorts sind, und ein Listeneigentümer die Spam-Regeln des Standorts nicht in ein Verwerfen verwandeln können darf.

SENDER_POSTS_PER_HOUR (Standard 30)

Wie viele Beiträge eines Absenders eine Liste pro Stunde verteilt. Ein Beitrag über der Grenze, den die Kette angenommen hätte, wird stattdessen für den Moderator zurückgehalten, mit einer entsprechenden Begründung; eine Freigabe durch einen Moderator wird nie gezählt. Das Zeitfenster beginnt mit dem ersten Beitrag des Absenders und dauert eine Stunde (pepsi.list_post_rate). Seitenweit statt als Listenattribut, weil es eine Verfügbarkeitsgrenze ist: Eine Liste vervielfacht jeden angenommenen Beitrag mit ihrer Mitgliederzahl. 0 deaktiviert sie.

MAX_HELD_MESSAGES (Standard 1000)

Wie viele Nachrichten eine Liste zur Moderation zurückhalten darf. Ein Beitrag, der darüber hinaus zurückgehalten würde, wird verworfen — nicht gespeichert, und es wird keine Mitteilung gesendet — mit einer Warnung im Log. Zurückgehaltene Beiträge werden als ihre vollständigen Wire-Bytes aufbewahrt, bis ein Moderator handelt oder max_days_to_hold der Liste (Standard: nie) sie verfallen lässt, sodass eine offene Liste ohne Obergrenze alles speichern würde, was ihr irgendjemand schickt. Der neue Beitrag wird verworfen statt des ältesten, damit eine Flut nicht die Beiträge hinausspülen kann, die noch auf einen Moderator warten. Gleichzeitige Worker können die Grenze um einige überschreiten. 0 deaktiviert sie.

NEXT_STAGE ist hier bedeutungslos, und pepsi-setup weist einen Abschnitt zurück, der es setzt: Jeder Weg aus dieser Stage heraus ist ein Abschluss.

85.1.20.1.11. State

Eingaben: state.list.id (geschrieben von pepsi-stage-list(1)), state.auth (die SPF/DKIM/DMARC-Urteile, die pepsi-ingress(1) festgehalten hat) und state.list.pending_rcpt bei einem wiederaufgenommenen Fan-out.

Ausgaben: ein state.list-Deskriptor auf jeder Geschwisterzeile — die Mitglieds-ID, seine Sprache, seine Abmelde-Seriennummer und das Duplikat-Flag.

Übergänge: beendet (der letzte Stapel eines Fan-outs, ein Zurückhalten, ein Verwerfen), beendet mit einem Nebenklon (die DSN einer Ablehnung) oder pausiert (ein Fan-out mit verbleibenden Empfängern).

85.1.20.1.12. Befehle

worker

Läuft als persistenter pepsi-dispatch(1)-Worker, der Nachrichten-IDs von der Standardeingabe liest.

85.1.20.1.13. Globale Optionen

Die übliche Gruppe — -c/–config, -L/–log, -v/–verbose, -h/–help und -V/–version —, die sich wie bei jedem Pepsi-Programm verhält; siehe pepsi-config(1).

85.1.20.1.14. Fehler und Wiederholungen

Ein Beitrag, der eine nicht mehr existierende Liste nennt (sie wurde gelöscht, nachdem der Router die Nachricht geroutet hatte), wird dem Absender gemeldet: eine DSN pro Empfänger über BOUNCE_STAGE, die besagt, dass die Mailingliste nicht mehr existiert, und der Beitrag ist fort. Eine Nachricht, die der MIME-Parser ablehnt (zu tief verschachtelt oder aufgeteilt, um sie zu verarbeiten), schlägt sofort fehl. Jeder andere Fehler – die Datenbank, eine Vorlage, das Archiv – ist ein Fehler des Hosts: Der Beitrag wird pausiert und mit Backoff bis MAX_LIFETIME wiederholt (siehe pepsi-dispatch(1)). Ein Abschnitt, der sich nicht parsen lässt, hindert den Worker überhaupt am Start, sodass die Warteschlange auf die Korrektur wartet, statt einen Beitrag nach dem anderen fehlschlagen zu lassen.

Eine Wiederholung darf nicht ändern, was der Beitrag bewirkt. SENDER_POSTS_PER_HOUR und die Karenzzeit des Beitrags-Autoresponders sind beides Ansprüche, die festgehalten werden, bevor der Beitrag festgeschrieben wird, und sind daher beide an das Warteschlangen-Token der Nachricht gebunden: Ein Beitrag, der wiederholt wird, nachdem sein Festschreiben fehlschlug, erhält dieselben Antworten wie bei seinem ersten Versuch und wird weder doppelt gezählt (und wegen unserer eigenen Wiederholung zurückgehalten) noch erhält er die Auskunft, er sei heute bereits beantwortet worden. Die automatische Antwort selbst wird in derselben Transaktion wie der Beitrag festgeschrieben – mit dem ersten Fan-out-Stapel oder mit dem Löschen eines respond_and_discard-Beitrags –, sodass ein Beitrag nie beantwortet wird, ohne behandelt zu werden, oder behandelt, ohne seine Antwort.

85.1.20.1.15. Exit-Status

0

Der Beitrag wurde angenommen, zurückgehalten, abgelehnt oder verworfen.

1

Der Worker konnte nicht laufen (seine Datenbank ließ sich nicht öffnen, oder Standardein-/-ausgabe schlug fehl). Der Grund wird in das Log geschrieben.

85.1.20.1.16. Beispiele

[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. Siehe auch

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. Fehler

Melden Sie Fehler an den Pepsi-Issue-Tracker.