85.1.1. pepsi-ingress

receive incoming e-mail over SMTP and store it for Pepsi

Handbuchabschnitt:

1

85.1.1.1.1. Name

pepsi-ingress - SMTP-Server, der eingehende E-Mail in PostgreSQL speichert.

85.1.1.1.2. Übersicht

pepsi-ingress [GLOBAL-OPTIONS] serve

85.1.1.1.3. Beschreibung

pepsi-ingress ist die mailempfangende Komponente von Pepsi. Es betreibt einen SMTP-Server (einen Message Transfer Agent), der eingehende E-Mail über Klartext, implizites TLS und STARTTLS annimmt. Ein Nachrichtentext kann mit DATA gesendet werden oder, für Clients, die es ankündigen, mit CHUNKING/BDAT (RFC 3030) — in der EHLO-Antwort angekündigt. Es speichert jede angenommene Nachricht — zusammen mit ihrem Umschlag, einer kleinen Menge geparster Metadaten und den SMTP-Ursprungs-Metadaten der zustellenden Verbindung (Client-IP, HELO/EHLO-Name, TLS, Listener, Reverse-DNS/iprev; siehe pepsi.conf(5)) — in der workqueue-Tabelle des pepsi-Schemas einer PostgreSQL-Datenbank. Die Nachricht wird aufgeteilt in eine headers-Spalte (alles bis zur Leerzeile) und einen Nachrichtentext gespeichert, sodass nachgelagerte Stages, die nur die Header berühren, den Nachrichtentext nicht laden müssen. Der Nachrichtentext ist ein Datensatz in einer eigenen Tabelle, workqueue_body, auf die der Nachrichten-Datensatz verweist: Jeder Empfänger-Datensatz, den die Pipeline später von der Nachricht abspaltet, teilt sich diese eine Kopie (siehe pepsi.conf(5), Gespeicherte Daten). Die Nachricht tritt bei der Anfangs-Stage (stage = init) in die Stage-Pipeline ein. Nachdem der Datensatz committet wurde, weckt ein NOTIFY auf dem workqueue-Kanal pepsi-dispatch(1). Diese Benachrichtigung wird außerhalb des Bandes ausgelöst und zusammengefasst — höchstens eine je DISPATCH_WAKE_INTERVAL (Standardwert 5 ms), sodass ein Schwall von Annahmen den Dispatcher einmal weckt —, denn eine Transaktion, die eine Benachrichtigung trägt, kann nicht am Gruppen-Commit teilnehmen, was jede gleichzeitige SMTP-Sitzung hinter ihrem eigenen fsync serialisieren würde. Geht eine verloren, steht nichts auf dem Spiel: Der POLL_INTERVAL-Durchgang des Dispatchers ist das Sicherheitsnetz, die Kosten sind also Latenz.

Der Server nimmt Mail für eine konfigurierte Menge von Domains an. Innerhalb dieser Domains nimmt er jeden Local-Part an, ohne zu verifizieren, dass das Postfach existiert; Empfänger außerhalb der konfigurierten Domains werden als Relaying abgelehnt (550). Das domainlose reservierte Postfach <Postmaster> wird unabhängig von den konfigurierten Domains immer angenommen (RFC 5321 §4.5.1) und in eine routbare Adresse umgeschrieben — [pepsi-ingress] POSTMASTER, wenn gesetzt, sonst postmaster@HOSTNAME — sodass der Rest der Pipeline (Aliase, lokale Zustellung, Relay) sie wie jeden gewöhnlichen Empfänger routet. Damit Postmaster-Mail eine Person erreicht, richten Sie entweder POSTMASTER auf ein echtes Postfach oder fügen einen Alias für die umgeschriebene Adresse in einer pepsi-stage-aliases-ALIASES-Map hinzu. Eine solche Nachricht wird außerdem mit state.spam = false committet, sodass die Sprach- und Pay-to-Send-Schranken (pepsi-stage-block-language(1), pepsi-stage-anti-spam(1)) sie weiterleiten und das Postmaster-Postfach erreichbar halten, wie RFC 5321 §4.5.1 es verlangt — aber nur, wenn ``<Postmaster>`` der einzige Empfänger der Transaktion war. Eine Nachricht, die an den Postmaster und ein gewöhnliches Postfach adressiert ist, wird ohne die Umgehung committet und passiert die Schranken wie jede andere, denn sonst könnte ein Spammer die Mail eines echten Opfers freistellen, indem er einfach ein <Postmaster>-RCPT danebensetzt. Wenn SRS konfiguriert ist (der gemeinsame [pepsi-srs]-Abschnitt; siehe pepsi-stage-srs(1)), wird ein Empfänger in der SRS-Domain, dessen Local-Part ein gültiges Sender-Rewriting-Scheme-Token ist, zum ursprünglichen Absender rückwärtsdekodiert und dorthin zum Relay angenommen — obwohl die Domain dieses Absenders keine ist, die wir bedienen, weil die gültige Signatur es autorisiert; ein gefälschtes oder abgelaufenes Token wird abgelehnt (550), ohne zu sagen, welches von beiden es war. Weil die Annahme eines solchen Empfängers ein Relay autorisiert, ist dieses Paar aus Annahme und Ablehnung ein Online-Orakel über die SRS-Signatur; eine Verbindung, die drei ungültige SRS-Empfänger vorlegt, wird daher auf warn protokolliert und mit 421 geschlossen, sodass die Suche nach einem gültigen Token eine Verbindung je drei Rateversuche kostet und für die Missbrauchsüberwachung sichtbar ist. So finden Bounces auf von Pepsi weitergeleitete Mail ihren Weg zurück zu den ursprünglichen Absendern. Der Empfang einer Nachricht wird dem Client (250) erst bestätigt, nachdem die Nachricht dauerhaft in die Datenbank committet wurde; ist die Datenbank nicht verfügbar, erhält der Client einen vorübergehenden Fehler (451), und von ihm wird eine Wiederholung erwartet.

Wie von RFC 5321 §4.4 verlangt, stellt pepsi-ingress jeder angenommenen Nachricht einen Received:-Trace-Header voran, der den sich verbindenden Client (HELO/EHLO-Name, Reverse-DNS und IP), diesen Server, den verwendeten Transport (SMTP, ESMTP, ESMTPS, ESMTPA oder ESMTPSA gemäß RFC 3848 — siehe Authentifizierung unten) und den Zeitpunkt des Empfangs benennt; eine for-Klausel benennt den Empfänger nur bei einer Einzelempfänger-Transaktion. Der Header wird vor der Authentifizierung hinzugefügt, sodass das ARC-Siegel (später von pepsi-stage-arc(1) hinzugefügt) ihn abdeckt.

Bei einer verschlüsselten Sitzung hält das ESMTPS-Schlüsselwort für sich genommen nicht fest, welche TLS-Version und -Chiffre ausgehandelt wurden. Wie von RFC 8314 §4.3 empfohlen, hängt pepsi-ingress daher die ausgehandelten Parameter als Kommentar nach dem with-Schlüsselwort an, z. B.:

by mail.example.org (pepsi-ingress) with ESMTPS
    (version=TLSv1_3 cipher=TLS13_AES_128_GCM_SHA256)

Diese Werte sind dieselben TLS-Parameter, die im Nachrichten-state unter origin.tls festgehalten werden (siehe pepsi.state(7)); was auch immer der TLS-Stack an Version/Chiffre meldet, wird aufgenommen.

