85.1.3. pepsi-httpd

serve MTA-STS policy, Web Key Directory, the Outlook add-in and metrics

Handbuchabschnitt:

1

85.1.3.1.1. Name

pepsi-httpd - HTTP/HTTPS-Server für MTA-STS, das Web Key Directory, das Outlook-Add-in und Metriken.

85.1.3.1.2. Übersicht

pepsi-httpd [GLOBAL-OPTIONS] serve

pepsi-httpd [GLOBAL-OPTIONS] prune

85.1.3.1.3. Beschreibung

pepsi-httpd ist der HTTP/HTTPS-Server von Pepsi. Er bindet einen oder mehrere Listener und routet jede Anfrage durch eine generische Dispatch-Tabelle, wobei er nach HTTP-Methode und URL-Form abgleicht: Der Pfad einer Route ist eine Liste von Segmenten, deren jedes ein Literal, eine benannte Erfassung eines Segments oder eine Erfassung des gesamten Rests ist. Routen werden in Registrierungsreihenfolge probiert, und die erste, deren Methode und Pfad passen, gewinnt. Weitere Endpunkte werden als einzelne Tabelleneinträge hinzugefügt.

Jeder [pepsi-httpd-listener-<name>]-Abschnitt bindet einen Socket — SERVE = tcp (BIND_TO/PORT, Standardport 443), unix (UNIXPATH) oder systemd (Socket-Aktivierung) — mit MODE = plain oder tls. Ein TLS-Listener wählt sein Zertifikat pro Verbindung anhand des SNI-Hostnamens des Clients: Jeder [pepsi-httpd-cert-<name>]-Abschnitt führt einen oder mehrere SNI-Hostnamen mit einem TLS_CERT/TLS_KEY-Paar auf, und das eigene TLS_CERT/TLS_KEY des Listeners (falls vorhanden) ist der Rückfall für Verbindungen, die kein passendes SNI vorlegen.

Wenn bereits ein anderer Webserver die Ports 80/443 besitzt, konfiguriert pepsi-setup(1) stattdessen eine Reverse-Proxy-Installation. Der Listener bleibt socket-aktiviert (SERVE = systemd), aber Klartext-HTTP (MODE = plain); der Wechsel geschieht stattdessen in der pepsi-httpd.socket-Unit: pepsi-setup legt ein Drop-in-Override (/etc/systemd/system/pepsi-httpd.socket.d/10-reverse-proxy.conf) ab, das das ausgelieferte ListenStream=443 zurücksetzt und es an einen UNIX-Socket unter /run/pepsi/httpd.sock neu bindet (Modus 0660, im Besitz der eigenen Gruppe des Frontservers — www-data, wo es diese Gruppe gibt, sonst nginx/apache/httpd), sodass systemd den fd dieses Sockets genau so an pepsi-httpd übergibt, wie es den Port-443-Socket übergeben würde. Der Frontserver terminiert TLS und leitet die mta-sts.<domain>-Anfragen an ihn weiter; in diesem Modus hält pepsi-httpd keine eigenen Zertifikate. Das Löschen des Drop-ins und das Ausführen von systemctl daemon-reload stellt die direkte :443-Bindung wieder her. Siehe pepsi-setup(1) und --no-reverse-proxy.

85.1.3.1.4. Endpunkte

GET /.well-known/mta-sts.txt

Gibt die MTA-STS-Richtlinie (RFC 8461) zurück, gebaut aus den [pepsi]-MTA_STS_*-Optionen und dem [pepsi-ingress]-HOSTNAME (dem mx). Die Richtlinie wird nur bereitgestellt, wenn der Host der Anfrage mta-sts.<domain> für eine Domain in ACCEPTED_DOMAINS ist; jeder andere Host (oder MTA_STS_MODE = none) ergibt 404. Sie hier bereitzustellen erspart das händische Hosten der Richtliniendatei; pepsi-setup(1) gibt den _mta-sts-TXT-Eintrag zur Veröffentlichung im DNS aus.

GET /mail/config-v1.1.xml

GET /.well-known/autoconfig/mail/config-v1.1.xml

Liefert das Dokument zur Autokonfiguration von Mailkonten (draft-ietf-mailmaint-autoconfig): das XML, das ein Mail-Client abholt, wenn er nur die Adresse des Benutzers kennt, um herauszufinden, welche Server zu verwenden sind, auf welchen Ports, unter welcher Transportsicherheit und mit welcher Authentifizierung.

Dies sind die ersten beiden Sprossen der Rückfallkette, die der Draft definiert, und der Host wählt zwischen ihnen: Die erste wird bedient, wenn der Anfrage-Host autoconfig.<domain> ist (die verpflichtende Sprosse und die, die Clients zuerst versuchen), die zweite, wenn er <domain> selbst ist. In beiden Fällen muss DOMAIN in ACCEPTED_DOMAINS stehen; jeder andere Host ergibt 404. Der optionale ?emailaddress=-Parameter des Drafts wird angenommen und ignoriert — das Dokument nennt %EMAILADDRESS%, den Platzhalter, den der Client ersetzt, sodass nie anfragegesteuerter Text in die Antwort interpoliert wird.

Die Antwort ist text/xml; charset=utf-8, eine Stunde lang cachefähig und — wie der Draft es verlangt — öffentlich: Sie trägt keine Authentifizierung, weil ein Client sie lesen muss, bevor er wissen kann, wie er sich authentifiziert.

Das Dokument wird aus [pepsi-autoconfig] gebaut, und der Endpunkt antwortet mit 404, bis dieser Abschnitt mindestens einen eingehenden Server benennt (IMAP_HOST oder POP3_HOST). Der ausgehende Server wird aus dem [pepsi-ingress-listener-*] mit dem Kennzeichen SUBMISSION = yes abgeleitet, sofern SMTP_HOST ihn nicht überschreibt. Die Veröffentlichung erfordert einen autoconfig.<domain>-DNS-Eintrag und ein Zertifikat, das ihn abdeckt; siehe pepsi.conf(5).

GET /.well-known/openpgpkey/hu/HASH

GET /.well-known/openpgpkey/policy

GET /.well-known/openpgpkey/DOMAIN/hu/HASH

GET /.well-known/openpgpkey/DOMAIN/policy

Das Web Key Directory (draft-koch-openpgp-webkey-service), sowohl in seiner direkten Form (unter der Domain selbst bedient) als auch in seiner erweiterten Form (unter openpgpkey.<domain> bedient, mit der im Pfad wiederholten Domain). HASH ist der z-base-32-SHA-1 des kleingeschriebenen lokalen Teils, der neben der Domain gespeichert und indiziert wird, sodass eine Anfrage eine indizierte Abfrage kostet.

Ein Schlüssel wird nur für eine Domain in ACCEPTED_DOMAINS und nur aus einer Identität zurückgegeben, die published, active und OpenPGP ist. Die Antwort ist der binäre übertragbare öffentliche Schlüssel — nicht Armor — mit Content-Type: application/octet-stream und Access-Control-Allow-Origin: *, wie die Spezifikation es verlangt. Ein unbekannter Hash ergibt 404 mit leerem Nachrichtentext.

