30. pepsi-httpd

Der HTTP/HTTPS-Server: MTA-STS-Richtlinie, das Web Key Directory, Prometheus-Metriken, das Secure-Link-Portal, die administrative API und Konsole sowie die Mailinglisten-Seiten.

30.1. Rolle

pepsi-httpd beantwortet HTTP-Anfragen für das Pepsi-System. Unter anderem bedient es die MTA-STS-Richtliniendatei, das Web Key Directory, das die OpenPGP-Schlüssel unserer Benutzer veröffentlicht, und eine Prometheus-/metrics-Seite; die vollständige Liste folgt. Es ist um eine generische Dispatch-Tabelle herum gebaut, sodass weitere Endpunkte hinzugefügt werden können, ohne den Server-Kern anzufassen. Führen Sie es mit pepsi-httpd serve aus.

HTTP/1.1 (RFC 9112) mit der Semantik von RFC 9110, über hyper; TLS ist RFC 8446 über rustls. Was es bedient, ist anderswo definiert, und jeder Endpunkt hat sein eigenes maßgebliches Dokument:

  • /.well-known/mta-sts.txt — die Richtliniendatei von RFC 8461, deren Inhalt die Relay-Stages auf dem Weg hinaus abrufen und beachten.

  • /.well-known/openpgpkey/… — das OpenPGP Web Key Directory (draft-koch-openpgp-webkey-service, ein Internet-Draft und kein RFC), in beiden Layouts, dem direkten und dem erweiterten.

  • /mail/config-v1.1.xml und /.well-known/autoconfig/mail/config-v1.1.xml — die Autokonfiguration von Mailkonten (draft-ietf-mailmaint-autoconfig), unten beschrieben.

  • /secure/… — das Secure-Link-Portal (Das Secure-Link-Ausweichportal), dessen Sitzungs-Cookie RFC 6265 folgt.

  • /api/v1/… und /ui — die administrative API und Konsole (Die administrative API, Die Verwaltungskonsole), nur auf Listenern mit dem Flag ADMIN = yes bedient, authentifiziert mit dem Bearer-Schema von RFC 6750 oder einem Sitzungs-Cookie.

  • POST /resume — der Webhook des GNU-Taler-Händlers: Eine Authorization: Bearer-Anfrage mit {"message_id": "<token>"} setzt den passenden paused-Datensatz zurück auf pending und weckt den Dispatcher, sodass pepsi-stage-anti-spam in dem Augenblick erneut läuft, in dem eine Bestellung bezahlt wird. Der Endpunkt ist abgeschaltet (404), sofern [pepsi-httpd] RESUME_AUTHORIZATION_TOKEN nicht gesetzt ist.

  • /addin/manifest.xml und /addin/taskpane.html — das Outlook-Add-in, das mit 404 antwortet, sofern [pepsi-httpd] ADDIN nicht eingeschaltet ist.

  • /3.0/… und /3.1/… — die REST-API von GNU Mailman 3 (Die REST-API von GNU Mailman 3), nur auf Listenern mit dem Flag LIST_API = yes bedient und mit dem einzigen Paar [pepsi-list] API_USER/API_PASS authentifiziert.

  • /lists/…, /archives/list/… und /robots.txt — die öffentlichen Mailinglisten-Seiten, die Konten der Mitglieder und Listeneigentümer sowie das Archiv (Die Verwaltungskonsole, Archive), nur auf Listenern mit dem Flag LISTS = yes bedient.