Ein X-Pepsi-List-Hops:-Header (der Schleifenzähler für Mailinglisten) wird aus jeder Nachricht entfernt, die über eine nicht authentifizierte Sitzung eintrifft, da ein Absender den Zähler sonst zurücksetzen könnte; die Kopie einer authentifizierten Sitzung bleibt erhalten.

Mehrere SMTP-Ströme werden gleichzeitig verarbeitet. Mehrere Listener können konfiguriert werden, jeder an seinen eigenen TCP-Port, UNIX-Domain-Socket oder einen über Socket-Aktivierung von einem systemd-Elternprozess geerbten Socket gebunden, und jeder mit seinem eigenen Transportmodus. Das gesamte Verhalten wird durch eine Konfigurationsdatei im INI-Stil gesteuert (siehe pepsi.conf(5)).

Das Datenbankschema wird von pepsi-setup(1) installiert, das die Migrationen jeder Komponente auf das gemeinsame pepsi-Schema anwendet; pepsi-ingress selbst hat keinen Befehl zur Schema-Initialisierung.

85.1.1.1.3.1. SMTP-Erweiterungen

Die EHLO-Antwort kündigt genau diese Schlüsselwörter an, in dieser Reihenfolge:

SIZE <MAX_MESSAGE_SIZE>
8BITMIME
SMTPUTF8
PIPELINING
CHUNKING
DSN
ENHANCEDSTATUSCODES

SIZE (RFC 1870) kündigt [pepsi-ingress] MAX_MESSAGE_SIZE an, was eine serverweite und keine listenerweise Einstellung ist. Zwei weitere Schlüsselwörter sind bedingt: STARTTLS (RFC 3207) wird auf einem Listener angekündigt, der TLS-Material hat, solange die Sitzung noch Klartext ist — also MODE = starttls, denn MODE = tls ist vom ersten Byte an verschlüsselt —, und AUTH PLAIN LOGIN (RFC 4954) nur auf einer bereits verschlüsselten Sitzung, deren Listener ein SASL_TYPE-Backend konfiguriert.

Mehr wird nicht angeboten. Insbesondere BINARYMIME wird weder angekündigt noch angenommen (siehe 8-Bit- und internationalisierte Inhalte); VRFY wird mit dem unverbindlichen 252 beantwortet und EXPN mit 502 abgelehnt, da RFC 5321 §7.3 die Offenlegung von Listenmitgliedschaften zu einem Informationsleck und nicht zu einem Dienst macht.

85.1.1.1.3.2. Authentifizierung

Bevor eine Nachricht gespeichert wird, authentifiziert pepsi-ingress sie, damit nachgelagerte Empfänger — nachdem Pepsi die Mail weitergeleitet und das ursprüngliche SPF- und DKIM-Alignment zerstört hat — weiterhin das Urteil sehen können, das Pepsi an der Grenze gefällt hat. Für jede angenommene Nachricht:

  1. verifiziert SPF (auf der MAIL FROM-Identität, unter Verwendung der IP des sich verbindenden Clients), DKIM und DMARC; und

  2. stellt einen Authentication-Results-Header voran, der diese Ergebnisse berichtet, mit dem [pepsi-ingress] HOSTNAME als authserv-id, und hält den gerenderten Ergebnistext unter state.origin.ar_results fest.

ARC wird separat behandelt: Ingress verifiziert weder die ARC-Kette noch versiegelt es die Nachricht. Die Pipeline-Stage pepsi-stage-arc(1) verifiziert jede eingehende ARC-Kette und fügt das frische ARC-Set (AAR/AMS/AS) als die [pepsi] ARC_DOMAIN-Identität hinzu, indem sie den Sitzungskontext (authserv-id, Client-IP, HELO/EHLO und das ar_results-Fragment) liest, den Ingress im state des Datensatzes festgehalten hat. Mit diesem Fragment kann die Stage die AAR bauen, ohne SPF/DKIM/DMARC erneut auszuführen, sodass die Authentifizierungsprüfungen genau einmal geschehen, und es hält die ARC-Signierschlüssel aus dem SMTP-Front-End heraus.

Die Verifikation ist fail-open: Ein DNS-Fehler oder eine nicht parsbare Nachricht lehnt die Mail nie ab; sie wird als temperror/none festgehalten und angenommen. Nur wenn DMARC_ENFORCE gesetzt ist, verursacht ein eindeutiger DMARC-Fehlschlag unter einer quarantine/reject-Richtlinie eine 550-Ablehnung; standardmäßig wird solche Mail angenommen (und die ARC-Stage versiegelt das fehlschlagende Ergebnis), damit der endgültige Empfänger urteilt. Die SPF/DKIM/DMARC-Urteile werden im state.auth-Objekt des Datensatzes gespeichert (als spf/dkim/dmarc/arc) und die authserv-id unter state.origin; das arc-Urteil beginnt als none und wird später von der ARC-Stage ausgefüllt. Die DNS-Abfragen geschehen synchron in der DATA-Phase, sodass ein langsamer Resolver das 250 verzögert.

Der Aufwand ist begrenzt, denn die Anzahl der Signaturen wählt der Absender, und jede kostet eine DNS-Abfrage und eine Public-Key-Verifizierung. Höchstens MAX_DKIM_SIGNATURES (Standardwert 10) DKIM-Signature-Felder werden verifiziert, und zwar zuerst diejenigen, deren d= die From:-Domain oder eine über- oder untergeordnete Domain davon ist — die einzigen, die DMARC verwenden kann —, sodass das Auffüllen einer Nachricht mit Signaturen die entscheidende nicht verdrängen kann. SPF und die ausgewählten Signaturen werden gleichzeitig unter einer gemeinsamen Frist von VERIFY_TIMEOUT (Standardwert 20 s) geprüft und DMARC unter einer zweiten, sodass eine Nachricht ihre Sitzung nie länger als das Doppelte davon in der Verifizierung festhält. Eine Prüfung, die ihre Frist verpasst, wird allein für diese Prüfung als temperror festgehalten: Eine abgelaufene Signatur, deren Domain nicht zur From:-Domain passt, ist eine, die DMARC ignoriert, sodass ein Fälscher sein eigenes DNS nicht verlangsamen kann, um aus einem DMARC-Fehlschlag etwas zu machen, worauf DMARC_ENFORCE nicht reagiert.

85.1.1.1.3.3. Zustellungsstatusbenachrichtigungen (DSN)

pepsi-ingress kündigt die DSN-Erweiterung (RFC 3461) in seiner EHLO-Antwort an und nimmt ihre Parameter an: RET und ENVID bei MAIL FROM und NOTIFY und ORCPT bei RCPT TO. Ihre Syntax wird validiert — ein ungültiger RET/NOTIFY-Wert, ein NOTIFY=NEVER in Kombination mit anderen Schlüsselwörtern oder ein fehlerhaftes ENVID/ORCPT-xtext wird mit 501 abgelehnt — und die angenommenen Werte werden im state.dsn des Datensatzes gespeichert (ret/envid auf Nachrichtenebene plus ein Array von notify/orcpt pro Empfänger). Die Stage-Pipeline beachtet sie: Die Relay-Stages propagieren die Parameter an den nächsten Hop, wenn er ebenfalls DSN ankündigt, und pepsi-stage-bounce(1) erzeugt einen Fehler-Bounce nur, wenn das NOTIFY des Empfängers es anfordert (FAILURE, der Standardwert bei Abwesenheit), und spiegelt ENVID/ORCPT in die DSN. Jede Stage muss state.dsn bewahren.