Die policy-Datei ist ein 200 mit einem Nachrichtentext der Länge null. Sie trägt keine Flags, aber sie muss existieren: GnuPG deutet ihr Fehlen als „diese Domain betreibt kein Web Key Directory“ und gibt auf, bevor es nach einem Schlüssel fragt.

Der Parameter ?l=<local-part> wird gelesen und ignoriert. Ihn zu beachten würde den Endpunkt zu einer Abfrage nach einer vom Aufrufer gelieferten Adresse machen statt nach einem Hash, den der Aufrufer bereits kennen musste.

Der Endpunkt bedient nur die eigenen veröffentlichten Identitäten dieser Installation (crypto_identity). Er bedient nie einen zwischengespeicherten Korrespondentenschlüssel (peer_key): Dieses Material wurde nie von uns verifiziert, und es steht uns nicht zu, es unter unserem eigenen Namen zu veröffentlichen. Es gibt keine Option, das zu ändern.

Es gibt hier bewusst keine Ratenbegrenzung und keine einheitliche 404-Formung. Ein Web Key Directory ist konstruktionsbedingt ein öffentliches Orakel, der Hash deckt nur den lokalen Teil ab (er beantwortet also Rateversuche, statt aufzuzählen), und die Adressen stehen außen auf jeder Nachricht, die die Domain versendet. Siehe pepsi-keys(1) und das Kapitel zur Schlüsselverwaltung im Handbuch.

Für die erweiterte Form muss openpgpkey.<domain> auf diesen Host auflösen und das ausgelieferte Zertifikat den Namen abdecken; es ist ein SNI-Name auf demselben HTTPS-Listener, kein zweiter Listener. pepsi-setup(1) fügt ihn der certbot-Anforderung hinzu und meldet jeden, der nicht auflöst.

GET /addin/manifest.xml

GET /addin/taskpane.html

Das Outlook-Add-in: ein Manifest und ein einseitiger Aufgabenbereich, die einem Benutzer eines Mailsystems hinter diesem Gateway erlauben, für eine Nachricht Signierung oder Verschlüsselung anzufordern. Der Bereich setzt X-Pepsi-Sign / X-Pepsi-Encrypt auf der gerade verfassten Nachricht; pepsi-stage-encrypt(1) liest sie und entfernt sie, bevor die Nachricht auf die Leitung geht.

Beide Routen ergeben 404, sofern [pepsi-httpd] ADDIN nicht gesetzt ist. Zwei Platzhalter werden je Anfrage ersetzt: der öffentliche https://-Origin dieses Gateways (aus ADDIN_URL, sonst dem Host der Anfrage; das Schema ist immer https, weil Outlook sich weigert, Add-in-Ressourcen über einfaches HTTP zu laden – ein konfigurierter http://-Origin wird beim Start abgelehnt, es sei denn, sein Host ist Loopback, was für lokale Tests erlaubt ist) und ein aus diesem Origin abgeleiteter Manifest-Bezeichner — stabil für eine Installation und zwischen Installationen verschieden, sodass zwei per Sideloading in eine Exchange-Organisation eingebrachte Gateways nicht kollidieren. Ein Host, der irgendetwas anderes enthält, als ein Hostname und Port enthalten dürfen, wird mit 400 abgelehnt statt maskiert.

Die Verteilung erfolgt per Sideloading: Der Exchange-Administrator richtet „Integrierte Apps“ auf die Manifest-URL. Siehe das Kapitel „Microsoft Exchange als Gateway“ im Handbuch.

GET /metrics

Pipeline-Statistiken im Prometheus-Text-Expositionsformat. Live-Anzeigen (pepsi_stage_active_messages, pepsi_pause_backlog) werden direkt aus der Warteschlange gelesen; Zähler (pepsi_stage_timeouts_total, pepsi_stage_crashes_total, pepsi_stage_messages_total, pepsi_stage_duration_seconds_total und die globalen pepsi_stages_executed_total / pepsi_messages_processed_total) stammen aus den Statistiktabellen, die pepsi-dispatch(1) aktualisiert.

Der Endpunkt ist administrativ: Warteschlangentiefen, Absturz- und Timeout-Zahlen je Stage, Gesamtsummen über die Lebensdauer und jeder vom Operator gewählte Stage-Name beschreiben zusammen, wie viel Mail diese Installation trägt und wie ihre Pipeline gebaut ist. Er wird daher nur auf einem Listener mit dem Kennzeichen ADMIN = yes bedient, der die administrative Oberfläche tatsächlich tragen darf, und antwortet überall sonst mit einem schlichten 404 — dem, was jeder unbekannte Pfad erhält. Anders als /api/v1 verlangt er kein Credential, weil ein Prometheus-Scraper keines vorzulegen hat: Das Listener-Kennzeichen ist die gesamte Zugriffskontrolle, kennzeichnen Sie also nur einen Listener, den allein das Überwachungssystem erreichen kann.

Richten Sie den Scraper auf den administrativen Listener, oder ergänzen Sie ADMIN = yes bei dem Listener, den er bereits abfragt (ein Klartext-Listener muss Loopback oder ein UNIX-Socket sein — siehe Die administrative Oberfläche unten). pepsi-httpd selbst an eine private Adresse zu binden ist keine Alternative: Derselbe Prozess muss mta-sts.<domain> (RFC 8461) und openpgpkey.<domain> aus dem öffentlichen Internet beantworten, und es gibt keine Listener-Bindung je Route. Die erste Abfrage, die abgelehnt wird, weil ihr Listener nicht gekennzeichnet ist, wird mit dem Namen des Listeners im Journal vermerkt.

POST /resume

Gibt eine pausierte Nachricht zurück in die Pipeline frei: Der passende Datensatz wird von paused auf pending gesetzt und der Dispatcher benachrichtigt, sodass die zuständige Stage erneut läuft. Dies ist das Ziel des GNU-Taler-pepsi-resume-Zahlungs-Webhooks (siehe pepsi-setup(1) und [pepsi-payments]), der aufgerufen wird, wenn eine Bestellung bezahlt ist.

Die Anfrage muss Authorization: Bearer <token> tragen, das zum konfigurierten RESUME_AUTHORIZATION_TOKEN passt (in konstanter Zeit verglichen), und einen JSON-Nachrichtentext {"message_id": "<token>"}, der das externe Token der Nachricht (die Händler-order_id) benennt. Antworten: 200, wenn die Nachricht existiert (eine pausierte Nachricht wurde fortgesetzt, oder sie war bereits nicht pausiert — der Aufruf ist idempotent), 404, wenn das Token unbekannt ist, 401 bei einer fehlgeschlagenen Autorisierung, 400 bei einem fehlerhaften Nachrichtentext, 413 bei einem Nachrichtentext über 4 KiB. Der Endpunkt ist deaktiviert (404), wenn RESUME_AUTHORIZATION_TOKEN nicht konfiguriert ist.

