6. Konfiguration

Alle Pepsi-Komponenten lesen eine Konfigurationsdatei im INI-Stil (das Format wird mit den GNU-Taler-Werkzeugen geteilt). Die vollständige Optionsreferenz steht in der einzigen Handbuchseite pepsi.conf(5) und in den programmweisen Kapiteln unter Programme.

6.1. Dateiformat

Eine Datei ist eine Folge von [SECTION]-Kopfzeilen, jeweils gefolgt von OPTION = VALUE-Zuweisungen. Abschnitts- und Optionsnamen sind unabhängig von der Groß-/Kleinschreibung (konventionell Großschreibung); # oder % beginnt einen Kommentar; ein Wert kann in doppelte Anführungszeichen gesetzt werden, um umgebende Leerzeichen zu erhalten.

Drei Direktiven setzen mehrere Dateien zusammen — wichtig, um Geheimnisse aus der für alle lesbaren Konfiguration herauszuhalten:

@inline@ FILE

Bindet eine andere Datei ein, relativ zur aktuellen.

@inline-matching@ GLOB

Bindet jede Datei ein, die auf ein Shell-Glob passt.

@inline-secret@ SECTION FILE

Führt einen Abschnitt aus einer modusbeschränkten Datei (typischerweise Zugangsdaten) zusammen. Eine Direktive beendet den Abschnitt, in dem sie steht, schreiben Sie sie also nach dessen letzter Option; eine Option darunter gehörte zu keinem Abschnitt, und die Datei ließe sich nicht laden. Ein Fragment, das nicht gelesen werden kann, ergibt nur eine Warnung, wodurch die von ihm getragenen Optionen ungesetzt aussehen; pepsi-setup run übergibt jedes Fragment an das Dienstkonto, das es lesen soll.

Pfadwertige Optionen durchlaufen eine $-Expansion aus dem [PATHS]-Abschnitt oder der Umgebung ($VAR, ${VAR}, ${VAR:-default}). Werttypen sind boolean (YES/NO), Zahl, Dauer (nur Einheiten h/m/s — siehe den Vorbehalt unten), Pfad und Unix-Modus (oktal).

Warnung

duration-Werte werden von jiff::SignedDuration geparst, das nur Stunden/Minuten/Sekunden und kleinere Einheiten akzeptiert. Eine Kalendereinheit wie 5 d oder 2 weeks lässt sich nicht parsen. Für Zeitfenster von einem Tag oder länger schreiben Sie das Äquivalent in Stunden (120 h) oder verwenden, wo vorhanden, eine ganzzahlige Option (z. B. [pepsi-srs] MAX_AGE_DAYS).

6.1.1. Mitgelieferte Standardwerte: config.d

Unterhalb der Datei des Operators gibt es noch eine Schicht. Jedes *.conf in ${DATADIR}/config.d (/usr/share/pepsi/config.d bei einer Paketinstallation) wird vor pepsi.conf geparst, sodass diese Dateien Standardwerte liefern, die pepsi.conf Option für Option überschreibt. Sie gehören zum Paket: Definieren Sie in pepsi.conf neu, was Sie wollen, statt sie zu bearbeiten, sonst verwirft ein Upgrade die Änderung.

Zwei werden mitgeliefert, und sie sind die zwei Formen, für die diese Schicht gedacht ist.

vacation.conf

Konfiguration, die als Wortlaut statt als Wert ausgeliefert wird — die [pepsi-vacation-default-message]-Notizen in zehn Sprachen, auf die pepsi-stage-vacation zurückfällt. EN in pepsi.conf neu zu definieren lässt die anderen neun bestehen.

thunderbird.conf

Ein Standardwert, den ein Operator in einem Schritt rückgängig machen können muss: [pepsi] CRYPTO_ALLOW_DOWNGRADE = yes, sodass S/MIME als AES-256-CBC-EnvelopedData verschlüsselt wird. Thunderbird kann das AES-256-GCM-AuthEnvelopedData, das Pepsis Code-Standardwert erzeugt, nicht lesen und scheitert dabei stillschweigend (siehe Interoperabilität mit Clients). Der Code-Standardwert bleibt AuthEnvelopedData, sodass das Löschen der Datei authentifizierte Verschlüsselung sofort wiederherstellt — weshalb dies in config.d liegt und nicht im Quelltext.

6.2. Die gemeinsamen Abschnitte

Diese Abschnitte werden von mehr als einer Komponente gelesen, daher stehen sie einmal in der gemeinsamen Datei:

[pepsi]