85.1.1.1.3.4. 8-Bit- und internationalisierte Inhalte

pepsi-ingress kündigt 8BITMIME (RFC 6152) und SMTPUTF8 (RFC 6531) in seiner EHLO-Antwort an, sodass eine Nachricht rohe 8-Bit-Oktette in ihrem Nachrichtentext und UTF-8 in ihren Headern und ihrem Umschlag tragen darf. Der BODY=-Parameter bei MAIL FROM wird angenommen und festgehalten (state.origin.body_8bit); nur 7BIT und 8BITMIME sind gültig — BINARYMIME wird nicht angekündigt und mit 501 abgelehnt. Die Relay-Stages kündigen BODY=8BITMIME / SMTPUTF8 einem nächsten Hop, der sie unterstützt, erneut an und stufen herab, wenn er es nicht tut: Ein 8-Bit-Nachrichtentext zu einem Nicht-8BITMIME-Hop wird per MIME auf ein 7-Bit-Content-Transfer-Encoding umkodiert, und UTF-8-Header zu einem Nicht-SMTPUTF8-Hop werden als RFC-2047-Encoded-Words umgeschrieben, bzw. nach RFC 2231 für einen MIME-Parameter wie den Dateinamen eines Anhangs (eine Nicht-ASCII-Adresse dort kann nicht herabgestuft werden und erzeugt einen Bounce). Eine Nachricht, deren Nachrichtentext sich überhaupt nicht konvertieren lässt — fehlerhaft über das hinaus, was der Umkodierer reparieren kann —, erzeugt einen Bounce, statt 8-Bit gesendet zu werden, wie RFC 6152 §3 es verlangt. Siehe pepsi-stage-relay-to-internet(1).

85.1.1.1.3.5. ESMTP-Parametervalidierung

pepsi-ingress validiert die MAIL FROM-/RCPT TO-Parameter streng. Die erkannten Parameter sind SIZE, BODY und SMTPUTF8 (sowie AUTH) bei MAIL FROM und die obigen DSN-Parameter; ein SIZE=-Wert, der keine nicht-negative Ganzzahl ist, wird mit 501 (RFC 1870 §6.1) abgelehnt, statt stillschweigend verworfen zu werden. Ein Parameter, den der Server nicht implementiert, wird mit 555 5.5.4 (RFC 5321 §4.1.1.11) abgelehnt — er wird nicht stillschweigend ignoriert. Die einzige Ausnahme ist der AUTH=-Parameter nach RFC 4954 bei MAIL FROM (den ein authentifizierter Client anhängen darf, weil AUTH angekündigt wird): er wird akzeptiert und ignoriert — Pepsi gibt die behauptete Identität nicht weiter — statt abgelehnt zu werden.

85.1.1.1.3.6. Client-Authentifizierung

Jeder Listener kann seine Clients authentifizieren und angenommene Mail als lokal erzeugt markieren. Eine Sitzung ist authentifiziert, wenn einer von vier listenerspezifischen Mechanismen sie akzeptiert:

  • MYNETWORKS — die sich verbindende IP liegt innerhalb einer durch Leerraum/Komma getrennten Liste vertrauenswürdiger IPv4/IPv6-CIDR-Netze (eine bloße Adresse wird als /32- oder /128-Host genommen).

  • SASL — ein SASL_TYPE-Backend (derzeit dovecot, über den SASL_PATH-Auth-Client-Socket) akzeptiert einen AUTH-Austausch. AUTH (Mechanismen PLAIN und LOGIN) wird nur auf einer TLS-geschützten Sitzung angekündigt und akzeptiert (implizites TLS oder nach STARTTLS); ein Klartext-AUTH-Versuch wird mit 504 5.5.4 abgelehnt (RFC 4954 §4, der die 538-Antwort dafür für veraltet erklärt).

  • TLS-Client-Zertifikat — der Client legt ein Zertifikat vor, dessen SubjectPublicKeyInfo-SHA-256 (64 Hex-Zeichen) in TLS_AUTH_CLIENT aufgeführt ist oder dessen Kette per Signatur bis zu einem gelisteten CA-Schlüssel validiert.

  • AUTH_PEERCRED — auf einem UNIX-Domain-Socket wird die vom Kernel bezeugte uid des Gegenübers (SO_PEERCRED) zu einem Login aufgelöst und der USERNAME_MAP-Maschinerie zugeführt, sodass ein Aufrufer nur als er selbst senden kann. Standardwert ``yes`` für einen ``SERVE = unix``-Listener (setzen Sie AUTH_PEERCRED = no, um es abzuschalten); auf einem TCP-Listener rundweg abgelehnt, wo ein Gegenüber keine vom Kernel bezeugte Identität hat. Das ist es, was den lokalen Submission-Socket authentifiziert, den pepsi-sendmail(1) verwendet, und es setzt das RFC-3848-Schlüsselwort ESMTPA. Für einen SERVE = systemd-Listener wird es akzeptiert, aber nie hergeleitet, da die Familie in der Unit liegt; eine Warnung wird protokolliert, wenn sich der geerbte Deskriptor als TCP herausstellt.

SASL_TYPE und TLS_AUTH_CLIENT erfordern einen TLS-Listener (MODE = tls oder starttls). Eine authentifizierte Sitzung wird mit state.local_origin = true festgehalten (sonst false) und darf an jede Domain weiterleiten, nicht nur an die bedienten Domains. Eine Sitzung, die eine Identität trägt — SASL AUTH oder AUTH_PEERCRED auf einem UNIX-Socket, aber nicht MYNETWORKS oder ein Client-Zertifikat —, setzt außerdem das A nach RFC 3848 im with-Schlüsselwort des Trace-Headers: ESMTPSA über TLS und schlichtes ESMTPA für den Klartext-UNIX-Submission-Socket, von dem lokal eingelieferte Mail (pepsi-sendmail(1), cron) kommt. Filtern Sie auf beide, nicht nur auf ESMTPSA. Ein nicht erkanntes Client-Zertifikat bricht den Handshake nie ab — es lässt die Sitzung einfach nicht authentifiziert.

85.1.1.1.3.7. Nachrichten-Submission (RFC 6409)

Ein Listener mit SUBMISSION = yes wird als Message Submission Agent (RFC 6409) statt als MX behandelt:

  • Authentifizierung ist verpflichtend. Ein nicht authentifiziertes MAIL FROM wird mit 530 5.7.0 Authentication required abgelehnt; der Client muss zuerst einen der obigen Client-Authentifizierung-Mechanismen erfüllen. Das Flag erfordert daher mindestens einen konfigurierten Mechanismus (SASL_TYPE, TLS_AUTH_CLIENT, MYNETWORKS oder AUTH_PEERCRED); ein Submission-Listener ohne einen solchen ist ein Konfigurationsfehler, der beim Parsen der Datei zurückgewiesen wird — also von pepsi-setup(1) und vom Server selbst beim Start. Es erfordert außerdem einen TLS-geschützten MODE (tls oder starttls) — außer auf einem lokalen Socket, also SERVE = unix oder einem SERVE = systemd-Listener, der einen solchen erbt, wo es kein Netz zu schützen gibt. Der ausgelieferte lokale Submission-Socket ist genau das: MODE = plain mit SUBMISSION = yes und AUTH_PEERCRED. Die Regel aufzuschieben heißt nicht, sie fallen zu lassen — ein Klartext-Submission-Listener, der sich beim Binden als Erbe eines Netz-Sockets herausstellt, lässt pepsi-ingress den Start verweigern.

  • Submission-Korrekturen werden auf jede angenommene Nachricht angewendet: ein fehlender Date:-Header wird ergänzt (§8.2) und ein fehlendes Message-ID: als <random@HOSTNAME> erzeugt (§8.3). Bereits vorhandene Header bleiben unberührt, und die Korrekturen laufen, bevor der Received:-Trace-Header vorangestellt wird, sodass die hinzugefügten Felder vom nachgelagerten ARC-Siegel und der DKIM-Signatur abgedeckt werden.

