85.1.10. pepsi-stage-relay-to-internet¶
deliver a message directly to the recipient’s MX hosts
- Handbuchabschnitt:
1
85.1.10.1.1. Name¶
pepsi-stage-relay-to-internet - die Direkt-zu-MX-Zustell-Stage der Pepsi-Pipeline.
85.1.10.1.2. Übersicht¶
pepsi-stage-relay-to-internet [GLOBAL-OPTIONS] worker
pepsi-stage-relay-to-internet [GLOBAL-OPTIONS] mta-sts DOMAIN
85.1.10.1.3. Beschreibung¶
pepsi-stage-relay-to-internet ist ein Stage-Programm, das von pepsi-dispatch(1) als persistenter Worker ausgeführt wird, der Nachrichten-IDs von der Standardeingabe liest. Es lädt diesen pepsi.workqueue-Datensatz (und weigert sich zu handeln, sofern sein status nicht running ist), liest seinen [stage-<stage>]-Abschnitt und stellt die Nachricht direkt an die Mail-Exchanger der Empfängerdomain zu, unter Verwendung des eigenen Umschlagabsenders der Nachricht (des Null-Absenders <> bei Bounces). Die Zustellung erfolgt mit einem Empfänger je Versuch: Ein Datensatz, der noch mehrere Empfänger enthält, wird zunächst in einen Datensatz je Empfänger aufgeteilt (jeder mit seinem Ausschnitt von state.dsn), die alle erneut in diese Stage eintreten. Eine Nachricht mit mehr als MAX_HOP_COUNT Received:-Headerfeldern wird als Mailschleife behandelt und schlägt dauerhaft fehl.
Es führt seine eigene DNS-MX-Abfrage durch: Kandidaten-Hosts werden in aufsteigender MX-Präferenz versucht (Hosts gleicher Präferenz in zufälliger Reihenfolge), eine Domain ohne MX-Einträge fällt auf ihre Adress-Einträge zurück (der implizite MX von RFC 5321 §5.1), und eine nicht existierende Domain oder ein RFC-7505-Null-MX ist ein dauerhafter Fehler — der Null-MX wird als 556 mit dem erweiterten Status 5.1.10 gemeldet (RFC 7505 §4.1), sodass der Bounce sagt, dass die Adresse nie zustellbar sein kann, statt wie jedes andere 550 auszusehen. Die A/AAAA-Adressen jedes MX-Hosts werden mit Happy-Eyeballs ausgewählt und pro Adresse in der dns_address-Tabelle zwischengespeichert; die ADDRESS_FAMILY-Einstellung (mit den Familien geschnitten, die der Host routen kann) wählt, welche zu verwenden ist. Verbindungen werden auf Port 25 hergestellt. Jede Abfrage erfolgt für den vollqualifizierten Namen, sodass eine search-Liste in /etc/resolv.conf nie angewandt wird: andernfalls würde eine Domain ohne MX-Einträge ein zweites Mal unter jeder Such-Domain nachgeschlagen, und das daraus entstehende NXDOMAIN — nicht das NODATA der Domain selbst — würde gemeldet, was eine gewöhnliche Zustellung über den impliziten MX in einen dauerhaften Fehler verwandelt.
Sofern nicht mit MTA_STS = no deaktiviert, wird die MTA-STS-Richtlinie der Empfängerdomain (RFC 8461) beachtet: Eine enforce-Richtlinie beschränkt die Zustellung auf die gelisteten MX-Hosts über STARTTLS mit einem Zertifikat, das für den Host gültig ist, und lässt die Nachricht (vorübergehend) fehlschlagen, wenn kein MX passt. Eine testing-Richtlinie wird auf dieselbe Weise validiert — der Versuch und sein Ergebnis werden für TLS-Reporting festgehalten — blockiert aber nie die Zustellung: Ein Transportsicherheitsfehler (oder eine Richtlinie, die auf keinen MX passt) fällt auf opportunistisches STARTTLS über alle MX-Hosts zurück, sodass der Domain-Eigentümer aus den Berichten lernt, ob enforce Mail brechen würde, bevor er darauf umschaltet. Domains ohne Richtlinie verwenden opportunistisches STARTTLS. Richtlinienabfragen sind nur dann fail-open, wenn nichts zwischengespeichert ist.
Richtlinien werden für ihr max_age zwischengespeichert (auf eine Woche gedeckelt). Die _mta-sts-TXT-id wird bei jeder Verwendung einer zwischengespeicherten Richtlinie erneut geprüft (RFC 8461 §3.1): Eine geänderte id löst eine sofortige Neubeschaffung aus, während ein vorübergehend unerreichbarer Eintrag (oder eine fehlgeschlagene Neubeschaffung einer geänderten Richtlinie) die noch gültige zwischengespeicherte Richtlinie behält. Ein Richtlinienhost, der ein vorübergehendes 5xx/429 zurückgibt, wird als „Beschaffung fehlgeschlagen“ behandelt (fail-open, nicht als „keine Richtlinie“ zwischengespeichert), während ein 404 oder ein nicht-text/plain-Body eine definitive Abwesenheit ist.
DANE (RFC 7672): Wenn DANE warn (der Standardwert) oder strict ist und der Ziel-MX-Host DNSSEC-validierte TLSA-Einträge veröffentlicht, wird das MX-Zertifikat gegen sie statt per PKIX authentifiziert — dies hat für diese Verbindung Vorrang vor MTA-STS. DANE beruht auf DNSSEC: Der konfigurierte Resolver (DNS_SERVERS oder resolv.conf) muss validieren und über einen vertrauenswürdigen Pfad erreicht werden, weil Pepsi dem AD-(Authentic-Data-)Bit der Antwort vertraut, statt selbst zu validieren. TLSA- (und MX-)Antworten ohne das AD-Bit werden als „kein DANE“ behandelt. Im strict-Modus schiebt ein nutzbarer, aber nicht übereinstimmender Eintrag (oder eine fehlgeschlagene TLSA- oder MX-Sicherheitsabfrage, z. B. für einen DNSSEC-bogus-Eintrag) die Nachricht als vorübergehenden Fehler auf; im warn-Modus wird die Nichtübereinstimmung protokolliert und die Zustellung fährt fort. Dieser Vorrang gilt nur dort, wo DANE mindestens so stark ist wie das, was es ersetzt: Für einen Host, der von einer enforce-MTA-STS-Richtlinie abgedeckt ist, wird der warn-Modus nicht verwendet (eine Nichtübereinstimmung im warn-Modus nimmt jedes Zertifikat an, was die Validierungsanforderung von RFC 8461 §5 verwerfen würde) — das Zertifikat wird per PKIX validiert und die Ersetzung protokolliert. Der strict-Modus hat dort weiterhin Vorrang. Eine Domain, die einfach keine TLSA-Einträge veröffentlicht, wird selbst im strict-Modus normal zugestellt (DANE ist opportunistisch — es greift nur, wenn Einträge existieren). Eine Domain, die TLSA-Einträge veröffentlicht, von denen keiner nutzbar ist (eine PKIX-Verwendung oder ein Matching-Typ, den dieser Build nicht implementiert), ist nicht dasselbe: Nach RFC 7672 §2.2 hat sie den Host dennoch auf TLS festgelegt, sodass STARTTLS für ihn verpflichtend wird — unauthentifiziert, aber nie Klartext — und ein Hop, der es nicht anbietet, schiebt die Nachricht auf.
SMTP TLS Reporting (RFC 8460): Wenn [pepsi-tlsrpt] SEND_REPORTS gesetzt ist, wird das Ergebnis jeder TLS-Sitzung — ein Erfolg oder ein klassifizierter Fehler (starttls-not-supported, Zertifikat/Handshake oder DANE/MTA-STS-Validierung) — als Aggregat-Zähler in pepsi.tls_session festgehalten, geschlüsselt nach der angewandten Richtlinie (tlsa/sts/no-policy-found) und MX-Host. pepsi-tlsrpt(1) stellt diese später zu täglichen Berichten zusammen. Das Festhalten erfolgt nach bestem Bemühen (ein Datenbank-Schluckauf lässt eine Zustellung nie fehlschlagen) und wird für eine Berichtsnachricht selbst übersprungen (state.tlsrpt).
Bei Erfolg wird der Datensatz entfernt (oder, falls die Stage NEXT_STAGE setzt, weitergeschaltet). Ein vorübergehender Fehler pausiert die Nachricht mit einem exponentiellen Backoff (von pepsi-dispatch(1) erneut eingereiht, wenn ihr timeout abläuft); nach MAX_LIFETIME wird der Fehler als dauerhaft behandelt. Ein dauerhafter Fehler einer gewöhnlichen Nachricht wird zum BOUNCE_STAGE der Stage geroutet (oder, ohne ein konfiguriertes, wird der Datensatz als failed markiert). Ein dauerhafter Fehler eines Bounces (Null-Absender) wird nie erneut gebounct: Eine Kopie wird, falls konfiguriert, an POSTMASTER zugestellt, andernfalls wird sie verworfen, und der Datensatz wird dann entfernt.
DSN (RFC 3461): Wenn der MX des Empfängers die DSN-Erweiterung ankündigt, werden die vom Ursprung angeforderten Parameter (RET/ENVID und das NOTIFY/ORCPT des Empfängers, aus dem state.dsn des Datensatzes gelesen) an ihn propagiert; andernfalls werden sie weggelassen. Bei einem dauerhaften Fehler werden das NOTIFY/ORCPT des Empfängers und das ENVID an die Bounce-Stage übergeben, sodass eine Failure-DSN nur dann erzeugt wird, wenn angefordert. Diese Stage bewahrt state.dsn.
Wenn das globale [pepsi]-ORIGINATE_SUCCESS_DSN aktiviert ist und der Empfänger einer erfolgreichen Zustellung NOTIFY=SUCCESS angefordert hat, wird die Nachricht (nach der Zustellung) zu BOUNCE_STAGE geroutet, das einen positiven (Action: delivered-)Bericht an den Absender ausgibt. Mit ausgeschaltetem Flag oder ohne eine SUCCESS-Anforderung wird eine erfolgreiche Zustellung einfach abgeschlossen.
Wenn DELAY_DSN_AFTER gesetzt ist und eine noch nicht zugestellte Nachricht mindestens so lange eingereiht war, wird dem Absender eine einmalige „verzögerte“ (Action: delayed-)DSN gesendet — aber nur, wenn der Absender NOTIFY=DELAY angefordert hat (Delay hat keinen impliziten Standardwert). Die ursprüngliche Nachricht wird weiterhin wiederholt; die Warnung ist eine separate Nachricht, die durch BOUNCE_STAGE geroutet und höchstens einmal gesendet wird.
Herkunftsnachweis: Wenn der gemeinsame [pepsi-origin]-Abschnitt konfiguriert ist (standardmäßig an — pepsi-setup(1) erzeugt ein Geheimnis, wenn keines gesetzt ist), wird jede ausgehende Nachricht vor der Zustellung mit einem Pepsi-Origin-Header gestempelt — einem HMAC über das Ursprungs-Absenderkonto, eine frische 128-Bit-Nonce, einen Zeitstempel und unser HOSTNAME — und die Nonce wird in pepsi.origin_nonce festgehalten (zwei Wochen gültig). Dasselbe Stempeln wird mit pepsi-stage-relay-to-smarthost(1) geteilt, sodass eine Nachricht einen Nachweis erhält, welchen ausgehenden Pfad sie auch nimmt. Ein zurückkehrender Bounce bettet diesen Header ein und lässt pepsi-stage-anti-spam(1) bestätigen, dass der Bounce eine Antwort auf Mail ist, die wir tatsächlich gesendet haben. Der Header wird wie der Received:-Trace-Header vorangestellt (über etwaige bestehende Signaturen, sodass er keine stört) und bleibt unsigniert. Eine Nonce, die sich nicht festhalten lässt (ein Datenbankfehler), hält die Nachricht zurück — sie wird wie jeder Fehler des Hosts wiederholt, siehe pepsi-dispatch(1) —, denn ein Header, dessen Nonce nicht verzeichnet ist, ließe jeden Bounce auf die Nachricht gefälscht aussehen. Ohne den [pepsi-origin]-Abschnitt wird kein Header hinzugefügt; ein Abschnitt, dessen Geheimnis sich nicht lesen lässt, hindert die Worker der Stage am Start (die Warteschlange wird angehalten, und das Journal sagt, warum), genau wie pepsi-setup(1) ihn ablehnt.
Inhalts-Herabstufung (RFC 6152 / RFC 6531): Die Nachricht wird an die Erweiterungen angepasst, die der MX tatsächlich ankündigt, statt wörtlich gesendet zu werden. Wenn der Nachrichtentext 8-Bit-Oktette enthält und der MX 8BITMIME unterstützt, trägt der Umschlag BODY=8BITMIME; wenn er es nicht tut, wird der MIME-Baum durchlaufen und jeder 8-Bit-Blattteil mit einer 7-Bit-Content-Transfer-Encoding umkodiert (Quoted-Printable für text/*, sonst Base64). Was ein Teil deklariert, gilt nicht als Beweis dafür, was er enthält: Ein als base64 gekennzeichneter Teil, der rohe 8-Bit-Oktette trägt, wird ebenfalls umkodiert, und ein multipart/*, dessen deklariertes boundary= nirgends in seinem Nachrichtentext vorkommt, hat keine zu erhaltenden Teile und wird als Ganzes kodiert. Das Ergebnis wird dann erneut geprüft, denn der Durchlauf erfolgt nach bestem Bemühen über eine Nachricht, die niemand validiert hat (ein über die Tiefengrenze hinaus geschachtelter Baum wird unverändert durchgereicht): Ein Nachrichtentext, der weiterhin 8-Bit-Oktette trägt, ist ein dauerhafter Fehler (554) und nichts, was man auf die Leitung gibt, was RFC 6152 §3 „unter allen Umständen“ verbietet.
Wenn die Nachricht SMTPUTF8 benötigt (UTF-8 in den Headern oder im Umschlag) und der MX es unterstützt, trägt der Umschlag SMTPUTF8; wenn er es nicht tut, werden UTF-8-Header-Felder als RFC-2047-Encoded-Words umgeschrieben — an den drei Stellen, an denen RFC 2047 §5 eines erlaubt, sodass das Feld weiterhin parst: einem unstrukturierten Feldinhalt, einer Display-Name-phrase (ein Quoted-String wird zu einem Encoded-Word, ohne die Anführungszeichen) und dem Inneren eines comment (die Klammern bleiben). Ein Nicht-ASCII-Parameter von Content-Type oder Content-Disposition, für den §5 kein Encoded-Word kennt, erhält stattdessen die erweiterte Form von RFC 2231 (filename*=UTF-8''caf%C3%A9.txt, aufgeteilt in *0*/*1*-Abschnitte, wenn eine Zeile ihn nicht fassen würde); ASCII-Parameter werden unverändert übernommen. Dies betrifft den Header-Block jedes MIME-Teils, nicht nur den der Nachricht, denn dort steht der Dateiname eines Anhangs. Ein Nicht-ASCII-boundary= hat überhaupt keine kodierte Form — es muss Oktett für Oktett mit den Begrenzungszeilen des Nachrichtentexts übereinstimmen, und RFC 2046 erlaubt dafür nur 7-Bit-Zeichen —, daher erhält der Multipart eine frische zufällige ASCII-Begrenzung, und seine Begrenzungszeilen werden passend umgeschrieben, statt für die Nachricht einen Bounce zu erzeugen. Ein Teil-Header, der sich nicht umwandeln lässt (etwa rohe Latin-1-Oktette), wird unverändert kopiert und der erneuten 8BITMIME-Prüfung überlassen, da der Transport ihn als Inhalt des Nachrichtentexts sieht. Eine Nicht-ASCII-Umschlagadresse und ein Nicht-ASCII-Oktett innerhalb einer Header-Adresse können nicht herabgestuft werden — eine solche Nachricht ist ein dauerhafter Fehler (gebounct) gegenüber einem Nicht-SMTPUTF8-Hop. Das Umkodieren eines Nachrichtentexts zerstört zwangsläufig jede den Nachrichtentext abdeckende Signatur, die bereits auf der Nachricht ist (der DKIM-Body-Hash des Urhebers und die ARC-Message-Signature von pepsi-stage-arc(1)); dies ist unvermeidbar, da die Fähigkeiten des MX erst nach der MX-Auswahl bekannt sind, und in der Praxis selten.
85.1.10.1.4. Konfiguration¶
Die Pipeline-Verdrahtung und jede Betriebsoption liegen im eigenen [stage-<name>]-Abschnitt der Stage (PROGRAM = pepsi-stage-relay-to-internet): die Identitäten SERVER_NAME/POSTMASTER, die Verbindungs-Timeouts, der Retry-Backoff-Zeitplan und MAX_LIFETIME, DELAY_DSN_AFTER, MAX_HOP_COUNT, die DNS-Einstellungen, MTA_STS/MTA_STS_TIMEOUT, ADDRESS_FAMILY und DANE. Sie sind in pepsi.conf(5) dokumentiert.
85.1.10.1.5. State¶
Eingaben (aus dem state des Datensatzes gelesen; alle optional):
state.dsn— die RFC-3461-Parameter (ret/envidauf Nachrichtenebene und dasnotify/orcptdes ersten Empfängers); an den MX propagiert, wenn erDSNankündigt, und herangezogen, um Failure-/Success-/Delay-Berichte abzuriegeln.state.origin— die 8BITMIME/SMTPUTF8-Hinweise (body_8bit,smtputf8), die von der Inhalts-Herabstufungs-Entscheidung verwendet werden.
Ausgaben (in das state des Datensatzes zusammengeführt):
Bei einem vorübergehenden Fehler (
pause):attempts,last_errorund, sobald eine Delay-DSN ausgegeben wurde,delay_sent.Bei einem dauerhaften Fehler mit einem
BOUNCE_STAGE(reroute) oder bei einer Delay-Warnung (einem eingereihten Klon): einstate.bounce-Objekt (kind=permanentoderdelay) für pepsi-stage-bounce(1). Für einen Fehler auf SMTP-Ebene hält es außerdem die strukturierten Detailangaben zum nächsten Hop fest —remote_mta(die Domain des Empfängers),smtp_code,enhanced_status,phaseundreply_text— sodass der Bounce genau angeben kann, warum die Zustellung verweigert wurde.Bei einer erfolgreichen Zustellung mit
ORIGINATE_SUCCESS_DSNundNOTIFY=SUCCESS: einstate.bounce-Objekt mitkind = success.Bei einem dauerhaften Fehler ohne
BOUNCE_STAGE(fail):last_error.
state.dsn und state.origin bleiben immer erhalten. Das State-Layout wird vollständig in pepsi.state(7) beschrieben.
Übergänge (der Wiederholungszeitplan ist RETRY_INITIAL / RETRY_FACTOR / RETRY_MAX_INTERVAL, begrenzt durch MAX_LIFETIME):
erfolgreiche Zustellung → abschließen (der Datensatz wird gelöscht) oder zu NEXT_STAGE weiterschalten, falls eines gesetzt ist; mit [pepsi] ORIGINATE_SUCCESS_DSN und einem Empfänger, der
NOTIFY=SUCCESSangefordert hat, leitet es stattdessen zu BOUNCE_STAGE um, um eine positive (Action: delivered-)DSN auszugeben;vorübergehender Fehler → pausieren für das nächste Wiederholungsintervall;
noch eingereiht über DELAY_DSN_AFTER hinaus mit einem
NOTIFY=DELAY-Empfänger → einen einmaligen Delay-DSN-Klon bei BOUNCE_STAGE einreihen (das Original bleibt eingereiht und wiederholt weiter);dauerhafter Fehler oder MAX_LIFETIME erschöpft → umleiten zu BOUNCE_STAGE oder fehlschlagen (terminal
failed), wenn kein BOUNCE_STAGE konfiguriert ist;ein unzustellbarer Bounce (Null-Absender) wird nie erneut gebounct: Eine Kopie wird an POSTMASTER zugestellt (falls gesetzt) und der Datensatz abgeschlossen.
85.1.10.1.6. Befehle¶
- worker
Läuft als persistenter pepsi-dispatch(1)-Worker, der Nachrichten-IDs von der Standardeingabe liest.
- mta-sts DOMAIN
Diagnose: die MTA-STS-Richtlinie nachschlagen und ausgeben, die bei der Zustellung an DOMAIN gelten würde. Verwendet den System-Resolver und benötigt keine Nachrichten- oder Stage-Konfiguration.
85.1.10.1.7. Globale Optionen¶
- -c FILE, –config FILE
Liest die Konfiguration aus FILE, statt die Standardorte zu durchsuchen.
- -L LOGLEVEL, –log LOGLEVEL
Setzt die Log-Ausführlichkeit (Standardwert
info).- -v, –verbose
Zeigt Log-Meldungen aus allen Quellen.
- -h, –help; -V, –version
Gibt eine Verwendungsübersicht / die Version aus und beendet sich.
85.1.10.1.8. Exit-Status¶
Das Ergebnis jeder Nachricht wird pepsi-dispatch(1) auf der Statuszeile des Workers gemeldet, nicht als Exit-Status.
- 0
Der Worker lief, bis seine Standardeingabe geschlossen wurde, oder die mta-sts-Abfrage wurde abgeschlossen.
- 1
Ein fataler Fehler ist aufgetreten (unlesbare Konfiguration, die Datenbank konnte nicht geöffnet werden, oder Standardeingabe/-ausgabe ist fehlgeschlagen). Der Grund wird in das Journal geschrieben.
85.1.10.1.9. Beispiele¶
Die Stage auf Nachricht 42 ausführen (sie muss running sein):
echo 42 | pepsi-stage-relay-to-internet -c /etc/pepsi/pepsi.conf worker
Prüfen, welche MTA-STS-Richtlinie für eine Empfängerdomain gilt:
pepsi-stage-relay-to-internet -c /etc/pepsi/pepsi.conf mta-sts example.com
85.1.10.1.10. Siehe auch¶
pepsi-config(1), pepsi-stage-bounce(1), pepsi-stage-dkim-sign(1), pepsi-stage-relay-to-smarthost(1), pepsi-dispatch(1), pepsi.conf(5), pepsi.state(7), pepsi-setup(1)
85.1.10.1.11. Fehler¶
Melden Sie Fehler an den Pepsi-Issue-Tracker.