Übergreifende Identität und Richtlinie: KEY_DIR, DKIM_SELECTOR, DKIM_ALGORITHMS, ARC_DOMAIN, ARC_ALGORITHM, ORIGINATE_SUCCESS_DSN, LOG_JSON (strukturiertes Logging als JSON-Zeilen, standardmäßig aus), LOG (die pipelineweite Standard-Logstufe) und die MTA-STS-Veröffentlichungsoptionen. Siehe pepsi.conf(5).

[pepsi-postgres]

Datenbankverbindung (CONFIG-Verbindungszeichenfolge; SQL_DIR für pepsi-setup). Jede Komponente verbindet sich mit derselben Datenbank und dem einen pepsi-Schema.

[pepsi-srs]

Die Parameter der SRS-Engine (SRS_DOMAIN, SECRET/SECRET_FILE, MAX_AGE_DAYS), die von pepsi-stage-srs und der Rückwärtsdekodierung von pepsi-ingress geteilt werden. Siehe pepsi-stage-srs.

[pepsi-dispatch]

Dispatcher-Optionen (MAX_RUNTIME, WORKER_IDLE_TIMEOUT, POLL_INTERVAL, STATS_INTERVAL, CONFIG_FILE, DB_POOL_SIZE). Die Dimensionierung des Worker-Pools pro Stage (PARALLELISM, MAX_MESSAGES, QUEUE_LIMIT) steht in jedem [stage-<name>]-Abschnitt. Siehe pepsi-dispatch.

[pepsi-httpd] / [pepsi-httpd-listener-<name>] / [pepsi-httpd-cert-<name>]

Der HTTP/HTTPS-Server: eine MAX_CONNECTIONS-Obergrenze, ein Listener-Abschnitt pro Socket (SERVE genau wie bei den Ingress-Listenern; MODE = plain (der Standard) oder tls; Standardport 443) und ein Zertifikatsabschnitt pro SNI-Hostnamen (SNI, TLS_CERT, TLS_KEY). Die bedienten Domains und die MTA-STS-Richtlinie werden aus den Abschnitten [pepsi-ingress] und [pepsi] entnommen. Siehe pepsi-httpd.

[pepsi-stage-relay-to-smarthost-mta-<name>]

Je ein vorgelagerter Smarthost, gemeinsam genutzt von jeder Smarthost-Relay-Stage. Siehe pepsi-stage-relay-to-smarthost.

6.3. Ingress und Listener

[pepsi-ingress] enthält die Richtlinie zur Nachrichtenannahme (HOSTNAME, ACCEPTED_DOMAINS, MAX_MESSAGE_SIZE, MAX_CONNECTIONS, DMARC_ENFORCE, DNS-Einstellungen). Jeder [pepsi-ingress-listener-<name>]-Abschnitt bindet einen Socket:

  • SERVE = tcp (mit BIND_TO/PORT), unix (mit UNIXPATH) oder systemd (Socket-Aktivierung, FD_INDEX).

  • MODE = plain | starttls | tls wählt die Transportsicherheit; starttls/tls erfordern TLS_CERT und TLS_KEY.

Deklarieren Sie so viele Listener wie nötig — z. B. einen MX-Listener auf 25 mit opportunistischem STARTTLS und einen Submission-Listener auf 465 mit implizitem TLS.

6.4. Die Stage-Pipeline

Die Pipeline wird vollständig aus [stage-<name>]-Abschnitten verdrahtet. Der Stage-Name ist ein vom Operator gewähltes Etikett; das PROGRAM des Abschnitts wählt das Binärprogramm, und NEXT_STAGE/BOUNCE_STAGE verbinden den Graphen:

PROGRAM

(erforderlich) Das Stage-Binärprogramm (auf PATH gefunden, sofern nicht absolut). Ein Binärprogramm kann mehrere Stages bedienen — es liest seine Optionen aus dem Abschnitt, an dem sich die Nachricht gerade befindet.

NEXT_STAGE

(optional) Wohin eine Nachricht bei Erfolg weiterschaltet. Eine terminale Stage ohne NEXT_STAGE entfernt die zugestellte Nachricht.

BOUNCE_STAGE

(optional) Wohin eine dauerhaft fehlgeschlagene (oder, mit ORIGINATE_SUCCESS_DSN, erfolgreich zugestellte) Nachricht geroutet wird, um eine DSN zu erzeugen — üblicherweise eine pepsi-stage-bounce-Stage.

PARALLELISM / MAX_MESSAGES / QUEUE_LIMIT