Das Flag hat auf einem MX-Listener keine Wirkung; lassen Sie es für Port 25 aus (der Standardwert).

85.1.1.1.3.8. Submission-Identität (RFC 6409 §6 / §8.1)

Eine Sitzung, die einen Benutzernamen trägt, ist an die Menge der E-Mail-Adressen gebunden, die dieser Benutzername verwenden darf. Zwei der vier obigen Mechanismen tragen einen: ein SASL-AUTH-Austausch (die authcid) und AUTH_PEERCRED auf einem UNIX-Socket (der Login, zu dem die vom Kernel gemeldete uid aufgelöst wird). Beide unterliegen allem in diesem Abschnitt; MYNETWORKS und ein TLS-Client-Zertifikat tun das nicht, denn sie authentifizieren eine Adresse oder einen Schlüssel und kein Konto.

Standardmäßig wird der Benutzername alice auf die einzelne Adresse alice@HOSTNAME abgebildet (die [pepsi-ingress] HOSTNAME-Domain); die optionale USERNAME_MAP-Datei überschreibt diese Zuordnung. Jede nicht-leere, nicht-Kommentar-Zeile ist:

username: addr1@example.org addr2@example.org

Ein # beginnt einen Kommentar, der Schlüssel ist der SASL-Benutzername (vor dem ersten :), und der Wert ist die durch Leerraum/Komma getrennte Liste erlaubter Adressen, die leer sein darf. Benutzernamen werden unabhängig von der Groß-/Kleinschreibung abgeglichen und Adressen kleingeschrieben. Die Datei wird neu gelesen, wann immer ihre Änderungszeit sich ändert (bei jeder Authentifizierung geprüft), sodass Bearbeitungen ohne Neustart wirksam werden; eine fehlende oder nicht lesbare Datei wird als leer behandelt (jeder Benutzer fällt auf den Standardwert zurück).

Eine erlaubte Adresse darf eine *-Wildcard verwenden (ein *-Glob, der gegen die Kandidatenadresse abgeglichen wird), sodass alice: *@example.org alice jede Adresse bei example.org verwenden lässt und ein bloßes * jede Adresse überhaupt erlaubt. Ein Wildcard-Eintrag benennt keine einzelne kanonische Identität, sodass er für die Sender:-Korrektur unten übersprungen wird (die erste konkrete, wildcard-freie Adresse ist die kanonische Identität, falls vorhanden). pepsi-setup prüft eine konfigurierte USERNAME_MAP zur Installationszeit auf Syntax (siehe pepsi-setup(1)). Die Wildcard ist ein Glob, kein regulärer Ausdruck: Schreiben Sie *@example.org, niemals .*@example.org (ein wörtlicher Punkt gefolgt von einem Glob — er passt auf nichts, sodass dem Benutzer jede Adresse verweigert würde, die er gewähren sollte). Das Setup lehnt ein solches Muster ab und nennt den Glob, den Sie gemeint haben.

Die Zuordnung hat drei Ergebnisse für einen Benutzernamen:

  • mit Adressen aufgeführt — genau diese Adressen (und Wildcard-Muster) sind erlaubt; die erste konkrete (wildcard-freie) ist die kanonische Identität, die für die Sender:-Korrektur verwendet wird;

  • ohne Adressen aufgeführt — der Benutzer wird abgewiesen: Obwohl SASL die Anmeldedaten angenommen hat, wird der AUTH-Befehl mit 535 5.7.8 abgelehnt und die Sitzung bleibt unauthentifiziert. Bei einer Peer-Credential-Sitzung gibt es keinen Befehl abzulehnen, sodass die Sitzung sich schlicht nie authentifiziert — und auf einem Submission-Listener wird ihr erstes MAIL FROM dann mit 530 5.7.0 beantwortet. So wird ein Systemkonto (etwa www-data) vom Einliefern gänzlich ausgeschlossen;

  • nicht aufgeführt (oder kein USERNAME_MAP konfiguriert) — der Standardwert username@HOSTNAME.

Mit dieser Menge laufen bei einer solchen Submission zwei Prüfungen:

  • Umschlag- und Header-Identität (§6). Der MAIL FROM-Umschlagabsender und die From:-Adresse der Nachricht müssen jeweils eine der erlaubten Adressen sein, andernfalls wird die Nachricht mit 550 5.7.1 abgelehnt (bei MAIL FROM bzw. am Ende von DATA). Der Null-Absender <> und eine Nachricht ohne parsbare From:-Adresse sind ausgenommen. RFC 5322 §3.6.2 macht From: zu einer Liste von Postfächern, sodass ein Wert legitim mehrere Postfächer benennen darf — und jedes davon muss eine erlaubte Adresse sein, nicht nur eines. Ein einzelnes Postfach zu prüfen wäre gar keine Prüfung, denn Mailprogramme zeigen das erste an, während ein erlaubtes irgendwo in der Liste stehen könnte.

  • Sender-Korrektur (§8.1). Das Sender:-Feld gehört der MSA, nicht dem Client: Eine Adresse, die ein Client dort hineinschreibt, unterliegt auf keinem Pfad einer Identitätsprüfung, und Pepsi würde sie anschließend per DKIM als Aussage darüber signieren, wer die Nachricht gesendet hat. Wann immer das Konto eine kanonische Identität hat, wird ein vorhandenes Sender: daher ersetzt oder entfernt, was §8.1 ausdrücklich erlaubt („The MSA MAY add or replace the ‚Sender‘ field“).

    Wenn die From:-Adresse eine erlaubte Adresse ist, die von der kanonischen Identität abweicht, oder mehr als ein Postfach benennt, wird ein Sender:-Header vorangestellt, der die kanonische Identität benennt — für den Fall mehrerer Postfächer verlangt RFC 5322 §3.6.2, dass ein Sender: vorhanden ist, und §8.1 ist es, was der MSA erlaubt, es zu liefern. Wenn From: ein einzelnes Postfach ist, das die kanonische Identität benennt, ist ein Sender: damit redundant (§3.6.2: „SHOULD NOT be used if it is redundant with the ‚From:‘ field“) und wird stattdessen entfernt.

    Ein Benutzer, dem nur Wildcard-Muster gewährt wurden, hat keine einzelne kanonische Identität zu behaupten, sodass keines von beidem zutrifft und ein vorhandenes Sender: unangetastet bleibt — es gibt nichts, worin es widerlegt werden könnte. Wie die anderen Korrekturen geschieht dies vor dem Trace-Header, sodass das Ergebnis vom ARC-Siegel und der DKIM-Signatur abgedeckt wird.