30.2. Funktionen

  • Mehrere Listener. Jeder Abschnitt [pepsi-httpd-listener-<name>] bindet einen Socket genau wie die Ingress-Listener: SERVE = tcp (BIND_TO/PORT, Standardport 443), unix (UNIXPATH) oder systemd (Socket-Aktivierung), mit MODE = plain oder tls.

  • SNI-Zertifikatsauswahl. Ein TLS-Listener wählt sein Zertifikat je Verbindung anhand des SNI-Hostnamens des Clients: Jeder Abschnitt [pepsi-httpd-cert-<name>] führt einen oder mehrere SNI-Hostnamen und ein Paar TLS_CERT/TLS_KEY auf. Das eigene TLS_CERT/TLS_KEY eines Listeners ist der Rückfall für Verbindungen ohne SNI-Treffer. (MTA-STS erfordert ein für mta-sts.<domain> gültiges Zertifikat.)

  • Schlüsselmaterial über systemd-Credentials. Der Server läuft unprivilegiert und kann certbots nur für root lesbares /etc/letsencrypt nicht lesen; unter systemd muss er das auch nicht. pepsi-setup schreibt ein LoadCredential=-Drop-in, systemd öffnet jedes Zertifikat und jeden Schlüssel als root, wenn es die Unit startet, und der Server liest die ihm unter $CREDENTIALS_DIRECTORY übergebene private Kopie. Ein Zertifikat, das sich dennoch nicht laden lässt, wird mit einer Fehlermeldung übersprungen, statt den ganzen Server stillzulegen — fatal ist nur ein Listener, dem kein brauchbares Zertifikat bleibt.

  • Generisches Routing. Anfragen werden nach Methode und URL-Form abgeglichen: Der Pfad einer Route ist eine Liste von Segmenten, jedes davon ein Literal, eine benannte Erfassung eines Segments oder eine Erfassung des Rests. Routen werden in Registrierungsreihenfolge probiert, der erste Treffer gewinnt, sodass GET /.well-known/mta-sts.txt, GET /.well-known/openpgpkey/$DOMAIN/hu/$HASH und ein künftiges GET /foo/$ID je ein Tabelleneintrag sind.

  • MTA-STS-Richtlinie. GET /.well-known/mta-sts.txt gibt die RFC-8461-Richtlinie zurück (gebaut aus [pepsi] MTA_STS_* und dem Ingress-HOSTNAME), wenn der Host der Anfrage mta-sts.<domain> für eine Domain in ACCEPTED_DOMAINS ist; jeder andere Host ergibt 404.

  • Web Key Directory. GET /.well-known/openpgpkey/... veröffentlicht die OpenPGP-Schlüssel unserer eigenen Benutzer, in der direkten wie in der erweiterten Form, beantwortet aus dem Schlüsselspeicher über eine indizierte Abfrage (domain, local-part hash). Die Antwort ist der binäre Schlüssel mit Access-Control-Allow-Origin: *; die Richtliniendatei ist ein 200 der Länge null (ihr Fehlen lässt GnuPG aufgeben, bevor es nach einem Schlüssel fragt); ein unbekannter Hash ist ein 404 mit leerem Body. Es bedient nur Identitäten, die diese Installation hält und als published markiert hat — nie einen zwischengespeicherten Korrespondentenschlüssel — und wendet bewusst keine Ratenbegrenzung an, weil das Protokoll konstruktionsbedingt ein öffentliches Orakel ist. Siehe Schlüsselverwaltung.

  • Prometheus-Metriken. GET /metrics stellt Live-Anzeigen je Stage (aktive und pausierte Nachrichten, direkt aus der Warteschlange gelesen) und von pepsi-dispatch geschriebene Zähler bereit (Timeouts je Stage, Abstürze, Nachrichtenzahl und Gesamtverarbeitungszeit für Durchschnitte sowie die globalen Stage-/Nachrichten-Gesamtwerte). Diese Zahlen — und die Stage-Namen, die sie beschriften — beschreiben, wie viel Mail die Installation trägt und wie ihre Pipeline gebaut ist, weshalb der Endpunkt administrativ ist: Er wird nur auf einem Listener mit ADMIN = yes bedient und antwortet anderswo mit dem gewöhnlichen 404. Er verlangt kein eigenes Credential, denn ein Scraper hat keines vorzuweisen; das Flag des Listeners ist die Zugangskontrolle. Den Server privat zu binden ist keine Alternative: Derselbe Prozess muss mta-sts.<domain> und openpgpkey.<domain> aus dem öffentlichen Internet beantworten.

  • Die administrative Oberfläche. Ein Listener mit dem Flag ADMIN = yes bedient zusätzlich die administrative API /api/v1 (Die administrative API), die administrative Konsole /ui (Die Verwaltungskonsole) und /metrics. Auf jedem anderen Listener antworten diese Pfade mit dem gewöhnlichen 404. Die Konsole ist ein Client der API — dieselbe Authentifizierung, dieselben Geltungsbereiche, dasselbe Prüfprotokoll — und übt keine Dienststeuerung aus.

30.3. Autokonfiguration von Mail-Clients

Ein Mailkonto von Hand einzurichten bedeutet, acht Fragen zu beantworten — zwei Hostnamen, zwei Ports, zwei Begriffe von Transportsicherheit, zwei Authentifizierungsmethoden —, für deren Beantwortung ausgerechnet der Kontoinhaber am schlechtesten gerüstet ist. draft-ietf-mailmaint-autoconfig behebt das, indem der Anbieter die Antworten veröffentlicht und der Client sie allein anhand der Adresse abholt.

Pepsi bedient die beiden Sprossen dieser Kette, die ein Anbieter veröffentlichen soll. Der Host entscheidet, welche gefragt ist:

https://autoconfig.example.org/mail/config-v1.1.xml             (required)
https://example.org/.well-known/autoconfig/mail/config-v1.1.xml (optional)

Beide liefern dasselbe text/xml-Dokument, und beide sind öffentlich: Der Entwurf verlangt es, und der Grund ist kein Versehen, sondern ein Reihenfolgeproblem — ein Client muss erfahren, welchen Authentifizierungsmechanismus er verwenden soll, bevor er sich authentifizieren kann. Das Dokument enthält daher kein Geheimnis, und Pepsi hält es so, indem es %EMAILADDRESS% nennt, den Platzhalter, den der Client ersetzt, statt irgendeiner echten Adresse. Der optionale Parameter ?emailaddress= des Entwurfs wird angenommen und ignoriert, was zugleich bedeutet, dass nie anfragegesteuerter Text in ein Dokument interpoliert wird, das aus dem eigenen Origin der Mail-Domain ausgeliefert wird.