(optional) Die Worker-Pool-Dimensionierung, die der Dispatcher auf diese Stage anwendet: die Obergrenze gleichzeitiger Worker-Prozesse (PARALLELISM, Standardwert 4, bei Bedarf gestartet und im Leerlauf abgeräumt), die Anzahl der Nachrichten, die ein Worker verarbeitet, bevor er erneuert wird (MAX_MESSAGES, Standardwert 1000), und wie viele Nachrichten gleichzeitig an einen Worker pipelinet werden (QUEUE_LIMIT, Standardwert 4, sodass die in Bearbeitung befindliche Kapazität der Stage QUEUE_LIMIT × PARALLELISM beträgt, während die Prozess- und Verbindungszahl durch PARALLELISM bestimmt bleibt). Siehe pepsi-dispatch.

FUSION

(optional) Ob ein Vorgänger diese Stage in seinem eigenen Worker-Prozess ausführen darf, anstatt den Datensatz an den Dispatcher zurückzugeben. Standard ist YES, wenn PROGRAM eine der schnellen, körperlosen Stages ist (pepsi-stage- + if, discard, auto-whitelist, block-language, check-whitelist, srs, list oder edit-settings), sonst NO; [pepsi] ALLOW_FUSION = NO schaltet die Fusion global ab.

Neue Nachrichten treten immer bei [stage-init] ein, das existieren muss. Jedes Programm liest seine eigenen Optionen aus seinem Abschnitt; diese sind je Programm unter Programme dokumentiert.

6.4.1. Reihenfolgebedingungen

Den Pipeline-Graphen zeichnen Sie selbst, aber fünf Reihenfolgen sind keine freie Wahl, und jede davon falsch zu setzen erzeugt Mail, die richtig aussieht. pepsi-setup warnt vor dreien davon: einer DKIM-signierenden Stage, die zu einer Encrypt-Stage führt, einer Decrypt-Stage, die zu einer ARC-Stage führt, und einer Decrypt-Stage ohne nachgelagerte lokale Zustellung.

Die Verschlüsselung kommt vor der DKIM-Signierung. Die empfohlene ausgehende Kette ist:

submission → … → encrypt → srs → dkim-sign → relay

pepsi-stage-encrypt schreibt den Nachrichtentext um, und DKIM muss die tatsächlich übertragenen Bytes signieren. Zuerst zu signieren ergibt eine Nachricht, deren DKIM-Signatur beim Empfänger nicht verifiziert — was schlimmer ist als gar keine Signatur, denn eine kaputte Signatur ist für einen empfangenden MTA ein stärkeres negatives Signal als eine fehlende.

Alles, was den Nachrichtentext liest, kommt vor der Verschlüsselung. Die Pay-to-Send-Schranke (pepsi-stage-anti-spam) und die Spracherkennung (pepsi-stage-detect-language) wollen beide Klartext, und beide bekommen ihn, weil die Verschlüsselung auf dem ausgehenden Pfad zuletzt kommt — was der Grund für die obige Reihenfolge ist.

SRS ist für weitergeleitete Mail da und auf dem ausgehenden Relay-Ende harmlos. Es schreibt einen Absender in der Domain eines anderen um — Mail, die dieser Host weiterleitet —, damit SPF beim nächsten Hop besteht. Der Null-Absender und ein Absender, der bereits in der SRS-Domain liegt, bleiben unangetastet; mit SRS_DOMAIN auf der primären Domain (dem Standard des Assistenten) läuft die Mail unserer eigenen Benutzer also unverändert durch, und ein Relay-Ende, das weitergeleitete und eingelieferte Mail gemeinsam nutzen, kann es vor dem Signieren ausführen, in der Reihenfolge, die pepsi-setup prüft: encrypt → srs → dkim-sign → relay. Ein Benutzer einer weiteren bedienten Domain wird ebenfalls umgeschrieben; das kostet die SPF-Ausrichtung für seine Domain, aber DMARC besteht dennoch über die ausgerichtete DKIM-Signatur, die dkim-sign danach hinzufügt.

ARC kommt vor der Entschlüsselung. Die empfohlene eingehende Kette ist:

init = arc → decrypt → aliases → (anti-spam / language / …) → local delivery

Ein ARC-Set versiegelt die Nachricht so, wie sie ankam. pepsi-stage-decrypt schreibt den Nachrichtentext um, sodass zuerst zu entschlüsseln das versiegelte AMS Bytes beschreiben ließe, die sonst niemand je gesehen hat — eine Aussage über eine Nachricht, die es nie gab.

Die Entschlüsselung braucht nachgelagert lokale Zustellung. Entschlüsseln entwertet die DKIM-Signatur des Absenders, was bei einer Nachricht, die gleich in ein Postfach auf diesem Host abgelegt wird, harmlos ist und bei einer weitergeleiteten nicht. Die Stage läuft daher nur für Empfänger, die dieser Host bedient, und teilt eine Nachricht mit Empfängern beider Art; pepsi-setup warnt, wenn von einer Decrypt-Stage aus kein pepsi-stage-relay-to-maildir oder pepsi-stage-relay-to-lmtp erreichbar ist, denn eine solche Stage ist entweder sinnlos oder schädlich.

