85.1.4. pepsi-stage-arc

ARC-verify, sign and seal in the Pepsi pipeline

Handbuchabschnitt:

1

85.1.4.1.1. Name

pepsi-stage-arc - die ARC-Verifizierungs- und Versiegelungs-Stage der Pepsi-Pipeline.

85.1.4.1.2. Übersicht

pepsi-stage-arc [GLOBAL-OPTIONS] worker

85.1.4.1.3. Beschreibung

pepsi-stage-arc ist ein Stage-Programm, das von pepsi-dispatch(1) als persistenter Worker ausgeführt wird, der Nachrichten-IDs von der Standardeingabe liest. Es implementiert ARC (RFC 8617): Es verifiziert jede vorhandene eingehende ARC-Kette, hält das resultierende Urteil im state.auth.arc des Datensatzes fest und — wenn eine ARC-Signieridentität konfiguriert ist — versiegelt die Nachricht als unsere ADMD, indem es ein frisches ARC-Set voranstellt: ARC-Authentication-Results (AAR), ARC-Message-Signature (AMS) und ARC-Seal (AS). Die Versiegelung stellt nur Header-Zeilen voran, sodass bestehende Signaturen weiter unten in der Nachricht unberührt bleiben. Der Datensatz schaltet dann zu seinem NEXT_STAGE weiter. Das state des Datensatzes — einschließlich etwaiger RFC-3461-state.dsn-Parameter — bleibt unberührt, damit spätere Stages es beachten.

Das Signieren mit ARC ist eine Stage und nicht Teil von pepsi-ingress(1), sodass Ingress die ARC-Signierschlüssel nicht hält. Ingress verifiziert SPF, DKIM und DMARC, schreibt den eigenständigen Authentication-Results-Header und hält den gerenderten Ergebnistext unter state.origin.ar_results fest; diese Stage fügt nur das ARC-Set hinzu. Die AAR (die gemäß RFC 8617 die vollständige Bewertung des Empfängers widerspiegeln sollte) wird gebaut, indem dieses gespeicherte Fragment direkt wieder eingespeist wird — sodass SPF/DKIM/DMARC hier nicht erneut verifiziert werden und die DNS-lastige Arbeit genau einmal erledigt wird. Nur die eingehende ARC-Kette — die Ingress nicht bewertet — wird von dieser Stage verifiziert, um den Kettenvalidierungs-(cv=-)Wert zu berechnen und ihre eigene arc=-Zeile an die AAR anzuhängen.

85.1.4.1.4. Wer die Kette versiegelt hat: state.auth.arc_sealers

Neben dem Urteil hält die Stage das d= jedes ARC-Seal fest, das die Nachricht bei ihrer Ankunft trug, als JSON-Array unter state.auth.arc_sealers — kleingeschrieben, um einen etwaigen abschließenden Wurzelpunkt gekürzt, dedupliziert und nach Instanz geordnet, sodass die dem Urheber nächste ADMD zuerst kommt. Unser eigenes ARC_DOMAIN erscheint nie: Die Liste wird aus der Nachricht gelesen, bevor das eigene Set dieser Stage gerendert wird, sodass ein von uns hinzugefügtes Siegel keine Regel über Zwischenstationen erfüllen kann.

Dies existiert, weil state.auth.arc die Frage nicht beantworten kann, die ein Empfänger tatsächlich hat. RFC 8617 §8.4 stellt ausdrücklich fest, dass eine gültige Kette keine Vertrauenswürdigkeit vermittelt — sie belegt nur, dass die darin benannten ADMDs die Nachricht bearbeitet haben, und überlässt die Frage, ob diese ADMDs ehrlich sind, bewusst der lokalen Richtlinie. Ein Empfänger, der einem weiterleitenden Hop sein kaputtes SPF oder DKIM nachsehen will, muss daher die Hops benennen, die er akzeptiert, und er braucht eine Stelle, aus der er die Hops lesen kann. Das ist dieser Schlüssel; die sealer_domain-Datensätze von pepsi-stage-check-whitelist(1) sind die dagegen geschriebene Richtlinie.

Das Array wird geschrieben, ob die Kette validiert hat oder nicht — ein unvalidiertes d= ist eine Behauptung, und die Aufzeichnung dessen, was behauptet wurde, ist das, womit ein Operator eine abgelehnte Nachricht diagnostiziert. Alles, was es verbraucht, muss daher zuerst state.auth.arc = pass verlangen; check-whitelist tut das und verwirft andernfalls die ganze Liste, sodass eine Kette, die sich jedermann ausstellen kann, keinen vertrauenswürdigen Versiegler benennen kann.

85.1.4.1.5. Kettenvalidierung und cv=

RFC 8617 §5.2 definiert drei Ergebnisse der Kettenvalidierung, und das cv=-Tag des ARC-Seal, das diese Stage hinzufügt, meldet, welches davon auf die Nachricht in dem Zustand zutrifft, in dem sie eingetroffen ist:

cv=none

Es war keine ARC-Kette vorhanden — diese ADMD ist die erste, die die Nachricht versiegelt.

cv=pass

Eine Kette war vorhanden, und jedes Set wurde erfolgreich validiert.

cv=fail

Eine Kette war vorhanden und ließ sich nicht validieren.

cv=none ist eine Aussage über die Herkunft und nicht ein Fehlen: Es teilt dem nächsten Empfänger mit, dass sich vorgelagert noch niemand für diese Nachricht verbürgt hat. Es über einer vorhandenen, aber beschädigten Kette auszugeben, würde diese Kette reinwaschen — der nachgelagerte Validierer sähe ein scheinbar makelloses i=1-Set und keinerlei Hinweis darauf, dass irgendetwas manipuliert wurde.

Pepsi versiegelt daher jede eingehende Kette, die sich nicht validieren lässt, mit cv=fail — auch in den Fällen, in denen die Kette überhaupt nicht gelesen werden kann: ein ARC-Seal/ARC-Message-Signature/ARC-Authentication-Results-Header, der sich nicht parsen lässt, eine Kette, die länger ist als die von RFC 8617 erlaubten 50 Sets, und eine Kette mit ungleichen Anzahlen der drei Header-Typen. Das neue Set setzt die Instanznummerierung der Kette fort, statt sie wieder bei i=1 zu beginnen.

Die eine Ausnahme ist eine Kette, deren jüngstes ARC-Seal bereits cv=fail sagt. Nach RFC 8617 §5.1.1 lässt sich eine fehlgeschlagene Kette nicht reparieren und dieses Seal ist das letzte, sodass diese Stage gar kein Set hinzufügt und die Nachricht mit der Kette so weitergeleitet wird, wie sie ist.

Eine Kette, die nur deshalb nicht validiert werden konnte, weil eine Schlüsselabfrage fehlschlug (ein DNS-temperror), wird nicht sofort versiegelt: Dieses cv=fail wäre über eine Kette, die womöglich intakt ist, falsch, und ein Siegel lässt sich nicht zurücknehmen. Die Nachricht wird für fünf Minuten pausiert und erneut versucht, bis zu dreimal (etwa eine Viertelstunde, gezählt in state.arc_temperrors); schlagen die Abfragen dann immer noch fehl, wird sie wie jede andere nicht validierende Kette mit cv=fail versiegelt.

Der Sitzungskontext, den die Versiegelung benötigt — die authserv-id, das gerenderte Authentication-Results-Fragment, die IP des sich verbindenden Clients und der angekündigte HELO/EHLO-Name — wird aus den SMTP-Ursprungs-Metadaten gelesen, die Ingress unter dem state.origin des Datensatzes festgehalten hat; der Umschlagabsender stammt aus der mail_from-Spalte. So werden Informationen von Ingress an diese Stage weitergegeben.

Der ARC-Signierschlüssel ist der DKIM-Schlüssel der [pepsi] ARC_DOMAIN-Domain unter KEY_DIR (von pepsi-setup(1) eingerichtet), signiert mit dem einzigen von [pepsi] ARC_ALGORITHM benannten Algorithmus (ARC erlaubt eine Signatur pro Hop).

ARC gilt nur für Mail, die wir empfangen und weiterleiten: Es bewahrt die Authentifizierung einer vorgelagerten ADMD über unseren Hop hinweg. Es darf daher niemals Mail berühren, die wir erzeugen — unsere eigenen authentifizierten Einlieferungen (state.local_origin), die stattdessen von pepsi-stage-dkim-sign(1) als Autor-Domain authentifiziert werden. Eine solche Nachricht zu versiegeln würde nur die eigenen SPF/DKIM/DMARC-Urteile der Einlieferung (typischerweise ein SPF-fail für einen mobilen Client, der nicht in unserem SPF-Eintrag gelistet ist) im ARC-Set veröffentlichen. Die Stage überspringt daher jede state.local_origin-Nachricht, die ihr übergeben wird, und schaltet sie unversiegelt weiter; das macht die Stage überall dort sicher, wo sie platziert wird. Auf einem Host, der sowohl empfängt als auch einliefert, platzieren Sie sie nur auf dem eingehenden Zweig, nach der state.local_origin-Aufteilung — sodass sie nur empfangene Mail sieht, vor der SRS-Umschlag-Umschreibung und jeder anderen Transformation. Auf einem reinen Empfangs-Host ist sie natürlicherweise die anfängliche Stage ([stage-init]).