Diese Prüfungen gelten für jede Sitzung, die einen Benutzernamen trägt, gleich ob er aus SASL AUTH oder aus AUTH_PEERCRED stammt. Eine MYNETWORKS- oder TLS-Client-Zertifikat-Sitzung hat keinen abzubildenden Benutzernamen und unterliegt ihnen nicht.

Neben der Prüfung nach Abschnitt 6 hält Ingress fest, wie das From: zugelassen wurde, als state.origin.from_bound: true genau dann, wenn From: ein einzelnes Postfach nennt, das auf einen konkreten (platzhalterfreien) Eintrag der erlaubten Adressen des Kontos passte, oder auf den Standardwert username@HOSTNAME; andernfalls false, und für eine Sitzung ohne Benutzernamen immer false. Die Erlaubnis, als eine Adresse zu senden, genügt zum Senden; an sie gebunden zu sein ist das, was einer Einlieferung erlaubt, für sie zu sprechen. pepsi-stage-encrypt(1) registriert den Schlüssel, den der Mail-Client eines Benutzers bewirbt, oder führt einen E-Mail-Schlüsselbefehl aus, nur für ein gebundenes From: (oder ein Relaying über MYNETWORKS/Client-Zertifikat): eine Berechtigung *@example.org erlaubt einem Konto, als seine Kollegen zu senden, darf ihm aber nicht erlauben, einen Schlüssel für sie unterzuschieben. pepsi-setup(1) warnt vor Konten, die Platzhalter-Berechtigungen besitzen.

85.1.1.1.3.9. Lokaler Submission-Socket

Ein Listener wird nie konfiguriert: der lokale Submission-Socket, mit dem sich pepsi-sendmail(1) — und damit /usr/sbin/sendmail, cron und alles andere, was lokal Mail einliefert — verbindet. pepsi-ingress bedient ihn bedingungslos, denn diesen Weg bereitzustellen ist eine Eigenschaft dessen, ein Mailserver zu sein, und keine einzuschaltende Funktion. Er ist kein optionaler Listener-Abschnitt, weil das die Einrichtung davon abhängig machen würde, dass drei getrennte Dinge übereinstimmen (dass der Abschnitt existiert, dass sein UNIXPATH zum Socket des Clients passt und dass das Laufzeitverzeichnis beschreibbar ist), von denen sich jedes leicht falsch machen lässt, mit demselben stillen Symptom: Lokale Mail verschwindet.

Der Pfad ist [pepsi-sendmail] SOCKET (Standardwert /run/pepsi/submission.sock), von beiden Enden aus dieser einen Option gelesen, sodass Client und Server nicht verschiedene Sockets benennen können. Alles Übrige liegt fest, denn nichts davon ist eine Wahl: Der Listener heißt im Log local-submission und ist MODE = plain mit SUBMISSION = yes und AUTH_PEERCRED. Wird er hier gebunden statt von pepsi-ingress.socket geerbt, so wird er mit dem Modus 0666 angelegt; der freizügige Modus gewährt nichts, denn das Öffnen des Sockets vermittelt keine Befugnis — der Kernel liefert die uid des Aufrufers, und USERNAME_MAP entscheidet, als wer er senden darf.

Ein ausdrücklicher [pepsi-ingress-listener-*]-Abschnitt, der denselben Pfad bedient, gewinnt, und daneben wird nichts synthetisiert; ein Operator, der dort andere Optionen möchte (einen strengeren Modus, ein UNIXPATH_GROUP, keine Peer-Credentials), schreibt also einen. Ihn nicht binden zu können ist fatal, wie bei jedem anderen Listener: Ein Server, der nur mit einem Teil dessen läuft, was er bedienen sollte, ist genau der Fehler, den diese Anordnung beseitigen soll. Der eine Weg, ihn abzuschalten, ist SOCKET = none, was auch pepsi-sendmail(1) den Dienst verweigern lässt; ist dieser Sentinel gesetzt und gibt es überhaupt keine Listener-Abschnitte, so würde der Server auf nichts lauschen und verweigert den Start.

85.1.1.1.3.10. Postfach-Quota bei RCPT

Wenn ein Empfänger auf einer der bedienten Domains liegt, kann pepsi-ingress ihn in der SMTP-Sitzung ablehnen, weil das Postfach bekanntermaßen voll ist — 452 4.2.2 oder 552 5.2.2 je nach [pepsi] MAILBOX_OVER_QUOTA. Hier abzulehnen, statt anzunehmen und später einen Bounce zu erzeugen, hält den sendenden MTA an der Leitung, sodass er es seinem Benutzer sagt, und Pepsi erzeugt keinen Backscatter an einen Umschlagabsender, der durchaus gefälscht sein kann.

Vier Eigenschaften machen es sicher, das aus einem Datenbankdatensatz heraus zu tun.

Keine Auflösung. Der lokale Teil des Empfängers — kleingeschrieben und um eine etwaige Unteradresse an [pepsi] RECIPIENT_DELIMITER gekürzt — wird exakt mit einem passwd-Login verglichen. Aliase, ~/.forward-Dateien und die passwd-Datenbank selbst gehören zur Zustell-Stage; pepsi-ingress rät bei keinem davon, sodass eine Adresse, die nicht wie ein Login geschrieben ist, schlicht nichts trifft und angenommen wird.

Keine Messung. pepsi-ingress kann ein 0700-Maildir nicht lesen und darf nie die Gruppe pepsi-maildir erlangen, die es den Helper ausführen ließe, der es kann. Es liest Zahlen, die jemand anders geschrieben hat, und hält auf der Tabelle nur SELECT.

Nur eine frische Messung zählt. Die laufende Belegungsschätzung wird bewusst nicht herangezogen: Sie zählt zu hoch, und darauf abzulehnen würde ein Postfach schließen, dessen Eigentümer es inzwischen geleert hat — und zwar dauerhaft, denn wenn jede Nachricht abgelehnt wird, läuft nie eine Zustellung, die es neu vermessen würde. Eine Messung, die älter ist als [pepsi] MAILBOX_QUOTA_MAX_AGE, nimmt die Nachricht an und überlässt dem Zustellpfad einen frischen Blick; reconcile von pepsi-quota(1) hält Konten an ihrer Grenze vermessen.

Jede Unsicherheit nimmt an. Kein Datensatz, keine Messung, ein Datenbankfehler — alles bedeutet Annahme, im Einklang mit der übrigen Eingangsverarbeitung.

Das domainlose Postfach <Postmaster> wird ungeachtet dessen angenommen, weil RFC 5321 §4.5.1 verlangt, dass es erreichbar bleibt.

Setzen Sie [pepsi] MAILBOX_QUOTA_RCPT_CHECK = no, um die Abfrage ganz zu überspringen und die gesamte Durchsetzung der Zustellzeit zu überlassen.

85.1.1.1.3.11. Unbekannte Empfänger bei RCPT

Ein Empfänger an einer bedienten Domain, den nichts auf diesem Host annehmen würde, wird in der SMTP-Sitzung mit 550 5.1.1 abgelehnt, aus demselben Grund wie ein volles Postfach: Der sendende MTA benachrichtigt seinen eigenen Benutzer, statt dass Pepsi die Nachricht später an einen Umschlagabsender bounct, den Spam fälscht (Backscatter, der schnellste Weg auf eine Blockliste). [pepsi-ingress] VERIFY_RECIPIENTS (Standard auto) steuert das.