Das Spiegelbild der ausgehenden Regel gilt ebenso: Alles, was den Nachrichtentext liest, kommt nach der Entschlüsselung, weshalb die Inhalts-Stages dort stehen, wo sie stehen. Beachten Sie, dass die Decrypt-Stage für jede Nachricht, die durch sie hindurchgeht, den gesamten Nachrichtentext lädt (ihr Load liegt je Stage fest, und ob eine Nachricht geschützt ist, lässt sich den Umschlagspalten nicht ansehen), sodass die Platzierung ebenso eine Leistungs- wie eine Korrektheitsfrage ist.

6.5. Durchgearbeitete Beispiele

Jedes Beispiel unten ist eine vollständige, minimale Pipeline für eine Aufgabe, ohne Sprachklassifizierung, ARC/DKIM-Signierung oder Spam-Filterung — nur die gemeinsamen Abschnitte, ein Listener und die Stages, die die Arbeit erledigen. Sie alle teilen sich dasselbe Grundgerüst:

  • [pepsi] (hier nur KEY_DIR — DKIM/ARC werden in diesen Beispielen nicht verwendet),

  • [pepsi-postgres] für die Datenbank, und

  • [pepsi-ingress] plus ein [pepsi-ingress-listener-*]-Socket.

Die Listener setzen MODE = starttls/tls, aber kein TLS_CERT/TLS_KEY: pepsi-setup füllt die certbot-Pfade automatisch aus (siehe Installation). Setzen Sie sie explizit oder übergeben Sie --no-certbot, um das Zertifikat selbst zu verwalten.

Jede ist eine echte, in sich geschlossene Konfiguration — um ARC, SRS, DKIM-Signierung oder Spam-Filterung darüberzulegen, fügen Sie die entsprechenden Stages zwischen init und die Zustell-Stage ein, wie es die Beispiel-pepsi.conf zeigt.

6.5.1. Minimal eingehend (Relay zu einem Smarthost)

Ein Edge-MX, der Mail für unsere Domains annimmt und jede Nachricht an einen authentifizierten vorgelagerten Smarthost übergibt. init ist die Relay-Stage; der Smarthost authentifiziert uns, sodass hier keine SRS-Umschreibung nötig ist:

[pepsi]
KEY_DIR = /var/pepsi/keys

[pepsi-postgres]
CONFIG = postgres:///pepsi

[pepsi-ingress]
HOSTNAME = mail.example.org
ACCEPTED_DOMAINS = example.org

[pepsi-ingress-listener-mx]
SERVE = tcp
BIND_TO = 0.0.0.0
PORT = 25
MODE = starttls

# init: relay every accepted message to the upstream smarthost. A successful
# delivery is terminal (no NEXT_STAGE).
[stage-init]
PROGRAM = pepsi-stage-relay-to-smarthost
SERVER_NAME = mail.example.org

# The upstream smarthost (one [...-mta-*] section per route; CATCH_ALL takes
# everything not matched by a DOMAINS list).
[pepsi-stage-relay-to-smarthost-mta-upstream]
HOST = smtp.relay.example.net
PORT = 587
MODE = starttls
AUTH = plain
USERNAME = relayuser
PASSWORD = changeme
CATCH_ALL = yes

6.5.2. Minimal ausgehend (Relay direkt ins Internet)

Ein Submission-/Relay-Host, der die Mail unserer eigenen Benutzer annimmt und sie direkt an den MX jedes Empfängers zustellt. Der Listener vertraut einem lokalen Netzwerk (MYNETWORKS), sodass diese Submissions als lokal erzeugt markiert werden und an jede Domain weiterleiten dürfen; ausgehende Mail behält ihren eigenen Umschlagabsender, daher gibt es keine SRS-Stage:

[pepsi]
KEY_DIR = /var/pepsi/keys

[pepsi-postgres]
CONFIG = postgres:///pepsi

[pepsi-ingress]
HOSTNAME = mail.example.org
ACCEPTED_DOMAINS = example.org

[pepsi-ingress-listener-submission]
SERVE = tcp
BIND_TO = 0.0.0.0
PORT = 587
MODE = starttls
# Trust submissions from this network without authentication (use SASL_TYPE /
# TLS_AUTH_CLIENT instead for roaming users).
MYNETWORKS = 10.0.0.0/8