Die Versiegelung ist fail-open: Wenn der Ursprungskontext fehlt, die [pepsi]-Konfiguration oder die ARC-Schlüssel nicht verfügbar sind, der Resolver nicht eingerichtet werden kann oder die Versiegelung fehlerhaft ist, wird die Nachricht unversiegelt mit einer Warnung weitergeschaltet, statt aufgehalten zu werden (das ARC-Urteil wird dennoch festgehalten, wann immer es berechnet werden konnte). Der eigene Abschnitt der Stage ist die Ausnahme: Er wird beim Start des Workers geprüft, und einer, der sich nicht parsen lässt, lässt den Worker den Start verweigern (Exit 78), sodass der Dispatcher die Warteschlange zurückhält, bis er korrigiert ist, statt jede Nachricht unversiegelt durchzulassen.

85.1.4.1.6. Konfiguration

Die Stage wird über ihren eigenen [stage-<name>]-Abschnitt verdrahtet und konfiguriert (PROGRAM = pepsi-stage-arc, ein erforderliches NEXT_STAGE). Ihre Optionen — die DNS-Einstellungen (DNS_SERVERS, DNS_TIMEOUT) und die Signaturparameter des ARC-Sets (HEADER_CANONICALIZATION, BODY_CANONICALIZATION, SIGNATURE_EXPIRATION_DAYS, SIGNED_HEADERS) — sind zusammen mit der gemeinsamen [pepsi]-Signieridentität (ARC_DOMAIN, ARC_ALGORITHM, KEY_DIR, DKIM_SELECTOR) in pepsi.conf(5) dokumentiert.

85.1.4.1.7. State

Eingaben: state.local_origin — wenn wahr, ist die Nachricht eine unserer eigenen Einlieferungen und wird unberührt weitergeschaltet (kein ARC). Andernfalls state.origin — die von Ingress festgehaltenen SMTP-Ursprungs-Metadaten. Die authserv_id ist erforderlich, um die AAR-Identität zu reproduzieren (ohne sie schaltet die Stage unversiegelt weiter, fail-open); ar_results trägt das von Ingress gerenderte Authentication-Results-Fragment, in der AAR wörtlich wiederverwendet, sodass SPF/DKIM/DMARC nicht erneut geprüft werden müssen; remote_ip und helo verfeinern den ARC-Verifizierungskontext.

Ausgaben: Die Stage überschreibt state.auth.arc mit dem neu bewerteten Kettenurteil (ersetzt die none-Saat, die Ingress dort hinterlässt), schreibt state.auth.arc_sealers (siehe oben) und stellt das ARC-Set der headers-Spalte voran; der Rest von state — einschließlich der anderen state.auth-Urteile und state.dsn — bleibt unverändert erhalten. Während sie einen DNS-temperror abwartet, zählt sie die Versuche in state.arc_temperrors. Das State-Layout wird in pepsi.state(7) beschrieben.

Übergänge: schaltet zu NEXT_STAGE weiter — nach dem Voranstellen des ARC-Sets, nach dem bloßen Festhalten des Urteils in state.auth.arc, wenn keine Signieridentität konfiguriert ist, oder sofort (unberührt) bei einer state.local_origin-Nachricht, die nie ARC-verarbeitet wird. Das einzige andere Ergebnis ist eine Pause von fünf Minuten, höchstens dreimal, für eine eingehende Kette, deren Validierung auf einen DNS-temperror stieß (siehe oben). Es gibt keine Verzweigung; die Stage schlägt nie fehl, leitet nie um und schließt nie ab.

85.1.4.1.8. Befehle

Als Worker ausgeführt, verifiziert und versiegelt das Programm jede Nachricht und schaltet sie weiter.

85.1.4.1.9. Globale Optionen

-c FILE, –config FILE

Liest die Konfiguration aus FILE statt aus dem Standard-Suchpfad.

-L LOGLEVEL, –log LOGLEVEL

Setzt die Log-Ausführlichkeit (error, warn, info, debug, trace; Standardwert info).

-h, –help

Gibt eine Verwendungsübersicht aus und beendet sich.

-V, –version

Gibt die Version aus und beendet sich.

85.1.4.1.10. Exit-Status

Das Ergebnis jeder Nachricht wird pepsi-dispatch(1) in der Statuszeile des Workers gemeldet, nicht als Exit-Status.

0

Der Worker lief, bis seine Standardeingabe geschlossen wurde.

1

Ein fataler Fehler ist aufgetreten (unlesbare Konfiguration, die Datenbank konnte nicht geöffnet werden, oder die Standardein- bzw. -ausgabe schlug fehl). Der Grund wird in das Protokoll geschrieben.

78

Der Worker hat den Start verweigert: Sein Abschnitt lässt sich nicht parsen. Der Dispatcher reiht erneut ein, was er übergeben hatte, und versucht die Stage später wieder.

85.1.4.1.11. Siehe auch

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

85.1.4.1.12. Fehler

Melden Sie Fehler an den Pepsi-Issue-Tracker.