pepsi-ingress lässt dafür nicht die Pipeline laufen. Es fragt, ob irgendetwas, das eine Adresse real machen kann, für sie bürgt:

  • ein lokales Konto, genau so aufgelöst, wie es die Maildir- und die ~/.forward-Stage auflösen;

  • ein Alias, abgeglichen mit dem eigenen Code der Aliases-Stage;

  • eine Mailingliste;

  • postmaster@, abuse@ und die Steueradresse jeder Stage, die eine beantwortet;

  • der MDA hinter einer LMTP-Stage oder das Backend hinter einer Gateway-Route, gefragt mit MAIL FROM:<> und RCPT TO und ohne Nachricht.

Welche davon für welche bediente Domain gelten, wird beim Start aus den [stage-*]-Abschnitten abgeleitet; pepsi-setup run gibt das Ergebnis aus. Eine Domain lehnt Unbekannte nur dann ab, wenn es für jeden Weg, den ihre Mail nehmen kann, etwas gibt, das antworten kann. Ein Catch-All-Alias, eine Stage, die ein Programm ausführt, das Pepsi nicht kennt, oder ein Backend, das niemand fragen darf, lässt die Domain wie bisher offen.

Jede Unsicherheit nimmt an: eine Alias-Map oder Empfängerliste, die sich nicht lesen lässt, ein Datenbankfehler, eine Probe, die fehlschlägt, in ein Timeout läuft oder aus irgendeinem anderen Grund als „kein solches Postfach“ abgewiesen wird, oder mehr als 16 laufende Proben. Eine Adresse wird nur abgelehnt, wenn jede zutreffende Quelle nein gesagt hat. Die pipelineeigene Behandlung unbekannter Postfächer bleibt dahinter bestehen, für die Fälle, die hier durchgelassen werden.

Ein „ja“ einer Probe wird eine Stunde lang zwischengespeichert, ein „nein“ fünf Minuten lang, sodass ein neues Postfach innerhalb von Minuten erreichbar ist. Nach MAX_INVALID_RECIPIENTS (Standard 10) unbekannten Empfängern wird eine Sitzung mit 421 geschlossen. Ein Wörterbuchangriff kostet dann Verbindungen, die die Verbindungslimits bemessen, statt eine Abfrage pro Rateversuch.

VRFY antwortet weiterhin für jede Adresse mit 252: RCPT sagt einem Absender bereits, ob eine Adresse existiert, wie bei jedem MTA, und ein zweiter, billigerer Weg, das zu erfragen, diente nur dem Einsammeln von Adressen.

85.1.1.1.3.12. Zulassungskontrolle der Warteschlange

pepsi-ingress nimmt keine Mail mehr an, solange die Warteschlange oder der Datenträger darunter an einer in [pepsi] gesetzten Grenze steht (siehe pepsi.conf(5)):

  • MAX_QUEUE_ROWS — eingereihte Nachrichten (Standardwert eine Million Empfänger-Datensätze);

  • MAX_QUEUE_BYTES — eingereihte Header-Blöcke plus verschiedene Nachrichtentexte (Standardwert unbegrenzt);

  • MIN_FREE_SPACE — freier Platz auf dem Dateisystem von FREE_SPACE_PATH (Standardwert 1 GiB auf /var/lib/postgresql).

Die Antwort ist 452 4.3.1 Insufficient system storage, eine vorübergehende Ablehnung, gegeben bei RCPT, bei DATA (oder dem ersten BDAT-Chunk, dessen Oktette trotzdem gelesen werden, damit die Sitzung im Takt bleibt) und noch einmal nach dem Ende der Daten, wenn die Größe der Nachricht selbst bekannt ist und eine einzelne Nachricht, die MAX_QUEUE_BYTES oder MIN_FREE_SPACE überschreiten würde, für sich allein abgelehnt wird. Der sendende MTA behält die Nachricht und versucht es erneut, während die bereits vorhandene Warteschlange abfließt; die Drosselung hebt sich von selbst auf. Lokale Submission wird genauso behandelt, und pepsi-sendmail(1) macht aus dem 452 ein EX_TEMPFAIL, sodass cron es erneut versucht.

Die Prüfung ist günstig: Die Warteschlange wird höchstens einmal je QUEUE_CHECK_INTERVAL (Standardwert 10 s) für den ganzen Server gemessen (die Funktion pepsi.queue_usage(), zwei sequenzielle Scans), und jeder Befehl dazwischen wird anhand dieses Werts beantwortet. pepsi-ingress braucht dafür keinen Zugriff auf eingereihte Mail — nur auf die generierten Größenspalten header_octets und workqueue_body.octets. Jede Unsicherheit lässt zu: Eine fehlgeschlagene Messung oder ein Prüfpfad, der nicht existiert (eine Datenbank auf einem anderen Host), lässt die Mail weiterfließen.

85.1.1.1.3.13. Überlastschutz

Mehrere Mechanismen halten eine Flut von Verbindungen — einschließlich Clients, die den TCP-Handshake abschließen und dann verstummen, ohne je zu schließen — davon ab, den Server zu erschöpfen. Sie arbeiten auf einem Quellbereich: einer einzelnen IPv4-Adresse (einem /32) oder dem /32-Präfix einer IPv6-Adresse. Lokale (UNIX-Socket-)Verbindungen bilden einen eigenen Bereich, der von der Ratenbegrenzung und der Verdrängung ausgenommen ist — ein lokaler Einreicher ist ein Benutzer dieser Maschine, und pepsi-sendmail darf nicht abgewiesen werden, weil ein entfernter Host beschäftigt ist — und stattdessen durch eine eigene Obergrenze beschränkt wird.

  • Timeouts pro Befehl (RFC 5321 §4.5.3.2). Eine Sitzung, die ihren nächsten Befehl nicht innerhalb von COMMAND_TIMEOUT (Standardwert 300 s) oder den nächsten Block eines DATA/BDAT-Nachrichtentexts nicht innerhalb von DATA_TIMEOUT (Standardwert 180 s) sendet, wird mit einem 421 geschlossen. Dasselbe COMMAND_TIMEOUT begrenzt einen ins Stocken geratenen STARTTLS- (oder Implicit-TLS-)Handshake.

  • Verbindungsobergrenze mit Verdrängung. Der Server lässt höchstens min(MAX_CONNECTIONS, MAX_OPEN_SOCKETS) gleichzeitige Verbindungen zu. MAX_OPEN_SOCKETS schützt vor Dateideskriptor-Erschöpfung; wenn nicht gesetzt, wird es aus dem weichen RLIMIT_NOFILE minus RESERVED_SOCKETS (Standardwert 64, zurückgehalten für den Datenbank-Pool, Listener, stdio und DNS-Sockets) abgeleitet. Wenn die Obergrenze erreicht ist und eine neue Verbindung eintrifft, schafft der Server Platz, indem er die am längsten untätige Verbindung im Bereich, dessen gesamte Leerlaufzeit am größten ist schließt (ein 421, dann schließen), sodass die gierigste Quelle ihren untätigsten Slot zuerst verliert. „Untätig“ ist die Zeit, die eine Verbindung blockiert auf Client-Eingabe zwischen Befehlen wartet — nicht die Zeit, die mit dem Empfangen eines Nachrichtentexts oder der Verarbeitung verbracht wird.

  • Obergrenze pro Bereich. Ein Quellbereich darf höchstens MAX_CONNECTIONS_PER_IP (Standardwert 16) Verbindungen gleichzeitig halten, und alle lokalen Verbindungen zusammen höchstens MAX_LOCAL_CONNECTIONS (Standardwert 32). Die Ratenbegrenzung unten beschränkt die Nebenläufigkeit nicht für sich allein — eine Verbindung pro Sekunde, jede beschäftigt gehalten, füllt jede Obergrenze in Minuten, und die Verdrängung gewinnt nur untätige Verbindungen zurück —, und ohne die lokale Obergrenze könnte ein lokaler Benutzer jeden Slot belegen und entferntes SMTP aushungern. Eine Verbindung über einer der beiden wird wie eine ratenbegrenzte abgewiesen. 0 schaltet die jeweilige Obergrenze ab.