# init: deliver directly to each recipient's mail exchanger. Terminal.
[stage-init]
PROGRAM = pepsi-stage-relay-to-internet
SERVER_NAME = mail.example.org
# Read by pepsi-setup to build the SPF record listing our sending hosts, and
# to check each address's PTR against SERVER_NAME above. PUBLIC_IP belongs in
# the relay *stage's* section: pepsi-setup finds it by walking [stage-*] and
# keeping the sections whose PROGRAM is a relay, never by the program's own
# section name.
PUBLIC_IP = 203.0.113.7 2001:db8::25

6.5.3. Minimale lokale Zustellung (Relay zu Maildir)

Ein MX, der Mail für unsere Domain in das Maildir/new/ lokaler Benutzer zustellt. Jeder Empfänger ist in ACCEPTED_DOMAINS (sodass Ingress sie annimmt) und löst auf ein lokales Konto auf, daher ist kein NEXT_STAGE nötig — fügen Sie eines hinzu (eine Smarthost- oder Bounce-Stage), falls ein Empfänger in unserer Domain kein lokales Postfach haben könnte:

[pepsi]
KEY_DIR = /var/pepsi/keys

[pepsi-postgres]
CONFIG = postgres:///pepsi

[pepsi-ingress]
HOSTNAME = mail.example.org
ACCEPTED_DOMAINS = example.org

[pepsi-ingress-listener-mx]
SERVE = tcp
BIND_TO = 0.0.0.0
PORT = 25
MODE = starttls

# init: write each local recipient's copy into their Maildir via the setuid
# helper. Terminal for local recipients.
[stage-init]
PROGRAM = pepsi-stage-relay-to-maildir
SERVER_NAME = mail.example.org

Eine Staging-Installation, die nie das Netzwerk berührt, tauscht die Zustell-Stage gegen pepsi-stage-discard (siehe pepsi-stage-discard).

6.6. Konfiguration in der Datenbank

Die oben beschriebene Datei ist die Basisschicht. Darüber liest Pepsi eine Menge administratorverwalteter Überschreibungen aus der Tabelle pepsi.config_override, sodass eine laufende Installation ohne Dateibearbeitung und ohne Neustart umkonfiguriert werden kann.

Nichts davon ist standardmäßig an. Ohne Datensätze in dieser Tabelle verhält sich eine Installation genau so, wie ihre Konfigurationsdatei es sagt — es gibt keine implizite Konfiguration, die sich in der Datenbank versteckt, und jede Überschreibung zu löschen bringt das System zum Verhalten der Datei zurück.

6.6.1. Die Geltungsbereichskette

Fünf Schichten, die niedrigste Präzedenz zuerst:

Schicht

Geschrieben von

Gilt für

die Konfigurationsdatei

dem Operator, mit einem Texteditor

alles

global

pepsi-config set

alles

domain:<domain>

pepsi-config set --scope

Nachrichten, deren maßgebliche Adresse bei dieser Domain liegt

address:<address>

pepsi-config set --scope

Nachrichten, deren maßgebliche Adresse genau diese ist

pepsi.settings

dem Kontoinhaber, per E-Mail

Nachrichten an/von genau dieser Adresse

Höhere Schichten überschreiben niedrigere Option für Option, nie Abschnitt für Abschnitt. Eine Option, die keine Schicht erwähnt, behält ihren Dateiwert, sodass eine Überschreibung immer eine gezielte Ergänzung ist und nicht der Ersatz eines ganzen Abschnitts.

Die „maßgebliche Adresse“ ist dieselbe, die die Einstellungsschicht je Adresse verwendet: der Absender des Umschlags bei einer lokal erzeugten Nachricht, sonst jeder Empfänger des Umschlags. Wenn die Empfänger einer Nachricht für die gleich laufende Stage auf verschiedene Konfigurationen auflösen, wird die Nachricht in einen Datensatz je eigener Konfiguration aufgeteilt, genau wie bei pepsi.settings.

Die Schichten domain: und address: enthalten nur [stage-*]-Abschnitte: Eine Stage zieht sie für den Abschnitt heran, den sie gerade ausführt, und zwar pro Nachricht. Jeder andere Abschnitt wird einmal pro Prozess gelesen und sieht die Datei plus die global-Schicht; daher weigert sich pepsi-config, eine bereichsbezogene Überschreibung eines solchen Abschnitts zu speichern, und pepsi-setup run meldet jeden solchen Datensatz als Fehler.

6.6.2. Warum pepsi.settings eine eigene Tabelle ist