/api/v1/…

Die administrative API — die Warteschlange, die Gesundheitszusammenfassung, die Konfiguration, der Schlüsselspeicher, das Prüfprotokoll. Nur auf einem Listener mit dem Kennzeichen ADMIN = yes bedient, und nur einem authentifizierten Prinzipal. Siehe Die administrative Oberfläche unten und das Kapitel „Die administrative API“ im Handbuch für die Endpunktreferenz, die Tabelle der Geltungsbereiche und die Fehlerform.

/ui, /ui/…

Die administrative Konsole: ein serverseitig gerendertes Browser-Front-End für die Warteschlange, den Schlüsselspeicher, die Konfiguration und die Journale. Auf denselben ADMIN = yes-Listenern bedient wie /api/v1, unter derselben Authentifizierung, denselben Prüfungen der Geltungsbereiche und demselben Prüfprotokoll — sie ist ein Client der API und fügt ihr keine Fähigkeit hinzu. Die Seiten sind

/ui (Dashboard), /ui/login, /ui/logout, /ui/queue, /ui/queue/ID, /ui/queue/ID/{requeue,bounce,cancel}, /ui/identities, /ui/identities/ID, /ui/identities/ID/{publish,unpublish,vks,primary,revoke,delete}, /ui/identities/request/{generate,register}, /ui/peers, /ui/peers/ID/delete, /ui/ca-trust, /ui/ca-trust/ID/delete, /ui/config, /ui/config/SECTION, /ui/logs, /ui/logs/{mail,tls,dns}, /ui/domains, /ui/domains/check, /ui/secure, /ui/secure/TOKEN, /ui/secure/TOKEN/ACTION, /ui/setup, /ui/setup/STEP (GET und POST), /ui/setup/tasks (GET und POST), /ui/setup/tasks/ID und das einzelne Stylesheet /ui/static/console.css.

Warten auf eine Setup-Aufgabe. GET /api/v1/setup/tasks/ID nimmt ?wait=SECONDS (höchstens 60, kombinierbar mit ?since=SEQ) und antwortet, sobald die Aufgabe beendet ist oder eine Fortschrittszeile nach SEQ hat, oder wenn die Zeit abgelaufen ist; die Aufgabenseite der Konsole wartet auf dieselbe Weise bis zu 20 Sekunden je Neuladen. Keines von beiden fragt die Datenbank zyklisch ab: Der Server hält eine LISTEN-Verbindung auf den Kanälen setup_task_done und setup_task_progress, auf denen die Schreibvorgänge des Appliers mit der Aufgaben-ID benachrichtigen, und weckt die Anfragen, die auf diese Aufgabe warten. Die Verbindung ist vom Anfrage-Pool getrennt und verbindet sich mit wachsender Verzögerung selbst neu; solange sie unterbrochen ist, kehrt ein Warten sofort zurück, statt zu hängen, und die Konsolenseite fällt darauf zurück, sich alle fünf Sekunden neu zu laden.

Der Telemetrie-Schalter. Die Frage SHARE_TELEMETRY des Interviews kann nur mit ja beantwortet werden, solange pepsi-telemetry-client(1) läuft (ruhend, solange Telemetrie ausgeschaltet ist) und [pepsi] SYSTEM_ID konfiguriert ist, denn dieser Server erzeugt nie eine Kennung, und ein ja ohne Daemon, der danach handelt, bewirkte nichts. Beides erfährt er aus der Lebendigkeitszeile des Daemons in pepsi.telemetry_client, die er lesen, aber nicht schreiben darf und die für die Kennung einen Wahrheitswert trägt, nie ihren Wert. Andernfalls wird das Kontrollkästchen deaktiviert mit Hinweisen dargestellt (die Unit starten; eine SYSTEM_ID hinzufügen), GET /api/v1/setup/questions kennzeichnet die Frage mit "disabled": true und einem disabled_reason, und ein ja über PUT /api/v1/setup/answers oder eine write-config-Aufgabe wird mit 409 telemetry_client_not_ready abgelehnt. GET /api/v1/setup/telemetry meldet denselben Zustand (sharing, can_enable, reason, client). Das Ausschalten der Telemetrie wird nie abgelehnt. Das write-config des Appliers benachrichtigt den Daemon nach dem Schreiben der Datei, in beide Richtungen.

Eine Pfad-Positivliste eines Reverse-Proxys muss auch die Familien /ui/setup* und /ui/secure* einschließen: die erste ist das gesamte Setup-Interview im Browser, die zweite sind die Verwaltungsseiten für Secure-Link.

Die Konsole liefert kein JavaScript und lädt nichts von einem anderen Host; jede Aktion ist ein Formular, jede zerstörerische wird bestätigt, und jedes verändernde Formular trägt das CSRF-Token der Sitzung als verstecktes Feld (eine verändernde Anfrage, deren Origin einen anderen Host benennt, wird rundweg abgelehnt). Sie übt keine Dienststeuerung aus — es gibt bewusst keinen Neustart-, Abschalt- oder Sicherungsknopf. Siehe das Kapitel „Die administrative Konsole“ im Handbuch.

/3.0/…, /3.1/…

Die REST-API von GNU Mailman 3 – upstream Version 3.3.10, nachgebaut, damit für GNU Mailman 3 geschriebene Software (mailmanclient, Postorius, HyperKitty) unverändert gegen Pepsi läuft. Wird nur auf einem Listener mit dem Flag LIST_API = yes angeboten, unter derselben Bindungsregel wie ADMIN (Klartext auf Loopback, ein Unix-Socket oder TLS), und mit HTTP Basic gegen das einzige Paar [pepsi-list] API_USER/API_PASS authentifiziert. Das ist nicht /api/v1: anderes Publikum, anderer Sammlungsrahmen, andere Fehlerform, keine Scopes. Siehe das Kapitel „Die REST-API von GNU Mailman 3“ im Handbuch.

LIST_API_TESTING = yes bewaffnet zusätzlich GET /3.1/reserved/reset, das jede Mailinglisten- und Archivtabelle leert. Es existiert für die Kompatibilitätssuiten und darf auf einer Installation mit echter Post nicht gesetzt werden.

GET /favicon.ico

GET /favicon-VERSION.svg

Das Pepsi-Abzeichen als Browser-Icon, ausgeliefert auf jedem Listener, gleich mit welchen Flags: Ein Icon verrät nichts über die Installation. Die Konsole, ihre Anmeldeseite und die Mailinglisten-Seiten verlinken das SVG, dessen Name den Anfang seines SHA-256 trägt; da ein neues Icon eine neue URL ist, wird es mit Cache-Control: public, max-age=31536000, immutable gesendet. /favicon.ico (16, 32 und 48 px) ist das, was ein Browser von sich aus für eine Seite abruft, die kein Icon verlinkt; seine URL kann sich nicht ändern, daher ist es eine Woche lang cachebar. Beide tragen ein ETag und beantworten ein passendes If-None-Match mit 304. Die Dateien sind in das Binary einkompiliert.