Zusätzlich werden angenommene neue Verbindungen pro Quellbereich durch einen Token-Bucket ratenbegrenzt: CONN_RATE_PER_SECOND (Standardwert 1) Nachfüllrate mit einem Burst von CONN_RATE_BURST (Standardwert 1). Eine Verbindung über der Rate erhält nach bestem Bemühen ein 421 und wird dann geschlossen, wobei sie für die Dauer dieses Schreibvorgangs einen Slot belegt — mit Absicht, damit eine Flut ratenbegrenzter Verbindungen nicht unbegrenzt viele Ablehnungs-Tasks erzeugen kann, die jeweils einen Deskriptor außerhalb von MAX_OPEN_SOCKETS festhalten. Greift die Ratenbegrenzung, während die Deskriptor-Obergrenze bereits erreicht ist, wird kein Slot belegt und die Verbindung ganz ohne Antwort geschlossen, da der 421-Schreibvorgang selbst den Deskriptor verbrauchen würde, der gerade geschont wird. (Auf einem Implicit-TLS-Listener entfällt das ablehnende 421 ebenfalls, da kein Klartext dem Handshake vorausgehen darf; die Verbindung wird einfach geschlossen.)

Innerhalb von Sitzungen und sitzungsübergreifend:

  • Nachrichten pro Sitzung. Eine Sitzung darf höchstens MAX_MESSAGES_PER_SESSION (Standardwert 100) Mail-Transaktionen beginnen (RSET setzt den Zähler nicht zurück); das nächste MAIL wird mit 421 beantwortet und die Verbindung geschlossen, sodass ein Absender mit mehr zuzustellender Mail sich neu verbindet — und das bemisst die Ratenbegrenzung.

  • Routing-Schleifen. Eine Nachricht, die den Ingress dieses Hosts bereits MAX_SELF_HOPS (Standard 5) Mal durchlaufen hat, wird am Ende der Daten mit 554 5.4.6 abgelehnt. Die Zählung stammt aus den Received:-Feldern, die der Ingress selbst schreibt. Die Ablehnung macht aus einer Schleife — eine Adresse an unserer eigenen Domain, die immer wieder an unseren eigenen MX weitergeleitet wird — einen einzigen Bounce durch die Stage, die sie weiterzuleiten versuchte.

  • Passwortraten. Drei fehlgeschlagene AUTH-Versuche schließen eine Sitzung. Sitzungsübergreifend darf ein Quellbereich AUTH höchstens AUTH_FAILURES_PER_HOUR (Standardwert 20) Mal fehlschlagen lassen, in einem Token-Bucket, der sich mit dieser Rate nachfüllt; ist er aufgebraucht, wird jedes weitere AUTH aus dem Bereich mit 421 beantwortet und die Verbindung geschlossen, ohne dass das SASL-Backend gefragt wird, bis sich der Bucket wieder füllt.

  • Rate lokaler Submission. Eine per AUTH_PEERCRED authentifizierte UNIX-Socket-Sitzung ist pro uid, über ihre Sitzungen hinweg, auf LOCAL_MESSAGES_PER_MINUTE (Standardwert 60) Nachrichten mit einem Burst von LOCAL_MESSAGE_BURST (Standardwert 120) begrenzt. Ein MAIL über der Rate wird mit 451 4.7.1 beantwortet, was pepsi-sendmail(1) in EX_TEMPFAIL umsetzt.

0 schaltet jeden dieser drei ab. Die sitzungsübergreifenden Zähler liegen im Speicher des Servers, ein Neustart vergisst sie also.

85.1.1.1.3.14. Privilegienabgabe

Die SMTP-Ports (25/465/587) sind privilegiert, daher wird pepsi-ingress oft als root gestartet. Es bedient jedoch nicht als root: Sobald jeder konfigurierte Listener gebunden ist (und etwaiges TLS-Schlüsselmaterial gelesen wurde), fällt der Prozess auf das unprivilegierte Dienstkonto pepsi-ingress ab, legt alle ergänzenden Gruppen ab und wechselt zu Gruppe und Benutzer-ID dieses Kontos, bevor er auch nur eine Verbindung annimmt. Das Konto muss daher existieren, bevor der Server als root gestartet wird; legen Sie es (zum Beispiel useradd --system --no-create-home --shell /usr/sbin/nologin pepsi-ingress) im Rahmen der Installation an.

Wenn die Abgabe beim Lauf als root nicht abgeschlossen werden kann — meist, weil das pepsi-ingress-Konto nicht existiert — protokolliert der Server einen Fehler und beendet sich ohne zu bedienen, statt zu riskieren, als root zu laufen. Wird er von einem Nicht-Root-Benutzer gestartet, wird keine Privilegienänderung vorgenommen (der Server läuft einfach als der aufrufende Benutzer); er läuft nie als root.

85.1.1.1.3.15. TLS-Schlüsselmaterial

TLS_CERT/TLS_KEY jedes Listeners mit MODE = tls/starttls werden einmalig beim Start gelesen, vor der obigen Privilegienabgabe.

Unter systemd läuft die Unit von Anfang an unter dem unprivilegierten Benutzer pepsi-ingress und besitzt nie root, kann also certbots nur für root lesbares /etc/letsencrypt/{live,archive} nicht öffnen. pepsi-setup(1) schreibt daher ein Drop-in /etc/systemd/system/pepsi-ingress.service.d/10-tls-credentials.conf mit einem LoadCredential=-Eintrag pro Datei: systemd öffnet sie beim Start der Unit als root und übergibt dem Dienst private Kopien unter $CREDENTIALS_DIRECTORY, die pepsi-ingress konsultiert, bevor es auf das Lesen des konfigurierten Pfades selbst zurückfällt. Führen Sie pepsi-setup run erneut aus, nachdem Sie ein Zertifikat hinzugefügt, verschoben oder entfernt haben, damit das Drop-in neu erzeugt wird. Da Credentials beim Start der Unit bereitgestellt werden, wird ein erneuertes Zertifikat erst nach einem Neustart ausgeliefert — der von pepsi-setup installierte certbot-Deploy-Hook führt ihn durch. Direkt gestartet (als root, mit Privilegienabgabe nach dem Binden) liest der Server die Dateien selbst, und es ist kein Credential beteiligt. Siehe pepsi-httpd(1), wo derselbe Mechanismus vollständig beschrieben ist.