Die beiden haben verschiedene Schreibbefugnisse:

  • pepsi.settings wird von Kontoinhabern geschrieben, aus ihrem eigenen Postfach, über pepsi-stage-edit-settings, beschränkt auf die Stage-Abschnitte, die der Operator in EDITABLE_STAGES aufgeführt hat;

  • config_override definiert die Pipeline und ist nur über die Datenbankrolle pepsi-config beschreibbar.

Sie zusammenzuführen setzte benutzerbeschreibbare Datensätze in dieselbe Tabelle wie die Definition des Mailservers, wo ein Fehler in einer Namensraumprüfung aufhört, ein Überschreibungsleck zu sein, und zu „ein Benutzer hat den MTA umkonfiguriert“ wird. PostgreSQL setzt die Trennung durch: pepsi-setup gewährt INSERT/UPDATE/DELETE auf config_override allein der Rolle pepsi-config, entzieht sie jedem Dienstkonto und verifiziert beides nach jeder Installation gegen die laufende Datenbank. Ein Stage-Worker parst berufsmäßig feindliche Mail; er darf die Konfiguration lesen und darf sie nicht ändern.

6.6.3. Was in der Konfigurationsdatei bleibt

Manche Abschnitte werden nie aus der Datenbank gelesen, welche Datensätze auch existieren. Eine Überschreibung, die einen davon benennt, wird zur Laufzeit ignoriert und beim Schreiben abgelehnt:

[pepsi], [pepsi-postgres], [PATHS]

Werden gebraucht, um die Datenbank überhaupt erst zu finden und zu öffnen, und — für [pepsi] — sind die Heimat der nur dem Administrator vorbehaltenen kryptographischen Richtlinie.

[pepsi-httpd], [pepsi-httpd-listener-*], [pepsi-httpd-cert-*]

Der Server, der die Konfigurationsoberfläche bedient, muss immer starten können, was auch immer die Datenbank sagt.

[pepsi-ingress-listener-*]

Lauschende Sockets werden beim Start gebunden, oft unter Socket-Aktivierung und bevor Privilegien abgegeben werden.

[pepsi-secure-link]

Sein PEPPER ist das serverseitige Geheimnis, das eine gestohlene Datenbank nutzlos macht (siehe Das Secure-Link-Ausweichportal). Eine Datenbank, die es ersetzen könnte, könnte stillschweigend dafür sorgen, dass die nächste gespeicherte Nachricht eine ist, die der Angreifer öffnen kann — dieselbe Art von Sache wie das Absenken der Kryptorichtlinie.

[pepsi-admin]

Entscheidet, wer den Server administrieren darf und wie die Administrationsoberfläche die Datenbank erreicht; eine Meinung der Datenbank dazu wäre ein Weg, sich selbst die Konsole zu gewähren.

[pepsi-crypto], [pepsi-srs], [pepsi-origin]

Jeder enthält einen serverseitigen Schlüssel, der gerade deshalb existiert, damit die Datenbank allein nicht genügt: die Schlüssel-Verschlüsselungsschlüssel (und welcher davon neue private Schlüssel verpackt), den SRS-Schlüssel, der das Relaying an beliebige Adressen autorisiert, und den Herkunftsnachweis-Schlüssel, gegen den ein zurückkehrender Bounce oder eine Zahlungsaufforderung geprüft wird. Eine Datenbank, die einen davon ersetzen könnte, könnte dafür sorgen, dass der nächste verpackte Schlüssel einer ist, den ein Angreifer öffnen kann, oder dass ein gefälschter Bounce weitergeleitet oder bezahlt wird. Der Rest jedes Abschnitts fährt mit.

[pepsi-wizard]

Aus einem dritten Grund hier: Es ist überhaupt keine Konfiguration, sondern die Aufzeichnung des Setup-Assistenten über die Antworten, die er bekommen hat — die das Browser-Interview als Entwurfs-Datensätze in eben dieser Tabelle ablegt. Entwürfe sind für jeden Leser unsichtbar, aber ein von Hand geschriebener Nicht-Entwurfs-Datensatz landete sonst in der effektiven Konfiguration jedes Prozesses. Zur Laufzeit liest niemand den Abschnitt, daher lautet die sichere Antwort, dass ihn auch die Datenbank nie liefert.

Die Trennlinie lautet „vor einer Datenbankverbindung nötig, eine Sicherheitsgrenze oder die Heimat eines serverseitigen Geheimnisses“. Listener stehen bewusst auf der zweiten Seite davon: Könnte die Datenbank einen lauschenden Socket verschieben oder sein TLS-Material ändern, würde eine Datenbankkompromittierung zu einer Kompromittierung der eigenen Ports des Mailservers.

Wichtig