/lists, /lists/…, /archives/list/…, /robots.txt

Die öffentliche Mailinglisten-Weboberfläche: das Listenverzeichnis, die Informationsseite einer Liste, die An- und Abmeldeformulare, die Bestätigungsseiten, das Ein-Klick-Abmeldeziel nach RFC 8058, das Archiv (in den URL-Formen von HyperKitty, sodass die Archived-At:-Links einer migrierten Installation weiter funktionieren) und seine Suche. Wird nur auf einem Listener mit dem Flag LISTS = yes angeboten, für jeden, ohne Zugangsdaten.

Dies ist das eine Flag, dessen Auslieferungsrat dem der beiden anderen entgegengesetzt ist. ADMIN ist für den Betreiber und LIST_API trägt ein gemeinsames Passwort, beide weigern sich daher, dort angeboten zu werden, wo ihre Zugangsdaten ein Klartextnetz überqueren würden; diese Oberfläche existiert, um aus dem offenen Internet erreicht zu werden, diese Einschränkung wird auf sie also absichtlich nicht angewandt. Ein Klartext-LISTS-Listener ist die Wahl des Betreibers und erhält eine Protokollzeile statt einer Weigerung – aber ein Anmeldeformular trägt die Adresse eines Menschen, legen Sie es also auf einen TLS-Listener.

Diese Seiten liefern kein JavaScript aus, und ihre Content-Security-Policy hat infolgedessen überhaupt kein script-src. /robots.txt erlaubt das Archiv und verbietet die Suche, also die eine Route, die je Anfrage eine Volltextabfrage ausführt; die Suche ist zusätzlich auf ihrem eigenen Budget ratenbegrenzt, denn ein Crawler, der die Datei ignoriert, muss dennoch überlebbar sein.

Beide Budgets sind Anfragen pro Minute je Quelladresse (ein IPv6-Client je /64) und werden aus dem Abschnitt [pepsi-list] gelesen:

WEB_RATE_LIMIT

(Ganzzahl, optional) Jede Route dieser Oberfläche und der Mitgliederebene unten. Standardwert 600.

WEB_SEARCH_RATE_LIMIT

(Ganzzahl, optional) Die Such-Route, zusätzlich zum obigen. Standardwert 30.

Ein Wert unter 1 wird auf 1 angehoben: keines von beiden lässt sich abschalten. Hinter einem Reverse-Proxy kommt jede Anfrage über den UNIX-Socket ohne Client-Adresse an, sodass sich alle Clients ein Budget teilen; heben Sie dort die Grenzen an und lassen Sie den Proxy je Client begrenzen.

Dasselbe Flag bietet auch die Mitgliederebene an – /lists/register, /lists/verify/<token>, /lists/sign-in, /lists/sign-out, /lists/reset und /lists/me – sowie die Konsole für Verwalter und Moderatoren unter /lists/<list-id>/admin.... Keines von beiden ist ein zweites Flag: ein Kontosystem, das niemand erreichen kann, ist keine Auslieferungsentscheidung, die jemand träfe.

Diese Seiten benutzen pepsi.list_session und die Cookies pepsi_list_session/pepsi_list_csrf, die nicht die pepsi_session/pepsi_csrf der Betreiberkonsole sind und dort nichts gewähren – und umgekehrt ebenso nicht. Befugnis über eine Liste ist eine Abfrage der Mitgliederliste und kein Kontoflag: ein list_member-Datensatz mit role = owner oder moderator für diese Liste, dazu list_user.is_server_owner als einzige globale Befugnis auf dieser Ebene. Ein Aufrufer ohne sie erhält die gewöhnliche Nicht-gefunden-Seite, denn auf einer öffentlichen Oberfläche hat eine Ablehnung, die sich unterscheidet, verraten, dass die Sache existiert. Siehe den Abschnitt „Zwei Kontosysteme“ im Handbuch.

85.1.3.1.5. Die administrative Oberfläche

Ein Listener mit ADMIN = yes bedient zusätzlich /api/v1, die /ui-Konsole und /metrics. Auf jedem anderen Listener antworten diese Routen mit einem schlichten 404, byte-für-byte demselben, den jeder unbekannte Pfad erhält, sodass den öffentlichen Listener zu veröffentlichen nicht die Administration veröffentlicht und ein öffentlicher Listener nicht daraufhin abgetastet werden kann, ob die Administration auf dieser Installation aktiviert ist.

/api/v1 und /ui authentifizieren und autorisieren zusätzlich jede Anfrage, wie unten beschrieben. /metrics tut das nicht: Ein Scraper hat kein Credential vorzulegen, das Listener-Kennzeichen ist daher alles, was davor steht.

pepsi-httpd weigert sich, sie auf einem gekennzeichneten Listener zu bedienen, der sie im Klartext von diesem Host wegtragen würde: Klartext-TCP wird nur auf einer Loopback-Adresse angenommen, während TLS-Listener und UNIX-Sockets stets in Frage kommen. Ein socket-aktivierter Listener (SERVE = systemd) kommt ohne TLS nie in Frage, was auch immer seine Unit bindet — auch ein ListenStream= auf Loopback oder auf einem UNIX-Pfad —, weil die Adresse des geerbten Deskriptors von hier aus nicht sichtbar ist und ihn als zulässig zu behandeln den strengsten Fall durch die lockerste Prüfung ließe. Da die ausgelieferte Installation socket-aktiviert ist, will die Administration einen eigenen Listener (einen UNIX-Socket oder einen mit TLS). Ein Listener, der die Prüfung nicht besteht, schreibt beim Start eine Warnung ins Journal und bedient nur die öffentlichen Endpunkte; pepsi-setup(1) meldet dasselbe zur Konfigurationszeit.

Drei Authentifizierungsmechanismen lösen sich zu einem Identitätsmodell auf, in dieser Reihenfolge probiert — ein ausdrücklich vorgelegtes Credential gewinnt immer, sodass ein bewusst eng gefasstes Token nie stillschweigend erweitert wird:

  1. Authorization: Bearer pepsi_<id>.<secret> — ein Datensatz in pepsi.api_token. Nur der Digest der geheimen Hälfte wird gespeichert.

  2. Ein Sitzungs-Cookie aus POST /api/v1/auth/login — oder aus dem eigenen Formular POST /ui/login der Konsole, das dieselbe Sitzung prägt —, mit einem bei jeder verändernden Anfrage erforderlichen CSRF-Token. Sitzungen haben standardmäßig eine Leerlaufzeitüberschreitung von 30 Minuten und eine harte Lebensdauer von 12 Stunden ([pepsi-admin] SESSION_IDLE und SESSION_LIFETIME).

  3. SO_PEERCRED auf einem UNIX-Socket-Listener: root oder ein Mitglied von [pepsi-admin] ADMIN_GROUP ist Administrator ganz ohne Credential. Das ist es, was den Bootstrap beim ersten Lauf funktionieren lässt und worauf sich die ausgelieferte Konfiguration stützt.