<?xml version="1.0" encoding="UTF-8"?>
<clientConfig version="1.1">
  <emailProvider id="example.org">
    <domain>example.org</domain>
    <displayName>Example Mail</displayName>
    <displayShortName>Example</displayShortName>
    <incomingServer type="imap">
      <hostname>mail.example.org</hostname>
      <port>993</port>
      <socketType>SSL</socketType>
      <authentication>password-cleartext</authentication>
      <username>%EMAILADDRESS%</username>
    </incomingServer>
    <outgoingServer type="smtp">
      <hostname>mail.example.org</hostname>
      <port>587</port>
      <socketType>STARTTLS</socketType>
      <authentication>password-cleartext</authentication>
      <username>%EMAILADDRESS%</username>
    </outgoingServer>
  </emailProvider>
</clientConfig>

Die ausgehende Hälfte wird abgeleitet, nicht konfiguriert. Ihr Port, ihre Transportsicherheit und ihre Authentifizierung werden von dem [pepsi-ingress-listener-*] mit dem Flag SUBMISSION = yes abgelesen, sodass eine Installation, die die Submission von STARTTLS auf 587 zu implizitem TLS auf 465 verlegt, ein korrigiertes Dokument erhält, ohne es anzufassen — die häufigste Art, wie eine veröffentlichte Konfiguration falsch wird, ist, dass sie nicht mehr zum Server passt, und hier gibt es nichts, was man zu aktualisieren vergessen könnte. SMTP_HOST überschreibt die Ableitung für eine Installation, deren öffentlicher Endpunkt ein Lastverteiler statt des Listeners ist.

Die eingehende Hälfte lässt sich nicht ableiten, denn Pepsi bedient keine Postfächer; es übergibt sie an einen MDA (pepsi-stage-relay-to-lmtp). Also muss IMAP_HOST oder POP3_HOST benennen, womit auch immer die Installation gepaart ist, und solange keines von beiden das tut, antworten beide Endpunkte mit 404. Das ist Absicht: Ein Client, der ein Konfigurationsdokument findet, bricht den Durchlauf der Rückfallkette ab, sodass ein unvollständiges den Benutzer schlechter stellt als gar keines.

Die Veröffentlichung braucht zwei Dinge außerhalb der Konfigurationsdatei: einen autoconfig.<domain>-DNS-Eintrag, der hierher zeigt, und ein Zertifikat, das diesen Namen abdeckt — Clients versuchen zuerst die https://autoconfig.…-URL, und ein Zertifikatsfehler dort ist schlicht eine fehlgeschlagene Abfrage. Eine Installation, die den Namen nicht hinzufügen will, kann sich auf die /.well-known/-Form verlassen, um den Preis, nur von den Clients gefunden zu werden, die sie versuchen.

30.4. Konfiguration

[pepsi-httpd]: MAX_CONNECTIONS (Standardwert 256), DB_POOL_SIZE, RESUME_AUTHORIZATION_TOKEN, ADDIN (Standardwert no) und ADDIN_URL (das ein https://-Origin sein muss — einfaches http:// wird beim Start abgelehnt, es sei denn, der Host ist Loopback, sodass ein Test kein Zertifikat braucht). Die administrative Oberfläche liest [pepsi-admin] und das Portal [pepsi-secure-link]. Listener und Zertifikate liegen in Abschnitten [pepsi-httpd-listener-<name>] und [pepsi-httpd-cert-<name>]. Die bedienten Domains, der MTA-STS-mx-Host und die Richtlinie selbst stammen aus den gemeinsamen Abschnitten [pepsi-ingress] und [pepsi]. Siehe Konfiguration.

Das Web Key Directory braucht keine eigene Konfiguration — es folgt dem published-Flag jeder Identität —, aber die erweiterte Methode braucht je bedienter Domain einen DNS-Eintrag openpgpkey.<domain>, und das Zertifikat des HTTPS-Listeners muss diesen Namen abdecken. pepsi-setup run bittet certbot darum und meldet jeden, der nicht auflöst.

[pepsi-autoconfig] konfiguriert das Autokonfigurationsdokument: IMAP_HOST / POP3_HOST (eines von beiden schaltet die Funktion ein) mit ihren _PORT, _SOCKET und _AUTH; die optionalen SMTP_*-Überschreibungen; sowie DISPLAY_NAME, DISPLAY_SHORT_NAME und DOCUMENTATION_URL. Siehe pepsi.conf(5).

30.5. Siehe auch

Architektur, Schlüsselverwaltung, pepsi-dispatch, pepsi-keys, pepsi-setup, pepsi.conf(5).