Einen Submission-Port hinzuzufügen ist eine Texteditor-und-Neustart-Operation. Ebenso das Ändern des TLS-Zertifikats eines Listeners, der Datenbankverbindung oder der [pepsi]-Kryptorichtlinie. Keine administrative Oberfläche kann diese ändern, und keine sollte den Eindruck erwecken, sie könne es.

Zugangsdaten werden nie in der Datenbank gespeichert, gleich in welchem Abschnitt sie stehen. Eine Option, deren Name sie als solche kennzeichnet — sie enthält PASS, SECRET, TOKEN, CREDENTIAL, CLIENT_ID oder PEPPER, dieselbe Regel, die einen Wert überall maskiert, wo eine Konfiguration angezeigt wird —, wird von pepsi-config set und von der Administrations-API abgelehnt und ignoriert, falls dennoch ein Datensatz dafür existiert. Die Regel erfasst auch die Datei, aus der ein Zugangsdatum gelesen wird, und den Endpunkt, an den es gesendet wird (TOKEN_FILE, TOKEN_ENDPOINT), denn eine Datenbank, die diese ändern kann, kann das Zugangsdatum umleiten. Legen Sie sie in die Datei oder in ein secrets.d-Fragment.

Ebenso wird eine domain:- oder address:-Überschreibung nur für einen [stage-*]-Abschnitt angenommen (siehe oben: nichts anderes liest diese Schichten) und für jeden anderen Abschnitt abgelehnt, statt dort gespeichert zu werden, wo nichts sie lesen wird.

Zwei weitere Optionen sind aus einem schlichteren Grund nur in der Datei: Sie werden gelesen, um die Datenbank selbst zu öffnen, bevor es eine Überlagerung geben kann: [pepsi-ingress] DB_POOL_SIZE und [pepsi-dispatch] DB_POOL_SIZE.

6.6.4. Hot Reload, und was einen Neustart braucht

Jeder Schreibvorgang in die Tabelle löst eine config_changed-Benachrichtigung aus. Was dann geschieht, hängt vom Abschnitt ab:

[stage-*] — ohne Neustart angewandt.

pepsi-dispatch tut zwei Dinge, wenn die Benachrichtigung eintrifft: Es baut seine eigene Sicht auf die Pipeline aus dem Overlay neu auf — welche Stages existieren und jeweils deren PROGRAM, PARALLELISM, MAX_MESSAGES und QUEUE_LIMIT —, und es setzt seine Stage-Worker außer Dienst (keine Nachricht wird unterbrochen: ein Worker bekommt keine weitere Arbeit und beendet sich, sobald seine aktuellen Nachrichten erledigt sind), sodass ihre Nachfolger die neuen Werte lesen. Die nächste Nachricht wird mit der neuen Konfiguration geroutet und ausgeführt; das ist es, was das Hinzufügen, Bearbeiten und Entfernen einer Stage im laufenden Betrieb möglich macht. Die eine harte Kante: Wenn sich der neu geladene Graph nicht parsen lässt, fährt der Dispatcher herunter und beendet sich mit EX_CONFIG, statt nach einer Pipeline zu routen, die seine Worker nicht mehr teilen. Die mitgelieferte Unit startet ihn neu (Restart=always, mit einem Backoff bis auf einmal pro Minute), sodass er von selbst weiterläuft, sobald die Konfiguration korrigiert ist. Ein Overlay, das sich nicht lesen lässt (ein Datenbankfehler), ist etwas anderes: Der Dispatcher behält seinen aktuellen Stage-Graphen und versucht das Neuladen erneut, statt jede nur in der Datenbank definierte Stage fallen zu lassen.

Alles andere — braucht einen Neustart der Komponente, die es liest.

[pepsi-ingress], [pepsi-srs], [pepsi-tlsrpt] und die übrigen werden beim Start einmal gelesen. Der Wert wird gespeichert und wirkt, wenn dieses Programm das nächste Mal gestartet wird. pepsi-config sagt nach jedem Schreibvorgang, was zutrifft.

Eine Komponente, die eine Benachrichtigung verpasst — weil ihr Listener getrennt war —, liest die Überlagerung erneut, wenn der Listener sich wieder verbindet, sodass eine verlorene Benachrichtigung Latenz kostet und keine Korrektheit.

6.6.5. Geheimnisse bleiben in Dateien

Die Datenbank speichert nur die @inline-secret@-Referenz, nie einen Geheimniswert, sodass jedes Fragment unter secrets.d dem einzigen Leser gehört, der es braucht: Eine unprivilegierte Stage kann ihr eigenes Geheimnis lesen und sonst nichts. Verschlüsselte Werte in der Datenbank gäben jedem Leser einen Schlüssel, der alles öffnet.