PAM wird bewusst nicht unterstützt: Es würde einen privilegierten Authentifizierungspfad und Systemkonto-Semantik in die Web-Schicht des Mailservers verlegen.

Die Autorisierung erfolgt über Geltungsbereiche, je Endpunkt deklariert (config:read, config:write, keys:read, keys:write, peers:write, queue:read, queue:write, logs:read, setup:write, secure:read, secure:write, dazu own:<address> für einen auf eine Adresse beschränkten Prinzipal). GET /api/v1/openapi.json wird aus der eigenen Routentabelle des Servers erzeugt und ist die maßgebliche Liste für einen gegebenen Build.

secure:read und secure:write sind getrennt, weil das Widerrufen einer Secure-Link-Nachricht deren einzige Kopie zerstört; eine unwiderrufliche Löschung gehört nicht hinter eine Fähigkeit namens „read“.

Schlüsselmaterial wird hier nie erzeugt oder widerrufen: privates Material gehört allein der Datenbankrolle pepsi-crypto. Einen servergeführten Schlüssel zu erzeugen (POST /api/v1/identities), den eigenen öffentlichen Schlüssel eines Benutzers zu registrieren (POST /api/v1/identities/client) und eine Identität zu widerrufen (PATCH /api/v1/identities/{id} mit {"status": "revoked"} und einem optionalen revocation_reason, was die private Hälfte eines reinen Signaturschlüssels vernichtet) wird stattdessen angefragt, als Zeilen generate-identity, register-client-key und revoke-identity in pepsi.setup_task, die pepsi-setup apply erneut validiert und als pepsi-crypto ausführt (siehe unten). Ein widerrufendes PATCH ist daher asynchron: es trägt allein status und antwortet mit der eingereihten Aufgabe; die Identität liest sich als revoked, sobald der Applier gelaufen ist. Die Registrierung umfasst kein privates Material, aber ein als eigener Schlüssel des Benutzers registrierter Schlüssel wird zu seinem öffentlichen Gesicht und verifiziert Signaturen als seine, daher darf eine kompromittierte Web-Schicht keinen solchen unterschieben können. Eine CSR zu erzeugen und ein ausgestelltes Zertifikat zu importieren, wird über HTTP nicht angeboten; verwenden Sie pepsi-keys(1).

Selbstbedienung für die eigenen Schlüssel. Ein auf eine Adresse beschränkter Prinzipal (own:<address>) darf, nur für diese Adresse, die drei Schlüssel-Schreiboperationen der Selbstbedienung im Stil von pEp erreichen: POST /api/v1/identities ({"address": ..., "vks": true|false}, vks optional und standardmäßig [pepsi-keys] VKS_PUBLISH), POST /api/v1/identities/client ({"address": ..., "key": "<armoured OpenPGP public key>"}, höchstens 64 KiB) und PATCH /api/v1/identities/{id} (dessen Feld vks_wanted, wenn true, eine Upload-Anfrage an den Keyserver für eine aktive OpenPGP-Identität festhält und sie über WKD veröffentlicht; den Upload übernimmt der Wiederholungsjob pepsi-keys identity publish --retry; und dessen status: revoked einen Widerruf anfragt). Jeder Handler prüft die Adresse erneut; die Identität einer anderen Adresse antwortet mit 404. Die beiden POST-Routen und ein widerrufendes PATCH antworten mit der eingereihten Aufgabe ({"task": ..., "kind": ..., "applier_notified": ...}), nicht mit dem Schlüssel, und der Prinzipal, der angefragt hat, darf dieser Aufgabe folgen: GET /api/v1/setup/tasks/ID antwortet jedem Prinzipal für eine Aufgabe, die er selbst eingereiht hat (404 für die eines anderen, wie für eine, die nicht existiert; der Besitz ist der requester_key der Aufgabe – der Selektor des Tokens, die Zeilennummer des Kontos oder der Login des Peers, von denen keiner je wiederverwendet wird –, nicht der Anzeigename requested_by), und GET /api/v1/setup/tasks listet einem Prinzipal ohne setup:write nur diese Aufgaben. Die Schlüsselformulare der Konsole antworten unter denselben Bedingungen mit der Seite der Aufgabe, /ui/setup/tasks/ID. DELETE /api/v1/identities/{id} bleibt dem Operator vorbehalten (keys:write): ein Benutzer setzt einen Schlüssel außer Dienst, er löscht nicht dessen Zeile. Die Konsole bietet dasselbe über die Formulare Generate a server-managed key und Register my own key auf der Identitäten-Seite sowie die Aktionen Upload to the key server und Revoke auf der Seite einer Identität.

Der Applier lässt diese drei Aufgabenarten mit einer eigenen Autorisierungsschranke zu: der anfragende Prinzipal muss keys:write besessen haben, oder own:<address> für genau die in den Parametern der Aufgabe genannte Adresse (setup:write allein genügt nicht). Anschließend verlangt er, dass die Adresse zu einer Domain aus [pepsi-ingress] ACCEPTED_DOMAINS gehört, und für eine Registrierung ein OpenPGP-Zertifikat, dessen User-IDs die Adresse nennen; für einen Widerruf muss die Identität zu dieser Adresse gehören. Ohne den Applier antworten alle drei mit 503 setup_applier_unavailable, wie jeder andere Applier-Pfad; das Einreihen benötigt außerdem die Verbindung [pepsi-admin] CONFIG_DB (andernfalls 503 setup_write_unavailable).

Der zweite Faktor. Jeder der drei Rümpfe (und die Formulare der Konsole) nimmt ein optionales "otp": "123456", den aktuellen Zweitfaktor-Code des Adressinhabers (siehe pepsi-keys(1), otp). Dieser Server kann ihn nicht prüfen – er hat überhaupt keine Berechtigung auf pepsi.otp_key –, also reicht er den Code nur in den Parametern der Aufgabe weiter, wo der Applier ihn für eine allein aufgrund von own:<address> zugelassene Aufgabe prüft, bevor er handelt; ein fehlender, falscher, wiederverwendeter oder gesperrter Code lässt die Aufgabe mit dem Grund fehlschlagen. Ein Prinzipal mit keys:write wird nicht gefragt. DELETE /api/v1/otp/ADDRESS (keys:write, unter own: nie erreichbar) reiht eine reset-otp-Aufgabe ein, die den zweiten Faktor der Adresse entfernt; so wird ein gesperrter entsperrt. Die synchronen Flag-Änderungen von PATCH /api/v1/identities/{id} (is_primary, published, vks_wanted) werden hier ausgeführt und sind daher nicht durch den zweiten Faktor geschützt.