Anders als pepsi-httpd, das viele Zertifikate bedient und eines überspringt, das es nicht laden kann, hat ein Listener hier genau eines: Ist es nicht lesbar, gibt es nichts, worauf zurückgefallen werden könnte, und der Server beendet sich, statt auf einem Port, den die Konfiguration als verschlüsselt ausweist, stillschweigend kein TLS anzubieten.

85.1.1.1.4. Befehle

serve

Bindet jeden konfigurierten [pepsi-ingress-listener-*]-Socket und bedient SMTP-Verbindungen, bis er unterbrochen wird. Erfordert, dass das Schema mit pepsi-setup(1) installiert wurde.

85.1.1.1.5. Globale Optionen

Diese globalen Optionen stehen vor dem Unterbefehl (ein nachgestelltes Flag wird abgelehnt).

-c FILE, –config FILE

Liest die Konfiguration aus FILE statt die Standardorte zu durchsuchen (siehe DATEIEN).

-L LOGLEVEL, –log LOGLEVEL

Setzt die Log-Ausführlichkeit. LOGLEVEL ist eines von error, warn, info, debug oder trace (Standardwert: info).

-v, –verbose

Zeigt Log-Meldungen aus allen Quellen, einschließlich Drittanbieter-Bibliotheken, die standardmäßig herausgefiltert werden.

-h, –help

Gibt eine Verwendungsübersicht aus und beendet sich.

-V, –version

Gibt die Version aus und beendet sich.

85.1.1.1.6. Signale

SIGINT

Leitet das Herunterfahren ein: Der Prozess stellt den Dienst ein und beendet sich mit 0. Noch laufende Sitzungen werden nicht abgearbeitet, und das müssen sie auch nicht — eine Nachricht wird ihrem Client erst dann bestätigt (250), wenn sie committet ist, sodass eine unterbrochene Sitzung ihre Nachricht entweder sicher in der Warteschlange hat oder nie etwas anderes erfahren hat, und der sendende MTA es erneut versucht.

SIGTERM

Nicht abgefangen: Die Standardbehandlung beendet den Prozess, was aus demselben Grund sicher ist. Dies ist, was systemctl stop pepsi-ingress sendet.

85.1.1.1.7. Exit-Status

0

Erfolgreicher Abschluss (einschließlich eines sauberen Herunterfahrens von serve).

1

Ein Fehler ist aufgetreten, zum Beispiel eine fehlerhafte Konfigurationsdatei, eine fehlgeschlagene Datenbankverbindung oder ein Listener, der nicht gebunden werden konnte. Der Grund wird in das Journal geschrieben.

85.1.1.1.8. Umgebung

XDG_CONFIG_HOME, HOME

Wird verwendet, um die Standard-Konfigurationsdatei zu finden, wenn –config nicht angegeben ist (siehe DATEIEN).

LISTEN_FDS, LISTEN_PID, LISTEN_FDS_FIRST_FD

Wird für die systemd-Socket-Aktivierung beachtet. Ein mit SERVE = systemd konfigurierter Listener übernimmt den von systemd übergebenen Dateideskriptor an dem Index, den seine FD_INDEX-Option angibt. LISTEN_FDS_FIRST_FD gibt die Nummer des ersten übergebenen Deskriptors an und hat den Standardwert 3 (SD_LISTEN_FDS_START); es wird zugunsten anderer Supervisoren als systemd gelesen, die es setzen. LISTEN_FDNAMES wird nicht herangezogen: Ein FileDescriptorName= benennt die Deskriptoren einer ganzen .socket-Unit statt eines einzelnen ListenStream=, könnte diese Listener also nicht auseinanderhalten. Der lokale Submission-Socket wird stattdessen über seine gebundene Adresse gefunden — siehe unten.

Das ausgelieferte pepsi-ingress.socket übergibt vier: die Ports 25, 465 und 587, die die Listener-Abschnitte über FD_INDEX 0, 1 und 2 beanspruchen, sowie den lokalen Submission-Socket /run/pepsi/submission.sock, der anders beansprucht wird — siehe unten. Diesen UNIX-Socket in der Unit statt im Server zu binden ist Absicht: systemd erzeugt ihn als root, bevor der Dienst startet, sodass das unprivilegierte pepsi-ingress nie Schreibzugriff auf /run/pepsi braucht — ein Laufzeitverzeichnis, das auch pepsi-httpd und die Setup-Apply-Türklingel deklarieren und das systemd demjenigen von ihnen neu zuschreibt, der zuletzt gestartet ist.

Der lokale Submission-Socket hat keinen FD_INDEX und keinen Listener-Abschnitt. pepsi-ingress bedient ihn bedingungslos (aus [pepsi-sendmail] SOCKET) und findet den Deskriptor, indem es den Kernel fragt, welcher der übergebenen Sockets an diesen Pfad gebunden ist. Ein Index wäre eine Position unter den ListenStream=-Einträgen der Unit und verschöbe sich daher, sobald ein SMTP-Port hinzukommt oder wegfällt; die gebundene Adresse kann mit nichts aus dem Takt geraten. Passt kein übergebener Deskriptor, wird der Pfad stattdessen vom Server gebunden — was ohne systemd geschieht und was dort das Scheitern des Bindens fatal macht.

Jeder andere übergebene Deskriptor muss von irgendeinem Listener-Abschnitt beansprucht werden. Einer, der es nicht wird, wird beim Start mit einer Warnung gemeldet, die seinen Index nennt, denn systemd nimmt auf ihm weiter Verbindungen an, die nie jemand beantworten wird — ein Client blockiert dann, bis sein eigener Lese-Timeout abläuft (zwei Minuten bei pepsi-sendmail(1)), statt sofort abgewiesen zu werden. Beides ist ein Aufschub, aber eines davon lässt den Aufrufer bei jeder Nachricht hängen. Wenn Sie einen Listener-Abschnitt entfernen, entfernen Sie auch dessen ListenStream= aus der Unit.

JOURNAL_STREAM

Wenn gesetzt (d. h. beim Lauf unter systemd), werden Zeitstempel in UTC ohne lokalen Zeitzonen-Offset ausgegeben.

85.1.1.1.9. Dateien

Wenn –config nicht angegeben ist, wird die erste vorhandene Datei aus der folgenden Liste verwendet:

  • $XDG_CONFIG_HOME/pepsi.conf

  • $HOME/.config/pepsi.conf

  • /etc/pepsi/pepsi.conf

  • /etc/pepsi.conf

Jede Pepsi-Komponente teilt sich dieselbe Konfigurationsdatei, sodass dies dieselbe Liste ist, die jede durchsucht.

85.1.1.1.10. Beispiele

Das Schema installieren (einmal, mit pepsi-setup) und das Bedienen starten:

pepsi-setup   -c /etc/pepsi/pepsi.conf
pepsi-ingress -c /etc/pepsi/pepsi.conf serve

Prüfen, für welche Domains Mail angenommen wird (mit pepsi-config(1)):

pepsi-config -c /etc/pepsi/pepsi.conf get pepsi-ingress ACCEPTED_DOMAINS

Die zusammengeführte Konfiguration mit Quellannotationen ausgeben:

pepsi-config -c /etc/pepsi/pepsi.conf dump --diagnostics

85.1.1.1.11. Siehe auch

pepsi-config(1), pepsi.conf(5), pepsi-setup(1), pepsi-dispatch(1), pepsi-sendmail(1), pepsi.state(7), systemd.socket(5)

85.1.1.1.12. Fehler

Melden Sie Fehler an den Pepsi-Issue-Tracker.