6.6.6. Die Überlagerung bearbeiten

pepsi-config set   stage-relay MAX_LIFETIME '48 h'
pepsi-config set   --scope domain:example.org stage-relay DELAY_DSN_AFTER '4 h'
pepsi-config unset stage-relay MAX_LIFETIME
pepsi-config list

Jeder Schreibvorgang wird zuerst validiert, indem die Konfiguration gebaut wird, die die Änderung ergäbe, und der echte Parser der besitzenden Stage darüber läuft — dieselbe Prüfung, die pepsi-stage-edit-settings auf eine per E-Mail übermittelte Überschreibung anwendet. Ein Wert, der ein Programm am Starten hindern würde, wird mit der eigenen Fehlermeldung dieses Programms abgelehnt, und nichts wird gespeichert.

Sollte dennoch ein fehlerhafter Datensatz in die Tabelle gelangen, hängt das Weitere davon ab, wie fehlerhaft er ist. Ein Datensatz, der sich in der Konfiguration nicht darstellen lässt (ein unbekannter Bereich, ein Nur-Datei-Abschnitt, ein fehlerhafter Name), wird laut protokolliert und übersprungen; eine Überlagerung, die sich gar nicht lesen lässt oder deren zusammengeführter Text sich nicht parsen lässt, wird protokolliert, und das Programm läuft allein mit der Konfigurationsdatei. Ein Wert, der wohlgeformt ist, den das Programm selbst aber ablehnt — eine nicht parsebare Dauer, ein NEXT_STAGE, das keine Stage benennt —, wird nicht übersprungen: Er ist Teil der effektiven Konfiguration, und das Programm scheitert daran genau so, wie es am selben Wert in der Datei scheitern würde. pepsi-setup run prüft auf beides (siehe Validieren).

6.6.7. Woher ein Wert stammt

pepsi-config dump --origin
pepsi-config dump --origin --scope address:user@example.org

annotiert jeden effektiven Wert mit der Schicht, die ihn gesetzt hat (file oder der Datenbank-Geltungsbereich), und jeden Abschnitt damit, ob eine Änderung daran live wirkt oder einen Neustart braucht.

6.6.8. Sie sichern

Die beiden Hälften werden absichtlich von verschiedenen Werkzeugen gesichert:

  • was in der Datenbank liegt — die Überschreibungen — wird von pg_dump zusammen mit dem übrigen Schema abgedeckt;

  • was das nicht sein kann, weil es existieren muss, bevor es eine Datenbankverbindung gibt — die Konfigurationsdatei und die secrets.d-Fragmente —, wird von pepsi-config export abgedeckt, das ein einzelnes passwortverschlüsseltes Archiv schreibt.

Das Archiv hält Modus, Eigentümer und Gruppe jeder Datei namentlich fest, und pepsi-config import stellt sie wieder her; es lehnt ab, statt zu raten, wenn ein benanntes Konto auf der Zielmaschine nicht existiert. Ein Archiv, das die Eigentumsverhältnisse verlöre, würde entweder jeden unprivilegierten Leser kaputtmachen oder stillschweigend ein Geheimnis für alle lesbar machen.

6.7. Validieren

Validieren Sie eine Konfiguration immer, bevor Sie sich auf sie verlassen:

pepsi-setup  -c /etc/pepsi/pepsi.conf check    # against live DNS
pepsi-config -c /etc/pepsi/pepsi.conf dump     # effective values

pepsi-setup run weigert sich, irgendetwas zu ändern, wenn die Konfiguration ungültig ist: Es prüft, dass [stage-init] existiert, dass jedes NEXT_STAGE/BOUNCE_STAGE auflöst, dass die PROGRAM-Konfiguration jeder Stage geparst werden kann und dass Domains und PUBLIC_IP-Werte wohlgeformt sind.

Es validiert auch die Datenbank-Überlagerung: Eine gespeicherte Überschreibung muss einen echten Bereich benennen, darf keinen Nur-Datei-Abschnitt benennen (der stillschweigend ignoriert würde), darf in einen domain:- oder address:-Bereich nichts außer einem [stage-*]-Abschnitt legen und muss einen existierenden Stage-Abschnitt benennen — in der Datei oder von der Überlagerung selbst definiert, da der Dispatcher eine mit pepsi-config set hinzugefügte Stage ohne Neustart lädt. Die dabei entstehende Konfiguration muss dann weiterhin den eigenen Parser jeder betroffenen Stage zufriedenstellen: die global-Schicht über der Datei und die Kette jedes domain:/address:-Bereichs darüber, wobei jeweils die Parser der berührten Stages laufen.