Zwei Operationen verweigert dieser Server bewusst: die Konfigurations-Überlagerung zu schreiben (503, sofern [pepsi-admin] CONFIG_DB nicht eine Verbindung benennt, die sich als Rolle pepsi-config authentifiziert, was der Server beim Start prüft) und jede privilegierte Setup-Anfrage, wenn das Applier-Paket nicht installiert ist (503, siehe Der privilegierte Applier, und ohne ihn auskommen unten).

Die Aufbewahrungsrichtlinie des Audit-Logs und, wenn es aktiviert ist, des Mail-Logs wird vom Befehl prune dieses Programms angewandt, nicht vom Server: Jedes Mail verarbeitende Konto darf an diese Tabellen anhängen, aber nicht aus ihnen löschen, und die Rolle pepsi-httpd ist die einzige Dienstrolle, die DELETE auf ihnen besitzt (der einzige andere Inhaber ist die Operator-Rolle pepsi-config). Der mitgelieferte pepsi-log-prune.timer führt ihn täglich unter diesem Konto aus, sodass die Aufbewahrung gilt, ob der Webserver läuft oder nicht — früher war sie eine Hintergrundaufgabe von serve, die nur gestartet wurde, wenn ein Listener die administrative Oberfläche bediente, und eine Installation ohne die Konsole behielt jeden Eintrag für immer.

Bemerkung

Ein Binärprogramm bedient die öffentliche und die administrative Oberfläche. Ein Fehler im gemeinsamen Serverprozess ist daher ein Fehler in beiden; die Isolierung zu konfigurieren ist Sache des Operators — binden Sie den administrativen Listener an einen UNIX-Socket oder an das Loopback.

GET /secure/TOKEN

Die Einstiegsseite des Secure-Link-Ausweichportals. Ohne Sitzung fragt sie nach der PIN; mit einem gültigen Sitzungs-Cookie rendert sie die Nachricht. Antwortet mit 404, wenn das Token unbekannt ist (oder das Portal nicht konfiguriert ist), mit 410, wenn die Nachricht abgelaufen ist, und mit 429, solange das Token gesperrt ist. Siehe das Secure-Link-Kapitel im Handbuch und pepsi-stage-secure-link(1).

POST /secure/TOKEN

Prüft die PIN. Bei Erfolg setzt sie das Sitzungs-Cookie und leitet (303) auf die obige Seite um; bei falscher PIN rendert sie das Formular mit 401 erneut, und bei dem Versuch, der MAX_ATTEMPTS ausschöpft, antwortet sie mit 429 und sperrt das Token für einen Zeitraum, der sich mit jeder weiteren Sperre verdoppelt. Eine Übermittlung ohne PIN wird ebenfalls mit 401 beantwortet, aber nicht auf MAX_ATTEMPTS angerechnet: Sie ist kein Rateversuch, und ein Client, der das Formular leer erneut absendet, würde sonst die Versuche eines legitimen Empfängers für ihn verbrauchen. Die PIN wird im Nachrichtentext der Anfrage übermittelt und erscheint nie in einer URL.

GET /secure/TOKEN/part/N

Lädt Anhang N der Nachricht herunter. Autorisiert durch die Sitzung, nie durch die PIN. Wird stets als Content-Type: application/octet-stream mit Content-Disposition: attachment und einer Sandbox-Content-Security-Policy ausgeliefert, welchen Medientyp die Nachricht auch behauptete — ein text/html-Teil, der aus dem eigenen Origin des Portals inline gerendert wird, ist genau das, was der Entwurf ausschließt.

POST /secure/TOKEN/reply

Verfasst eine Antwort, als multipart/form-data (ein text-Feld und optionale files). Autorisiert durch die Sitzung. Empfänger und Umschlagabsender der Antwort werden dem gespeicherten Datensatz entnommen und sind keine Eingaben; der Nachrichtentext ist stets text/plain; Uploads werden durch MAX_REPLY_SIZE / MAX_REPLY_FILES während des Streamens des Nachrichtentexts gedeckelt; und die injizierte Nachricht trägt bewusst kein state.local_origin, sodass sie keine Submission-Privilegien erben kann. Antwortet mit 404, wenn REPLY_STAGE nicht gesetzt ist (Antworten erfolgt je Installation auf Opt-in).

Jede Portalantwort trägt, je Route statt je Host, Content-Security-Policy: default-src 'none' (mit einer Nonce je Antwort für das eine eingebettete Stylesheet und keiner Skriptquelle), Referrer-Policy: no-referrer, X-Content-Type-Options: nosniff, X-Frame-Options: DENY und Cache-Control: no-store. Das Sitzungs-Cookie ist auf Path=/secure/ beschränkt, HttpOnly, Secure und SameSite=Strict, sodass das Teilen eines Origins mit den obigen Endpunkten konstruktionsbedingt sicher ist und nicht erst durch Disziplin bei der Installation.

85.1.3.1.6. Der privilegierte Applier, und ohne ihn auskommen

pepsi-httpd führt nie eine privilegierte Änderung durch. Es hält einen Absichtsdatensatz in pepsi.setup_task fest und verbindet sich mit einem Türklingel-Socket; systemds Socket-Aktivierung macht aus dieser Verbindung ein kurzlebiges root-pepsi-setup apply, das den Datensatz prüft und die Arbeit erledigt. Das vollständige Vertrauensmodell steht in pepsi-setup(1), Das Vertrauensmodell des Appliers.

Das macht die Fähigkeit entfernbar, und Debian paketiert sie getrennt:

pepsi

Der eigentliche Mailserver: die Binärprogramme — einschließlich pepsi-setup selbst —, das Schema, die Vorlagen und jede andere Unit (abgesehen von den getrennt paketierten pepsi-stage-detect-language und pepsi-telemetry). Eine vom Terminal aus verwaltete Installation braucht sonst nichts.

pepsi-httpd-admin

pepsi-setup-apply.socket und pepsi-setup-apply.service, und sonst nichts. Es ist das, was aus einer HTTP-Anfrage eine Änderung unter /etc/pepsi macht. pepsi Recommends es, sodass eine Standardinstallation es hat; apt remove pepsi-httpd-admin gelingt, ohne pepsi anzufassen.

Mit entferntem Paket gibt es nichts, was pepsi.setup_task leert. Die Schleife wird am Mechanismus gebrochen, nicht am Knopf: Ein Insert in diese Tabelle wird inaktiv, was auch immer mit der Web-Schicht geschieht. (Eine Quellinstallation bekommt denselben Hebel aus make install INSTALL_ADMIN_UNITS=no.)

Inaktiv, und es bleibt so: Das Paket wieder einzuspielen wirkt nicht auf das, was sich in seiner Abwesenheit angesammelt hat. Sein postinst führt pepsi-setup apply --clear aus, das jeden eingereihten Datensatz löscht, ohne einen auszuführen, sodass das Scharfstellen des Appliers nie zugleich das Ereignis ist, das eine Anfrage ausführt, an die sich niemand erinnert. Siehe pepsi-setup(1).

85.1.3.1.6.1. Was die Konsole dann tut

Nur lesend, und sie sagt es. Jede Einstellung, nach der das Setup-Interview fragt, wird weiterhin gerendert, mit ihrer vorbereiteten Antwort oder ihrem Standardwert; jedes Bedienelement, das eine ändern würde, ist deaktiviert, unter einem Banner, das das fehlende Paket benennt. Die Seiten werden nicht verborgen: Ein Operator, der den Applier bewusst entfernt hat, muss weiterhin sehen können, wozu die Installation konfiguriert ist.

Die API weist die entsprechenden Änderungen mit einem eigenen Status und Code zurück statt mit einem generischen Fehlschlag:

HTTP/1.1 503 Service Unavailable
{"code": "setup_applier_unavailable",
 "hint":  "the administrative package 'pepsi-httpd-admin' is not installed …",
 "detail": {"package": "pepsi-httpd-admin", "unit": "pepsi-setup-apply.socket"}}

Es gilt für POST /api/v1/setup/tasks, /setup/preflight, /setup/dns-check, /setup/certificates, PUT /api/v1/setup/answers, POST /api/v1/identities, POST /api/v1/identities/client, ein widerrufendes PATCH /api/v1/identities/{id} und DELETE /api/v1/otp/{address} sowie für die eigenen Formular-Posts der Konsole — POST /ui/setup/{step}, /ui/setup/tasks, /ui/identities/request/{generate,register}, /ui/identities/{id}/revoke und /ui/domains/check, den „jetzt prüfen“-Knopf auf der Domains-Seite. Jeder davon erreicht die Warteschlange über eine einzige Einreihungsfunktion, die zuerst nachfragt, sodass die Ablehnung eine Entscheidung ist statt einer je Oberfläche; das deaktivierte Bedienelement auf der Seite ist Darstellung über dieser Prüfung und nicht ihr Ersatz. Das Vorbereiten von Antworten ist bewusst eingeschlossen: Der einzige Zweck einer Entwurfsantwort ist, angewandt zu werden, und ein Assistent, der sechs Schritte davon auf einem Host anhäuft, auf dem nichts sie anwenden kann, ist stillschweigend wirkungslos. DELETE /api/v1/setup/answers ist nicht gesperrt — Entwürfe zu verwerfen kann keine privilegierte Änderung bewirken, und eine nur lesende Konsole muss ein halbfertiges Interview weiterhin leeren können.

Beachten Sie den eigenen Code. setup_write_unavailable (keine CONFIG_DB-Verbindung) und setup_applier_unavailable haben verschiedene Abhilfen, und der Applier wird zuerst gemeldet, wenn beides zutrifft: Eine Datenbankrolle zu konfigurieren hätte nicht geholfen.

Nicht betroffen sind /api/v1/config und die Konfigurationsseiten der Konsole. Diese schreiben pepsi.config_override, die Datenbank-Überlagerung, die jede Komponente zur Laufzeit liest — keine Datei in /etc. Ihre einzige Sperre ist [pepsi-admin] CONFIG_DB, weil der Applier mit ihnen nichts zu tun hat und die Nur-lesen-Regel darauf auszuweiten eine Fähigkeit entfernte, um die es bei der Paketaufteilung nicht ging. Die beiden Schichten sind unter Konfigurationsschichtung im Handbuch beschrieben.

85.1.3.1.6.2. Wie es das erkennt

Indem es zwei Dateien betrachtet, die zusammen installiert und scharfgestellt beantworten:

  1. pepsi-setup-apply.socket existiert als Unit-Datei in einem Verzeichnis, das systemd durchsucht (/etc/systemd/system, /run/systemd/system, /usr/local/lib/systemd/system, /usr/lib/systemd/system, /lib/systemd/system); und

  2. [pepsi-admin] APPLY_SOCKET existiert, ist ein Socket und ist für dieses Konto beschreibbar — sich mit einem UNIX-Socket zu verbinden braucht Schreibrecht.

Zwei stats und ein access, ohne Privileg — was zählt, denn der Server hat die Privilegien bereits an das unprivilegierte Konto pepsi-httpd abgegeben und kann weder dpkg noch systemd etwas fragen. Er verbindet sich bewusst nicht: Sich zu verbinden ist die Türklingel und würde bei jedem Seitenaufbau einen root-Prozess starten.

Keine der beiden Prüfungen ist überflüssig. Ohne die erste liest sich ein veralteter Socket-Knoten als lebendige Türklingel — und veraltete Knoten kommen vor: systemds RemoveOnStop= ist standardmäßig aus (die ausgelieferte Unit setzt es; eine von Hand bearbeitete vielleicht nicht), und ein Paket, dessen Dateien gelöscht statt gestoppt werden, lässt den Knoten schlicht zurück. Das ist genau der Fall, für den es diese Funktion gibt, und ihn falsch zu behandeln hieße, im Fehlerfall durchzulassen. Ohne die zweite läse sich ein frisch installiertes Paket, dessen Socket nie aktiviert wurde, als bereit, und jede Aufgabe bliebe für immer pending.

Die Prüfung ist fail-closed. Ein Pfad, der nicht untersucht werden kann, ein Pfad, der kein Socket ist, ein Fehler bei den Zugriffsrechten — jede Unsicherheit liest sich als „nicht verfügbar“, und die Konsole wird nur lesend. Der behebbare Fehlschlag ist, aufgefordert zu werden, etwas zu installieren, das bereits installiert ist; der unbehebbare ist zu glauben, eine Änderung sei vorgenommen worden.

Zwei Konsequenzen:

  • Ein Standort, der die Warteschlange anders leert — pepsi-setup apply --once aus cron, eine handgeschriebene Unit, ein Host ohne systemd, Unit-Dateien irgendwo, wo systemd nicht sucht —, hat keine Türklingel (oder keine Unit-Datei) und liest sich als nicht verfügbar. [pepsi-admin] APPLIER = yes setzt die Probe außer Kraft. Es ist ein Versprechen, das der Operator gibt; nichts hier kann es prüfen.

  • Umgekehrt: Der Socket kann vorhanden sein, während die Service-Unit maskiert oder kaputt ist, in welchem Fall eine Aufgabe eingereiht wird und pending bleibt. Die Aufgabenseite zeigt diesen Status, statt etwas anderes vorzugeben. Beide Units werden in einem Paket ausgeliefert, sodass dies ein von Hand hergestellter Zustand ist und kein Paketierungsergebnis.

[pepsi-admin] APPLIER = no macht die Konsole allein durch Konfiguration nur lesend, für eine Installation, die ihre Paketmenge nicht ändern kann.

85.1.3.1.7. Konfiguration

Folgendes ist in pepsi.conf(5) dokumentiert: die Optionen des Abschnitts [pepsi-httpd] (MAX_CONNECTIONS, MAX_CONNECTIONS_PER_IP, DB_POOL_SIZE, das Paar ADDIN / ADDIN_URL, das das Outlook-Add-in aktiviert, und das optionale RESUME_AUTHORIZATION_TOKEN, das POST /resume schützt), die Socket- und SNI-Zertifikatsabschnitte [pepsi-httpd-listener-<name>] und [pepsi-httpd-cert-<name>] (einschließlich der Listener-Flags ADMIN, LIST_API, LIST_API_TESTING und LISTS), der Abschnitt [pepsi-admin], der die administrative API konfiguriert, der Abschnitt [pepsi-list], der die Zugangsdaten API_USER/API_PASS der Mailman-API und API_RATE_LIMIT sowie WEB_RATE_LIMIT/WEB_SEARCH_RATE_LIMIT der Weboberfläche enthält, der Abschnitt [pepsi-autoconfig], aus dem das Autokonfigurationsdokument gebaut wird, und der Abschnitt [pepsi-secure-link], der das Portal aktiviert.

85.1.3.1.8. Privilegienabgabe

Um privilegierte Ports (80/443) zu binden, wird pepsi-httpd oft als root gestartet. Es bedient nicht als root: Sobald jeder konfigurierte Listener gebunden ist (und etwaiges TLS-Schlüsselmaterial gelesen wurde), gibt der Prozess die Privilegien an das unprivilegierte Dienstkonto pepsi-httpd 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-httpd) im Rahmen der Installation an.

Wenn die Abgabe beim Lauf als root nicht abgeschlossen werden kann — meist, weil das pepsi-httpd-Konto nicht existiert — schreibt der Server einen Fehler ins Journal 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.3.1.9. TLS-Schlüsselmaterial

Das Zertifikat und der private Schlüssel jedes TLS-Listeners — sein eigenes TLS_CERT/TLS_KEY sowie jedes [pepsi-httpd-cert-*]-SNI-Zertifikat — werden einmalig beim Start gelesen, vor der oben beschriebenen Privilegienabgabe. Woher sie gelesen werden, hängt davon ab, wie der Server gestartet wurde.

Unter systemd (die paketierte Installation). Die Unit läuft von Anfang an unter dem unprivilegierten Benutzer pepsi-httpd — sie besitzt nie root — und kann daher certbots nur für root lesbares /etc/letsencrypt/{live,archive} nicht öffnen. Sie muss es auch nicht: pepsi-setup(1) schreibt ein Drop-in /etc/systemd/system/pepsi-httpd.service.d/10-tls-credentials.conf mit einem LoadCredential=-Eintrag pro Datei. systemd öffnet diese Dateien als root, wenn es die Unit startet, und übergibt dem Dienst private Kopien unter $CREDENTIALS_DIRECTORY — einem Unit-eigenen tmpfs-Verzeichnis, das nur dieser eine Dienst lesen kann und das für jede andere Unit unsichtbar ist —, wo pepsi-httpd jeden konfigurierten Pfad nachschlägt, bevor es auf das Lesen des Pfades selbst zurückfällt. Die Konfiguration bleibt davon unberührt: TLS_CERT/TLS_KEY benennen weiterhin die echten Dateien, und aus diesen Pfaden werden die Credential-Namen abgeleitet.

Zwei Konsequenzen:

  • Führen Sie pepsi-setup run erneut aus, nachdem Sie ein Zertifikat hinzugefügt, verschoben oder entfernt haben. Das Drop-in wird aus der Konfiguration neu erzeugt und führt bewusst nur Dateien auf, die existieren, denn systemd weigert sich, eine Unit zu starten, deren Credential-Quelle fehlt.

  • Ein Credential wird beim Start der Unit bereitgestellt, ein erneuertes Zertifikat erreicht die Clients also erst nach einem Neustart. Der von pepsi-setup installierte certbot-Deploy-Hook führt ihn durch (systemctl try-restart).

Direkt gestartet, ohne systemd. Der Server wird als root gestartet, liest die Dateien selbst und gibt erst danach die Privilegien ab; es ist also kein Credential beteiligt, und die Dateirechte spielen nie eine Rolle.

Ein Zertifikat, das nicht geladen werden kann, wird gemeldet und übersprungen, nicht als fatal behandelt: Die SNI-Hostnamen, die es bedient hätte, sind dann nicht mehr über HTTPS erreichbar, während jedes andere Zertifikat weiter funktioniert. Nur ein TLS-Listener, dem kein einziges brauchbares Zertifikat bleibt, beendet den Server. Eine einzige unlesbare Datei kann somit nicht die MTA-STS-Richtlinie unbeteiligter Domains — oder den Metriken-Endpunkt — mit sich reißen.

85.1.3.1.10. Befehle

serve

Führt den Server aus, bis er unterbrochen wird. Erfordert, dass das Schema mit pepsi-setup(1) installiert wurde.

prune

Löscht Audit-Einträge, die älter als [pepsi-admin] EVENT_RETENTION_DAYS sind, Mail-Log-Einträge, die älter als MAIL_LOG_RETENTION_DAYS sind, sowie abgelaufene administrative Sitzungen und beendet sich dann. Als root ausgeführt, wechselt es zuerst zum Konto pepsi-httpd, dessen Datenbankrolle die DELETE-Berechtigung besitzt. pepsi-log-prune.timer führt es täglich aus; auf einem System ohne systemd führen Sie es über cron aus.

85.1.3.1.11. 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.3.1.12. Signale

SIGINT

Leitet das Herunterfahren ein und beendet sich sauber.

SIGTERM

Nicht abgefangen: Die Standardbehandlung beendet den Prozess sofort und schneidet jede laufende Anfrage ab. Dadurch geht nichts verloren — die Warteschlange liegt in PostgreSQL, und dieser Server hält keinen eigenen Zustand —, und so beendet systemctl stop die Unit.

85.1.3.1.13. Exit-Status

0

Sauberes Herunterfahren.

1

Ein Fehler ist aufgetreten (zum Beispiel eine fehlerhafte Konfigurationsdatei, ein nicht lesbares Zertifikat oder eine fehlgeschlagene Datenbankverbindung). Der Grund wird in das Journal geschrieben.

85.1.3.1.14. Beispiele

Über HTTPS bedienen:

pepsi-httpd -c /etc/pepsi/pepsi.conf serve

Eine Richtlinie zum Testen abrufen (Auflösen des SNI-Hosts auf den Server):

curl --resolve mta-sts.example.org:443:203.0.113.7 \
     https://mta-sts.example.org/.well-known/mta-sts.txt

Prüfen, ob der Schlüssel eines Benutzers veröffentlicht ist, so wie es der Client eines Korrespondenten täte:

gpg --locate-external-key alice@example.org

85.1.3.1.15. Siehe auch

pepsi-config(1), pepsi.conf(5), pepsi-dispatch(1), pepsi-keys(1), pepsi-setup(1)

85.1.3.1.16. Fehler

Melden Sie Fehler an den Pepsi-Issue-Tracker.