15. Sicherheitsmodell¶
Dieses Kapitel sagt, wogegen Pepsi sich verteidigt, wogegen nicht und was ein Angreifer aus jedem einzelnen Ding bekommt, das er erbeutet haben könnte — es ist damit nach einer Kompromittierung ebenso nützlich wie davor.
Pepsi ist ein MTA, der private Schlüssel hält und Mail lesen kann, was ihn angriffswürdig macht. Der Entwurf stellt immer wieder eine Frage: Wenn diese Komponente kompromittiert ist, was bleibt außer Reichweite? Wo die Antwort „nichts“ lautet, wird das hier gesagt, statt es entdecken zu lassen.
Die Zusicherungen — welche Schutzgüter, gegen welche Angreifer, in welchem Verwahrungsmodus — sind in Bedrohungsmodell formuliert; dieses Kapitel beschreibt die Mechanismen dahinter.
15.1. Was Pepsi voraussetzt¶
Ist einer davon falsch, gelten die untenstehenden Zusicherungen nicht:
Der Host ist nicht bereits kompromittiert. Nichts hier verteidigt gegen root auf der Maschine. Root kann jeden Schlüssel, jedes Geheimnis und jede Nachricht lesen.
PostgreSQL authentifiziert über Peer-Zugangsdaten auf einem lokalen Socket. Jede Komponente verbindet sich als ihr eigener Betriebssystembenutzer, und die Datenbank gewährt dieser Rolle nur das, was die Komponente braucht. Eine Installation, die auf Passwortauthentifizierung mit einem gemeinsamen Konto umstellt, wirft das meiste von Ein Angreifer, der die Datenbank hat weg.
Das Betriebssystem setzt Dateieigentum und das setuid-Bit durch. Die Grenze der privaten Schlüssel ist ein Datenbank-
GRANT, das sich nach der effektiven uid richtet; einenosuid-Einhängung oder ein gemeinsames Konto lässt sie zusammenbrechen.Ein validierender DNS-Resolver. Pepsi vertraut dem AD-Bit, statt DNSSEC selbst zu validieren (siehe pepsi-stage-relay-to-internet). Ein lügender Resolver hebelt DANE aus und stuft MTA-STS auf Vertrauen beim ersten Gebrauch herab.
TLS-Zertifikatsmaterial stammt nicht vom Angreifer. Die certbot-Integration schreibt nach
/etc/letsencrypt; ein Angreifer, der dort schreiben kann, kann sich als der Server ausgeben.
15.2. Die Privilegientrennung¶
Stützt
Eine kompromittierte Komponente (was jedes kompromittierte Konto erreicht), Ein lokaler Shell-Benutzer (keine Rolle ist von einem gewöhnlichen Konto aus erreichbar) und Wer eine Kopie der Datenbank hat (was eine Dienstrolle schreiben kann).
Pepsi ist nicht ein Programm. Jede Komponente läuft unter ihrem eigenen Konto, und die Trennung wird von der Datenbank ebenso durchgesetzt wie vom Dateisystem.
Die Datenbankseite zieht zwei Arten von Grenzen, und es lohnt sich, bei beiden genau zu sein.
Die Pipeline-Rolle ist absichtlich weit gefasst. pepsi (der Dispatcher und jede gewöhnliche Stage) und pepsi-crypto halten eine pauschale Berechtigung: SELECT, INSERT, UPDATE und DELETE auf jede Tabelle im Schema pepsi, die Nutzung jeder Sequenz, EXECUTE auf jede Funktion und dasselbe als Standardrechte für später angelegte Objekte. Jede Stage liest und schreibt die Warteschlange, und zusammen nutzen die Stages jede Pipeline-Tabelle, sodass geringste Rechte je Stage ein Konto je Stage erfordern würden; die Folge wird in Bedrohungsmodell benannt statt verschwiegen. Die Berechtigung wird dann dort wieder zurückgenommen, wo die Pipeline nichts zu suchen hat:
crypto_identity:SELECT/INSERT/UPDATEnur auf die Spalten außerprivate_wrapped(DELETEkennt keine Spaltengranularität und bleibt); nurpepsi-cryptohält die Spalte;config_override: nurSELECT— das Schreiben ist Sache der Rollepepsi-config(pepsi-httpdschreibt über eine[pepsi-admin] CONFIG_DB-Verbindung, die sich als diese Rolle authentifiziert);setup_taskundsetup_task_log: nichts; nurpepsi-configdarf eine Aufgabe perINSERTanlegen;secure_messageundsecure_access:INSERT,DELETEundSELECTauf jede Spalte außerciphertextbeisecure_message;SELECTundDELETEbeisecure_access;admin_account,admin_sessionundapi_token: nichts;event_logundmail_log: nur anhängend (keinUPDATE/DELETE).
Die Dienste, die nicht zur Pipeline gehören, bekommen, was sie nutzen. Eine Prüfung jeder Anweisung, die jeder von ihnen absetzen kann — im eigenen Crate und in jeder gelinkten Bibliothek, einschließlich der Aufrufe gespeicherter Funktionen —, legt ihre Berechtigungen fest, und pepsi-setup weigert sich abzuschließen, wenn PostgreSQL davon abweicht:
Rolle |
Gewährt |
Ausdrücklich nicht |
|---|---|---|
|
|
Jede andere Nachricht in der Warteschlange, Einstellungen, Whitelists, Schlüssel, alles über eine Liste außer ihrer Adresse. Ein Parser-Fehler in dem Prozess, der auf Port 25 lauscht, erreicht eine Tabelle, und zwar nur, um ihr etwas hinzuzufügen. |
|
Ihre Tabelle |
Alles andere, sodass ein Collector, der eine Datenbank mit einer Pipeline teilt, die Mail nicht erreichen kann. |
|
Die Tabellen der administrativen Zugangsdaten, das Portal, die Schlüsseltabellen (nicht |
Header oder Nachrichtentext einer Nachricht in der Warteschlange — die Konsole listet Umschläge auf und hat keinen Lesepfad zum Inhalt, noch kann sie die |
Eine Tabelle, die ein späterer Schema-Patch hinzufügt, liegt nicht in der Reichweite von pepsi-ingress oder pepsi-telemetry, bis ein Patch sie gewährt: Auch ihre Standardrechte sind entzogen.
Die Rollen außerhalb dieser Menge sind noch enger gefasst: pepsi-keydisc, pepsi-whitelist und pepsi-config halten jeweils nur Berechtigungen auf einigen benannten Tabellen. pepsi-crypto hält die pauschale Berechtigung plus private_wrapped. Der Schemaeigentümer pepsi-owner, als der sich pepsi-setup selbst verbindet, hält alles kraft Eigentümerschaft.
Komponente |
Läuft als |
Was sie erreichen kann |
|---|---|---|
|
|
|
|
|
Die obige Pipeline-Berechtigung: die Warteschlange, Einstellungen, Statistiken, die Whitelist und die Secure-Link-Tabellen ohne ihr |
|
|
Der gewöhnliche Pipeline-Zugriff, den jede Stage hat, zusätzlich das verpackte private Schlüsselmaterial und der Schlüsselverschlüsselungsschlüssel. Nichts sonst hält beides: Der Schemaeigentümer |
|
der aufrufende Operator |
Mit |
|
|
Die Tabellen in der zweiten Tabelle oben — lesend und schreibend, nicht überwiegend lesend: Seine Handler ändern das Routing der Warteschlange und Schlüsselmetadaten direkt. Keine Header oder Nachrichtentexte, kein privates Schlüsselmaterial, keine Einstellungen oder Whitelists; keine Möglichkeit, das Konfigurations-Overlay zu schreiben oder eine Setup-Aufgabe einzureihen, außer über seine |
|
|
Die Anfragewarteschlange der Schlüsselentdeckung und |
|
aufrufender Benutzer (setuid) |
Nur |
|
|
|
Die Postfach-Quota hält Messen und Ablehnen auseinander. Nur pepsi-helper-maildir-writer kann in ein Maildir hineinsehen (Modus 0700; es ist das eine Programm, das zum Benutzer wird), und nur Mitglieder von pepsi-maildir dürfen ihn ausführen — eine Gruppe, in der pepsi-ingress bewusst nicht ist und vor deren Hinzufügen pepsi-setup warnt. So kann der Prozess, der auf Port 25 lauscht, eine von jemand anderem genommene Messung lesen und darauf einen Empfänger ablehnen, und sonst nichts: pepsi-setup entzieht ihm den Schreibzugriff auf pepsi.mailbox_quota und lässt SELECT übrig. Beide Wege zur Abrechnung sind ihm verschlossen, und keiner der Verschlüsse stützt sich auf den anderen.
Die Krypto-Stages sind setuid, nicht setgid: Die Peer-Authentifizierung von PostgreSQL richtet sich nach der effektiven uid, sodass eine Stage, die bloß eine Gruppe erlangte, sich weiterhin als pepsi verbände und ihr die Schlüsselspalte verweigert würde. Sie muss pepsi-crypto sein.
15.2.1. Jedes Programm, das ein Bit trägt¶
Stützt
Ein lokaler Shell-Benutzer: Ein lokales Konto erlangt keine Pepsi-Rolle, und jeder Helper handelt nur mit der Autorität des Zielbenutzers (Grenze B4).
Ein Multi-Call-Binary kann kein programmspezifisches setuid- oder setgid-Bit tragen, daher wird jedes dieser Programme als eigene ausführbare Datei installiert statt als symbolischer Verweis auf das gemeinsame pepsi-Binary (die maßgebliche Liste ist STANDALONE_BINARIES im Makefile; die Modi werden von make install und, unter Debian, von dpkg-statoverride aus dem postinst gesetzt). Alles, was hier nicht aufgeführt ist, ist unprivilegiert.
Programm |
Eigentümer und Modus |
Warum |
|---|---|---|
|
|
Wird zum Empfänger, um in ein |
|
|
Das setgid-Bit ist die einzige Schranke vor dem Ausführen dieses Helfers, daher lautet der Modus |
|
|
Derselbe Helfer, damit ein |
|
|
Gibt die Rechte vollständig an den Benutzer ab, bevor dessen |
|
|
Dieselbe Schranke, für diesen Helfer. |
|
|
Steuert die Wallet als das Konto, dem sie gehört. |
|
|
Dieselbe Schranke, für diesen Helfer. |
|
|
Liest die gruppenbeschränkten OAuth-Token-Dateien, die |
|
|
Absichtlich für alle ausführbar: Jeder lokale Benutzer darf seinen eigenen Namensraum verwalten, und was ihn davon abhält, den eines anderen anzurühren, ist die Regel im Programm, nicht der Dateimodus. |
|
|
Die Peer-Authentifizierung richtet sich nach der effektiven uid, also muss die Stage die Rolle sein, der |
|
|
Siehe die Tabelle oben. Ein Standort kann es setuid |
|
|
Die unprivilegierte Hälfte des |
15.3. Die Verwahrungsgrenze für private Schlüssel¶
Stützt
Gateway-Verwahrung (custody = local, der MTA-Schlüssel) (Schlüssel werden niemals einem passiven Leser oder einer gewöhnlichen Stage offengelegt; Grenze B5).
Privates Schlüsselmaterial liegt in crypto_identity.private_wrapped und ist doppelt geschützt:
Durch Berechtigung. pepsi-setup gewährt diese eine Spalte der Rolle pepsi-crypto und entzieht sie jedem Dienstkonto, pepsi-httpd eingeschlossen; außer pepsi-crypto kann sie nur der Schemaeigentümer pepsi-owner kraft Eigentümerschaft lesen. Das gilt auf Spaltenebene und ist absolut: PostgreSQL lehnt eine Anweisung ab, die die Spalte auch nur nennt, selbst innerhalb eines IS NULL-Tests, sodass es keinen Lesepfad gibt, den man später einengen müsste.
Bemerkung
Diese Absolutheit hat eine praktische Kante. Eine Auflistungsabfrage kann „hat diese Identität eine private Hälfte?“ nicht als private_wrapped IS NOT NULL melden: Das wird für jede Rolle außer pepsi-crypto abgelehnt, und PostgreSQLs Fehler, permission denied for table crypto_identity, nennt keine Spalte. Die Auflistung leitet das Flag stattdessen aus wrap_key_id ab, das ein Tabellen-CHECK exakt im Gleichschritt hält.
Durch Verpackung. Gespeichert wird kein privater Schlüssel, sondern ein AEAD-Chiffrat, gebunden an das (address, protocol, purpose) dieses Datensatzes als zugehörige Daten, unter einem Schlüsselverschlüsselungsschlüssel, der außerhalb der Datenbank liegt, in einem secrets.d/*.secret-Fragment, das nur pepsi-crypto lesen kann — nicht pepsi-owner, sodass die einzige andere Rolle, die die Spalte lesen kann, trotzdem nichts hält, was sie öffnen könnte. Ein Blob zwischen Datensätzen zu verschieben scheitert an der Authentifizierung.
15.4. Ein zweiter Faktor für die Schlüsselverwaltung¶
Stützt
Wer das Mail-Passwort eines Benutzers hat (ein gestohlenes Mail-Passwort kann keinen Schlüssel für einen Benutzer registrieren, der einen zweiten Faktor eingerichtet hat).
Ein Benutzer kann für seine Adresse ein TOTP-Geheimnis einrichten (RFC 6238: HMAC-SHA-1, sechs Ziffern, 30-Sekunden-Schritte — was jede Authenticator-App implementiert); von da an braucht jede Änderung an seinen Schlüsseln, die er vornehmen kann, einen aktuellen Code:
Per E-Mail nimmt jeder Schlüsselbefehl außer
status—generate,register,publish,retireundotp enroll/otp removeselbst — den Code als sechsstelliges Wort imSubject:entgegen. DerAutocrypt:-Header oder ein angehängter Schlüssel einer gewöhnlichen Nachricht registriert für diese Adresse nichts mehr.In der Web-Konsole und der API tragen die Anfragen
generate-identity,register-client-keyundrevoke-identityeinesown:<address>-Prinzipals ein Feldotp.pepsi-httpdkann es nicht prüfen — es hält überhaupt keine Berechtigung auf die Tabelle — und reicht es durch;pepsi-setup applyprüft es, bevor es handelt, bei einer Aufgabe, die allein aufgrund vonown:<address>zugelassen wurde. Ein Operator, derkeys:writehält, wird nicht gefragt.Ein unprivilegiertes
pepsi-keys(eine setuid-Installation) braucht--otp CODEfür jede Identitätsänderung und jeden Export eines privaten Schlüssels; die Eigentümerschaft wird zuerst geprüft, sodass ein Benutzer niemals Fehlversuche gegen die Einrichtung eines anderen verbuchen kann.
Verwahrung. Das Geheimnis wird genau wie ein privater Schlüssel verpackt — AES-256-GCM unter dem KEK des Schlüsselspeichers, gebunden an (address, totp, second-factor) —, und die Tabelle otp_key ist allein der Rolle pepsi-crypto gewährt: pepsi-setup entzieht sie jeder anderen Rolle, pepsi der Pipeline eingeschlossen, und prüft das gegen PostgreSQL. Eine Stage, die feindliche Mail parst, kann daher weder ein Geheimnis lesen noch einen Zähler zurücksetzen noch eine Einrichtung löschen (was den Schutz abschalten würde). pepsi-keys wrap rotate verpackt die Geheimnisse zusammen mit den privaten Schlüsseln neu.
Versuche. Der Code wird von dem Prozess geprüft, der das Geheimnis entpacken kann; die Entscheidung trifft die SQL-Funktion otp_attempt unter der Datensatzsperre. Ein Code, der zu einem Zeitschritt nach dem zuletzt akzeptierten passt, wird akzeptiert und setzt den Fehlerzähler auf null zurück; derselbe Code noch einmal oder ein älterer ist eine Wiederholung (Replay) und zählt als Fehlversuch, ebenso ein falscher Code. Nach zehn aufeinanderfolgenden Fehlversuchen wird die Einrichtung gesperrt, und jeder Versuch wird ungeprüft abgelehnt. Ein Befehl ganz ohne Code wird abgelehnt, ohne gezählt zu werden. Jede Einrichtung hat eine nie wiederverwendete Seriennummer, sodass ein Versuch, der gegen ein inzwischen ersetztes Geheimnis geprüft wurde, verworfen statt gegen das neue verbucht wird.
Der Operator hebt eine Sperre auf, indem er die Einrichtung entfernt (pepsi-keys otp reset oder DELETE /api/v1/otp/{address}, das eine reset-otp-Aufgabe einreiht, die nur keys:write anfordern darf) oder sie ersetzt (pepsi-keys otp enroll braucht als Operator keinen Code); eine neue Einrichtung beginnt immer bei null Fehlversuchen.
Was er nicht leistet. Er schützt nicht die erste Einrichtung (es gibt noch keinen Code, nach dem gefragt werden könnte), und auch keine Einrichtungsantwort, die der Benutzer im Postfach liegen gelassen hat. Er sichert die synchronen Schalteränderungen der Web-Konsole nicht ab (WKD-Veröffentlichung, eine Anfrage zum Hochladen auf einen Keyserver, der primäre MTA-Schlüssel), die pepsi-httpd selbst ausführt. Und er fügt nichts gegen den Operator hinzu, der ihn zurücksetzen kann.
15.5. Ein Angreifer, der die Datenbank hat¶
Stützt
Wer eine Kopie der Datenbank hat.
Nehmen Sie einen vollständigen Dump an — eine gestohlene Sicherung, ein Replikat, SQL-Injektion in einen nur lesenden Pfad.
Sie bekommen:
jede derzeit in der Warteschlange befindliche Nachricht, im Klartext:
pepsi.workqueuehält dieheadersundpepsi.workqueue_bodyden Nachrichtentext, unverschlüsselt. Pepsi ist eine Pipeline; eine Nachricht in Bearbeitung muss für die Stage lesbar sein, die sie verarbeiten wird. Zugestellte Nachrichten werden gelöscht, sodass dies der Rückstau ist und nicht die Geschichte.den vollständigen Korrespondenzgraphen: Absender, Empfänger, Zeitstempel, Einstellungen je Adresse, Whitelists, die Tabellen
origin_nonce,auto_pay_spend,payment_request_replyunddns_address.jeden öffentlichen Schlüssel und jedes Zertifikat sowie alle Schlüsselmetadaten.
die ARC-/DKIM-/DMARC-Urteile und die TLS-Sitzungsergebnisse.
Sie bekommen nicht:
irgendeinen privaten Schlüssel, noch irgendein Geheimnis eines zweiten Faktors. Die verpackten Blobs sind AEAD-Chiffrat, und der Schlüsselverschlüsselungsschlüssel ist nicht in der Datenbank.
irgendeinen Secure-Link-Nachrichtentext.
secure_message.ciphertextist AES-256-GCM unter einem Schlüssel, den Argon2id aus der PIN des Empfängers, einem Salz je Nachricht und einem serverseitigen Pfeffer ableitet, der in der Konfiguration und nicht in der Datenbank liegt. Die PIN selbst wird in keinerlei Form gespeichert — nicht einmal als Hash. Ein Datenbankdieb muss eine PIN durchprobieren, die er ohne den Pfeffer offline nicht einmal prüfen kann.irgendein brauchbares Credential. Admin-Passwörter sind Argon2id-Hashes; API-Tokens werden als Digests gespeichert, sodass ein Token nicht aus einer Sicherung wiederhergestellt werden kann und genau einmal angezeigt wird.
die DKIM-Signierschlüssel, die im Dateisystem liegen, und die SRS- und Pepsi-Origin-Geheimnisse, die in
secrets.dliegen.
Warnung
Die Warteschlange ist die Angriffsfläche. Ein Operator, dem die Vertraulichkeit von Mail in Bearbeitung wichtig ist, sollte die Datenbank im Ruhezustand verschlüsseln und die Sicherungen entsprechend behandeln — Pepsi kann die Warteschlange nicht an sich selbst verschlüsseln, denn jede Stage bräuchte den Schlüssel, was dasselbe ist wie keinen zu haben.
15.6. Ein Angreifer, der die Konfigurationsdatei hat¶
Stützt
Wer die Konfiguration oder ein Geheimnis hat.
/etc/pepsi/pepsi.conf hat absichtlich den Modus 0644: Unprivilegierte Stages lesen sie. Sie darf daher keine Geheimnisse enthalten und tut es nicht — jedes Geheimnis liegt in einem secrets.d/<reader>.secret-Fragment, das über eine @inline-secret@-Direktive eingezogen wird und dem einen Konto gehört, das es braucht.
Lesezugriff auf die Konfigurationsdatei liefert die Topologie: Hostnamen, bediente Domains, den Pipeline-Graphen, Smarthost-Adressen, Dateipfade, welche Funktionen an sind. Das ist Aufklärung, keine Kompromittierung.
Lesezugriff auf ein ``secrets.d``-Fragment liefert genau das, was sein einer Leser hält, und nicht mehr — den SRS-Schlüssel, oder das Smarthost-Passwort, oder den Schlüsselverschlüsselungsschlüssel, oder den Secure-Link-Pfeffer, oder den Abmeldeschlüssel der Listen. Deshalb sind sie aufgeteilt: Es gibt keine einzelne Datei, deren Offenlegung total wäre.
Schreibzugriff auf die Konfigurationsdatei ist gleichbedeutend mit root. Sie benennt die Programme, die jede Stage ausführt. Behandeln Sie sie als root-eigene Datei, denn das ist sie.
Warnung
Zwei Fallen rund um @inline-secret@, die beide wie eine funktionierende Konfiguration aussehen:
die Direktive beendet ihren Abschnitt — alles danach Geschriebene landet in gar keinem Abschnitt;
ein nicht lesbares Fragment warnt nur. Die Option liest sich dann als nicht gesetzt, und eine Funktion läuft stillschweigend ohne ihr Geheimnis. Prüfen Sie, indem Sie das Fragment als das lesende Konto
stat-en, nicht indem Sie das Journal lesen.
15.7. Ein Angreifer, der einen Stage-Worker kompromittiert¶
Stützt
Eine kompromittierte Komponente, einschließlich seines verbleibenden Signaturrisikos.
Nehmen wir an, ein Parser-Fehler ergibt Codeausführung innerhalb einer Stage — der realistische Fall, denn Stages parsen berufsmäßig feindliche Eingaben.
Eine Stage unter dem Benutzer ``pepsi`` (die meisten) bekommt die Warteschlange: jede Nachricht in Bearbeitung lesen, verändern, umleiten oder löschen und die Whitelist und die Einstellungen lesen. Sie bekommt nicht die verpackten privaten Ende-zu-Ende-Schlüssel und kann nicht zu einem anderen Konto werden.
Sie bekommt allerdings die Möglichkeit, Mail signieren zu lassen, und darüber lohnt es sich, unverblümt zu sein, denn es ist das größte verbleibende Risiko im Entwurf:
pepsi-stage-dkim-signläuft alspepsiund liest die DKIM-Schlüssel der Domains über die Gruppepepsi, sodass jederpepsi-Prozess als jede bediente Domain signieren kann — direkt oder indem er einen Datensatz in der Warteschlange bearbeitet, bevor die signierende Stage ihn sieht.pepsi-stage-encryptsigniert als derFrom:-Autor des Datensatzes. Ingress setztUSERNAME_MAPbei der Einlieferung durch, aber der Datensatz in der Warteschlange ist danach für jedepepsi-Stage schreibbar, und nichts weiter unten kann erkennen, ob Ingress diese Entscheidung getroffen hat. Eine kompromittiertepepsi-Stage kann daher dasFrom:einer eingereihten Einlieferung umschreiben (oder mitworkqueue_injecteine Nachricht einschleusen) und sie mit dem Ende-zu-Ende-Schlüssel jedes beliebigen lokalen Benutzers signieren lassen.
Die Integrität einer von Pepsi erzeugten Signatur — DKIM oder benutzerspezifisches OpenPGP/S/MIME — ist also nur so stark wie die gesamte pepsi-Pipeline-Rolle, nicht so stark wie pepsi-crypto. Was die Trennung sehr wohl begrenzt, ist die Offenlegung von Schlüsseln: Ein kompromittierter Spracherkenner kann Nachrichten signieren lassen, solange er läuft, aber er kann keinen privaten Schlüssel mitnehmen, sodass der Schaden endet, wenn die Stage bereinigt ist, und keinen Schlüsselwechsel erfordert. Siehe Bedrohungsmodell dazu, wo dies unter den übrigen Zusicherungen steht.
Eine ``pepsi-crypto``-Stage bekommt alles, was dieses Konto hat: Sie kann eingehende Mail an die Installation entschlüsseln und als jede lokale Identität signieren. Das ist das wertvollste Ziel im System, weshalb die beiden Krypto-Stages klein sind, überhaupt keine Netzwerk-E/A betreiben (die Entdeckung ist ein eigenes Programm) und Gegenstand des Fuzzings in Testsuite sind.
``pepsi-ingress`` sieht jede Nachricht beim Eintreffen und entscheidet, wer sie eingeliefert hat, sodass es Mail als jeder lokale Benutzer zulassen kann. Seine Datenbankrolle kann der Warteschlange etwas hinzufügen und nichts aus ihr lesen, sodass das bereits Eingereihte, die Einstellungen, die Whitelists und die Schlüssel außerhalb seiner Reichweite bleiben.
``pepsi-httpd`` bekommt die Web-Oberfläche und ihre Datenbankberechtigungen — Umschläge und Routing der Warteschlange, niemals Header oder Nachrichtentext einer Nachricht. Es kann bemerkenswerterweise keine Konfiguration schreiben und keine privilegierten Setup-Schritte ausführen: Der Browser-Assistent handelt nicht, er fügt einen Datensatz in pepsi.setup_task ein, der beschreibt, was wahr sein soll, und das separat gestartete, als root laufende pepsi-setup apply entscheidet, ob es das tut. Eine kompromittierte Web-Schicht kann daher eine Umkonfiguration anfordern, aber nicht durchführen — und der Applier prüft jede Aufgabe, statt ihrer Beschreibung zu trauen.
Diese letzte Verteidigung lässt sich in eine absolute verwandeln, und das ist die richtige Haltung auf jeder vom Terminal aus verwalteten Installation. Die beiden systemd-Units des Appliers bilden ein eigenes Debian-Paket, pepsi-httpd-admin (pepsi Recommends es nur, sodass apt remove pepsi-httpd-admin gelingt und den Mailserver weiterlaufen lässt); eine Quellinstallation bekommt denselben Hebel aus make install INSTALL_ADMIN_UNITS=no. Sind sie fort, leert nichts pepsi.setup_task, sodass ein Insert darin inaktiv ist, was auch immer mit pepsi-httpd geschieht — die Schleife wird am Mechanismus gebrochen statt an der Schnittstelle, und kein Codepfad in der Web-Schicht kann einen root-Prozess starten. Die Konsole erkennt das, scheitert geschlossen, wenn sie es nicht feststellen kann, und bedient ihre Setup-Seiten nur lesend. pepsi-setup bleibt im Basispaket, sodass die Verwaltung des Hosts von Hand unberührt bleibt. Siehe Die Aufteilung als Härtungshebel.
15.8. Konkrete Verteidigungen¶
15.8.1. Die Identität des Einliefernden¶
Stützt
Ein lokaler Shell-Benutzer und Ein reiner Mail-Benutzer (Grenze B2): Ein Einliefernder sendet nur unter den Identitäten, die der Operator ihm zugeordnet hat.
Ingress entscheidet auf einem von vier Wegen, wer eine Nachricht eingeliefert hat — SASL über das konfigurierte Backend, ein TLS-Client-Zertifikat, eine Adresse in MYNETWORKS oder die SO_PEERCRED-Zugangsdaten des Sockets für lokale Einlieferung, womit sich sendmail ohne Passwort authentifiziert und weshalb dieses Socket für alle schreibbar sein darf. Welcher es auch ist, USERNAME_MAP entscheidet dann, welche From:- und Umschlagadressen diese Identität verwenden darf (RFC 6409 §6/§8.1), und eine Identität, die auf nichts abgebildet ist, darf sich authentifizieren, aber nicht senden. Für eine Adresse zu sprechen — einen Schlüssel für sie zu registrieren oder ihre Schlüssel per E-Mail zu verwalten — erfordert mehr als die Erlaubnis, unter ihr zu senden: Das From: muss genau ein Postfach sein, das von einem konkreten (platzhalterfreien) Eintrag der Zuordnung benannt wird (state.origin.from_bound). Eine Berechtigung *@example.org lässt ein Konto als jeder Kollege senden, aber niemals einen Schlüssel für ihn unterschieben.
Die Entscheidung wird im state der Nachricht festgehalten (local_origin, die authentifizierte Identität) und von jeder späteren Stage geglaubt — genau deshalb behandelt Eine kompromittierte Komponente eine kompromittierte pepsi-Stage als fähig, sie zu fälschen.
15.8.2. Entschlüsselte Mail für die Ablage neu versiegeln¶
Stützt
Client-Verwahrung (custody = client, der MUA-Schlüssel) (Mail, die das Gateway geöffnet hat, wird so abgelegt, dass nur der eigene Client des Benutzers sie lesen kann) und Die Garantien auf einen Blick (die Spalte Abgelegte Mail).
pepsi-stage-decrypt muss Klartext erzeugen: Die Spam-, Sprach-, Whitelist- und Schlüssellern-Stages danach lesen den Inhalt. pepsi-stage-reencrypt läuft, sobald sie das getan haben, unmittelbar vor der lokalen Zustellung, und versiegelt die Nachricht an den neuesten aktiven custody = client-Schlüssel des Empfängers. Der Mechanismus beruht auf vier Entscheidungen:
Er braucht kein Privileg. Das Versiegeln an einen öffentlichen Schlüssel verwendet kein privates Material, daher läuft die Stage als
pepsi, ist in das Multi-Call-Binary eingebunden und liest nur die öffentlichen Spalten voncrypto_identityunter der gewöhnlichen Pipeline-Berechtigung. Sie nennt niemalsprivate_wrapped, und die Identitätpepsi-cryptozu erlangen, gäbe ihr nichts, was sie nutzt.Er signiert nichts. Eine Gateway-Signatur würde nur sagen „das Gateway hat dies versiegelt“, in einem Client nicht von Urheberschaft zu unterscheiden, und bräuchte die setuid-Identität. Das Urteil über die Absendersignatur ist das von Decrypt festgehaltene, in
state.crypto.inund inX-Pepsi-Crypto— das auf dem äußeren Block bleibt und das Decrypt von jeder eingehenden Nachricht entfernt, bevor es sein eigenes schreibt, sodass es noch unseres ist, wenn der Client es liest.Er wirkt nur auf das, was Decrypt geöffnet hat (
state.crypto.insagt verschlüsselt und entschlüsselt), und hältstate.crypto.storefest, was ihn auch daran hindert, einen Datensatz jemals zweimal zu versiegeln. Mail, die im Klartext eingetroffen ist, wird nicht versiegelt.Eine Ablehnung gibt nichts zurück, was verschlüsselt war. Unter
ON_NO_CLIENT_KEY = bouncewird der Header-Block der DSN auf die Adressierungsfelder gekürzt, der Nachrichtentext geleert undRET=HDRSerzwungen, da der Absender die Nachricht verschlüsselt hat und eine DSN im Klartext reist. Es wird ein Bounce erzeugt statt aufgeschoben, weil Aufschieben den Klartext länger in der Warteschlange hielte.
Das verbleibende Risiko ist dasjenige, das Eine kompromittierte Komponente bereits benennt: Eine kompromittierte pepsi-Stage — die Neuverschlüsselungs-Stage eingeschlossen — sieht den Klartext, während er in der Warteschlange liegt, und kann eine Nachricht an der Stage vorbeileiten; die Zusicherung betrifft also bereits abgelegte Kopien, die nichts auf dem Server öffnen kann.
15.8.3. Wofür ein S/MIME-Vertrauensanker bürgt¶
Stützt
Was ein Sicherheitsindikator bedeutet, gegen Ein entfernter Absender: Eine CA, der der Operator für einen Namensraum vertraut, kann valid nicht für einen anderen erscheinen lassen.
Eine Organisation, die signierte Mail mit einem Partner austauscht, vertraut der CA des Partners oft nur für dessen eigene Domain — indem sie sie mit nameConstraints gegensigniert oder eine Sub-CA des Partners verankert, die diese trägt. Würde die Beschränkung ignoriert, könnte diese Partner-CA ein Zertifikat für einen der Benutzer dieser Installation ausstellen, und die Decrypt-Stage würde die Signatur valid nennen. pepsi-crypto::trust führt daher den Beschränkungszustand aus RFC 5280 §6.1 entlang jeder Kette, die es aufbaut:
Namensbeschränkungen gelten für den Subject-DN, jeden alternativen Subject-Namen und jedes
emailAddress-Attribut im Subject-DN — Letzteres immer, weil dieses Attribut das ist, woran eine Nachricht gebunden wird, wenn der SAN kein Postfach nennt. Erlaubte Teilbäume werden entlang der Kette geschnitten (zwei CAs, die disjunkte Domains erlauben, lassen kein Postfach zu, nicht jedes); ausgeschlossene Teilbäume sammeln sich an. Eine Sub-CA kann nicht erweitern, was ihr Elternteil delegiert hat.Die eigenen Beschränkungen des Ankers binden ihn (RFC 5937), sodass das direkte Verankern der Sub-CA des Partners ebenso sicher ist wie ihr Gegensignieren.
Richtlinienbeschränkungen (
requireExplicitPolicy,inhibitPolicyMapping,inhibitAnyPolicy) werden über den vollständigen Richtlinienbaum ausgewertet, in einer deduplizierten Form, deren Größe polynomiell in den Richtlinien ist, die ein Zertifikat nennt, und gedeckelt (64 Richtlinien oder Abbildungen je Zertifikat) — der Baum, der im naiven Algorithmus exponentiell wächst, ist der Denial of Service aus OpenSSL CVE-2023-0464. Die Namensprüfung ist aus demselben Grund auf 220 Vergleiche je Zertifikat gedeckelt.Was nicht eingehalten werden kann, wird abgelehnt. Eine nicht verarbeitbare Beschränkung — eine Teilbaumform, die Pepsi nicht abgleicht, wenn ein Name dieser Form darunter auftaucht; ein
minimumodermaximum; eine nicht dekodierbare Erweiterung, kritisch oder nicht — macht die Kettepolicy-rejected, statt sie ohne die Beschränkung gültig zu lassen. Wo Pepsi von RFC 5280 oder von OpenSSL abweicht, geschieht das immer in diese Richtung: Ein Platzhalter-dNSName, der für einen ausgeschlossenen Host stehen kann, wird abgelehnt, und eine URI ohne Host unter einer URI-Beschränkung wird abgelehnt.
Die NIST-PKITS-Konformitätssuite (Abschnitte 4.6 und 4.8–4.13, jede darin aufgeführte Eingabeeinstellung) läuft in cargo test, zusammen mit einem eigenen Korpus für die Formen, die PKITS nicht abdeckt (IP-Adressen, nicht implementierte Formen, ein namensbeschränkter Anker).
15.8.4. Header-Fälschung¶
Stützt
Was ein Sicherheitsindikator bedeutet, gegen Ein entfernter Absender.
X-Pepsi-Crypto sagt dem Client eines Benutzers, was Pepsi über die Kryptographie einer Nachricht geschlossen hat, sodass ein gefälschter eine mit unserer Autorität erzählte Lüge ist. Bei eingehender Mail wird der Header entfernt, bevor die Decrypt-Stage ihren eigenen hinzufügen kann, und ausgehende Anforderungs-Header werden durch STRIP_REQUEST_HEADERS entfernt. Ein entfernter Absender kann keinen erscheinen lassen.
Dieselbe Überlegung gilt für Authentication-Results und Pepsi-Origin: Ein Header, der bedeutet „das lokale System hat dies verifiziert“, ist nur dann aussagekräftig, wenn das lokale System das Einzige ist, das ihn schreiben kann.
15.8.5. Unbekannte Empfänger und Backscatter¶
Stützt
Schutzgüter (Sendeidentität und Reputation), gegen Ein entfernter Absender.
Ein Bounce geht an den Umschlagabsender, den eine Nachricht angibt, und Spam fälscht ihn. Ein MTA, der Mail für Adressen annimmt, die er nicht hat, und sie später bounct, schickt daher im Auftrag eines Spammers Mail an Fremde. Das ist Backscatter, und Blocklisten reagieren schnell darauf. Pepsi schließt das in drei Schichten, jede ein Netz unter der vorherigen:
Bei RCPT ablehnen.
pepsi-ingressantwortet550 5.1.1für eine Adresse an einer bedienten Domain, für die nichts auf dem Host bürgt: ein lokales Konto, ein Alias, eine Liste, eine Dienstadresse oder der MDA bzw. das Backend, gefragt mitRCPT TO. Die Quellen werden aus dem Stage-Graphen abgeleitet (pepsi-recipients). Jede Unsicherheit nimmt an, sodass ein Ausfall nie zu abgelehnter Mail wird.Nur an einen Absender berichten, der sich ausgewiesen hat.
pepsi-stage-bouncesendet eine DSN für eine eingehende Nachricht nur, wenn SPF für die Domain des Umschlagabsenders bestanden hat oder eine ausgerichtete DKIM-Signatur (BOUNCE_UNAUTHENTICATED = drop, der Standard). Alles andere wird verworfen und geloggt.Eine Schleife ablehnen. Eine Nachricht, die den Ingress dieses Hosts bereits
MAX_SELF_HOPSMal durchlaufen hat, wird als Routing-Schleife abgelehnt. Eine Adresse an unserer eigenen Domain, die an unseren eigenen MX weitergeleitet wird, kostet daher einen Bounce, nicht eine Runde durch die Pipeline für jeden Hop, den die Relay-Stages erlauben.
RCPT wahrheitsgemäß zu beantworten verrät einem Absender, welche Adressen existieren, wie es jeder MTA tut, der unbekannte Benutzer ablehnt. MAX_INVALID_RECIPIENTS schließt eine Sitzung nach zehn falschen Rateversuchen, sodass das Einsammeln Verbindungen kostet, die die Verbindungslimits bemessen. Proben beim MDA oder Backend werden zwischengespeichert und auf 16 gleichzeitig laufende begrenzt (darüber hinaus lautet die Antwort „unsicher“, was annimmt). Eine Flut von Rateversuchen lässt sich daher auch nicht gegen den Mailspeicher wenden. VRFY antwortet weiterhin auf alles mit 252.
Die Empfängerprüfung braucht eine Berechtigung, die pepsi-ingress bisher nicht hatte: die Namens- und Host-Spalten von mailing_list, die zusammen die öffentliche Adresse einer Liste ergeben und nichts weiter.
15.8.6. Wenn sich der Schlüssel eines Korrespondenten ändert¶
Stützt
Schlüssel von Korrespondenten, gegen Ein entfernter Absender: Ein geernteter Schlüssel darf durch einen neueren ersetzt werden, aber niemals stillschweigend.
Innerhalb der Autocrypt-Stufe (die Sprossen inbound und gossip) ist die Konfliktregel das Neueste-gewinnt aus Autocrypt Level 1, festgemacht am From:-Header, den ein Absender schreibt, und an einer Nachricht, die die eingehende Authentifizierung fail-open angenommen hat. Eine Nachricht, die nur behauptet, von einem Korrespondenten zu stammen, kann also den Schlüssel ersetzen, an den wir für ihn verschlüsseln. Die Mechanismen, die das begrenzen:
Die Ersetzung wird von der Anweisung festgehalten, die sie vornimmt.
crypto_peer_key_upsertschreibt in derselben Transaktion wie den Austausch einenkey.peer.rotate-Datensatz inpepsi.event_log(das Nur-Anhänge-Audit-Log, auf das keine Mail verarbeitende RolleUPDATEoderDELETEausführen kann), mit der Adresse, beiden Mengen von Fingerabdrücken und beiden Stichtagen. Kein Aufrufer kann einen Schlüssel ohne diesen Datensatz rotieren, und eine kompromittierte Stage, die eine Rotation verbergen wollte, müsste die Art Angreifer sein, der Eine kompromittierte Komponente die Warteschlange ohnehin schon zugesteht.Wer gleich antworten will, wird informiert.
pepsi-stage-autocrypt-learnschreibt jedem lokalen Umschlagempfänger der rotierenden Nachricht (RESPONSE_STAGE, die Vorlagekey-rotation) mit beiden Fingerabdrücken, sodass eine gefälschte Rotation etwas ist, das der betroffene Benutzer bemerken kann, bevor er etwas Vertrauliches sendet. Nur Empfänger bei bedienten Domains werden angeschrieben, sodass der Hinweis nicht zu Backscatter werden kann, und höchstens einer je Empfänger und Nachricht.Ein Operator kann die Rotation über einen lebenden Schlüssel verweigern.
ACCEPT_ROTATION = expirednimmt den neueren Schlüssel nur an, wenn jeder gespeicherte als widerrufen oder abgelaufen verzeichnet ist oder das Ablaufdatum überschritten hat, das sein eigenes Material angibt (gespeichert, als der Schlüssel gelernt wurde, und bei OpenPGP immer nur nach hinten verschoben, sodass eine wiedereingespielte ältere Kopie des Zertifikats es nicht zurückdatieren kann); andernfalls wird das Angebot zurückgehalten, alskey.peer.rotate.heldprotokolliert und den Empfängern gemeldet. Das tauscht eine gefälschte Rotation gegen eine echte, die abgelehnt wird, bis ein Operator eingreift.Die Stufe bleibt eingegrenzt. Nichts davon erweitert, was ersetzt werden darf: Nichts, was eine Domain, ein Verzeichnis, ein Keyserver oder ein Operator veröffentlicht hat, und kein angehefteter Datensatz kann von einer Nachricht angetastet werden, und einer Adresse, für die wir eine Identität halten, greift unser eigener Schlüssel ohnehin vor. Einer bedienten Adresse, für die wir keine Identität halten, wird nicht vorgegriffen, und
pepsi-keys peer unclaimedlistet jede solche Adresse mit einem geernteten oder gegossipten Schlüssel auf (Die Benutzer finden, die ihren eigenen Schlüssel mitbringen).Beförderung ist keine Rotation. Wenn der Eigentümer eines gegossipten Schlüssels denselben Schlüssel selbst ankündigt, verschiebt
crypto_peer_key_upsertden Datensatz von der Sprossegossipauf die Sprosseinboundund schreibt einenkey.peer.promote-Datensatz (niemalskey.peer.rotateund kein Hinweis, da sich der Schlüssel nicht geändert hat). Das wird unter derselben Sperre je Adresse entschieden, anhand der Namen der Quellen, und führt zum selben UrteilTrustSource::Autocrypt/Untrustedwie jeder geerntete Schlüssel (Ein gegossipter Schlüssel, den sein Eigentümer bestätigt, wird befördert).
pepsi-status listet die letzten 30 Tage beider Arten von Datensätzen auf.
15.8.7. SSRF bei der Schlüsselentdeckung¶
Stützt
Ein entfernter Absender und Schlüssel von Korrespondenten: Eine Adresse, an die jemand schreibt, kann den Server nicht auf dessen eigenes Netz richten.
Die Schlüsselentdeckung ruft URLs ab, die aus einer E-Mail-Adresse abgeleitet sind — WKD und HTTP-Keyserver —, was konstruktionsbedingt ein Server-Side-Request-Forgery-Primitiv ist. pepsi-keydisc löst den Hostnamen selbst auf und lehnt jede Kandidatenadresse in Loopback-, RFC-1918-, RFC-6598-CGNAT-, Link-Local- oder Unique-Local-Raum ab und wendet die Prüfung bei jeder Umleitung erneut an (die Umleitung ist das Loch, das ein Entwurf „Adresse prüfen, dann verbinden“ offen lässt). Die Prüfung wird nur durch eine ausdrückliche Testnaht deaktiviert, weil ein Stub-HTTPS-Server notwendigerweise auf dem Loopback lauscht.
15.8.8. Das Secure-Link-Portal¶
Stützt
Wer eine Kopie der Datenbank hat und die Zusicherung über gespeicherte Nachrichten in Das Secure-Link-Ausweichportal.
Das Portal rendert vom Angreifer beeinflussten Inhalt (eine Betreffzeile, einen Absendernamen) in HTML und stellt ihn hinter eine PIN-Schranke. Die Entwurfsentscheidungen, auf die es ankommt:
die PIN wird nie gespeichert, in keiner Form. Sie ist eine Eingabe der Argon2id-Ableitung des Inhaltsschlüssels, sodass ein Angreifer mit der Datenbank einen Rateversuch nicht verifizieren kann, ohne auch den Server-Pfeffer zu haben;
der Link und die PIN reisen getrennt — der Link zum Empfänger, die PIN zum Absender, der sie über einen anderen Kanal weitergibt. Ein Postfach zu kompromittieren liefert weder die Nachricht noch einen Weg, sie zu bekommen;
falsche PINs werden gezählt und sperren die Nachricht für ein konfiguriertes Intervall, sodass die Online-Raterate begrenzt ist;
eine Antwort darf nur an den ursprünglichen Absender adressiert werden, der im Datensatz festgehalten ist. Das Portal ist keine Oberfläche zum Mailversand.
15.8.9. Die administrative API¶
Stützt
Ein delegierter Administrator und die Grenzen B6/B7.
Drei Authentifizierungsmechanismen, ein Identitätsmodell: SO_PEERCRED auf einem UNIX-Socket (unfälschbar, und so funktioniert der Bootstrap beim ersten Lauf, wenn es noch keine Konten gibt), Bearer-Tokens für Automatisierung (als Digest gespeichert, zeitkonstant verglichen) und Passwort plus Sitzungs-Cookie für Browser (Argon2id, HttpOnly und SameSite=Strict, CSRF-Token bei jeder verändernden Anfrage). Administrative Routen werden nur auf Listenern mit dem Kennzeichen ADMIN = yes eingehängt, sodass eine Relay-Installation, die nie einen solchen Listener aktiviert, die Oberfläche überhaupt nicht offenlegt.
Antworten enthalten nie Geheimnisse: GET /api/v1/config maskiert Optionen, die Zugangsdaten tragen, statt sie wegzulassen, sodass ein Operator sehen kann, dass ein Passwort gesetzt ist, ohne dass der Endpunkt zu einem Weg wird, eines zurückzulesen.
15.8.10. Feature-Telemetrie über die Konsole einschalten¶
Stützt
Schutzgüter (Privatsphäre gegenüber Dritten: nichts, dem der Operator nicht zugestimmt hat) und Eine kompromittierte Komponente (pepsi-httpd).
[pepsi] SHARE_TELEMETRY steht in der Konfigurationsdatei, daher ändert die Konsole es nur so, wie sie dort alles ändert: durch eine write-config-Anfrage, die der Applier von root ausführt. Was es sicher macht, dies anzubieten, ist eine Vorbedingung, die der Operator von Hand herstellt und die die Konsole nur beobachten kann: pepsi-telemetry-client muss laufen (ruhend, solange Telemetrie ausgeschaltet ist), und [pepsi] SYSTEM_ID muss vorhanden sein. Der Daemon meldet beides in der einzeiligen Tabelle pepsi.telemetry_client, die pepsi-httpd lesen, aber nicht schreiben darf — sodass eine Konsole „bereit“ nicht fälschen kann —, und der Datensatz trägt für die Kennung einen Wahrheitswert, niemals ihren Wert. Ohne das ist die Frage mit Hinweisen deaktiviert, und eine API-Anfrage zum Einschalten wird abgelehnt (409 telemetry_client_not_ready); das Ausschalten wird niemals abgelehnt.
Diese Sperre ist eine Aussage über Nützlichkeit, nicht die Grenze. Die Grenze ist der Daemon: Er glaubt nur der Datei (über den einzigen Leser des Schalters) und liest sie bei der Benachrichtigung telemetry_changed, bei SIGHUP und jede Minute neu ein; eine Benachrichtigung kann ihn nicht einschalten. Ein kompromittiertes pepsi-httpd, das CONFIG_DB hält, konnte bereits jede Konfiguration anfordern, diesen Schalter eingeschlossen, und daran ändert sich nichts; was es nicht kann, ist eine Installation, deren Operator den Daemon nie gestartet hat, irgendetwas übermitteln zu lassen. Nichts auf diesem Weg erzeugt eine Kennung, die die Installation nicht bereits hatte: Der Applier übernimmt stattdessen eine vorhandene SYSTEM_ID über ein Speichern in der Konsole hinweg, und pepsi-setup erzeugt eine nur bei einem Ja. Das Ausschalten verwirft, was der Daemon gesammelt und noch nicht gesendet hatte.
15.8.11. Ressourcengrenzen¶
Stützt
Verfügbarkeit: Ein Fremder kann den Server durch eine Nachricht, eine Verbindung oder eine Adresse nicht unbegrenzt belasten.
Die Grenzen selbst sind in Verfügbarkeit tabelliert; worauf es hier ankommt, ist, wo jede durchgesetzt wird und warum sie sich nicht umgehen lässt.
Verbindungen werden je Quelle begrenzt, nicht nur insgesamt.
pepsi-ingresszählt offene Verbindungen je Quellbereich (MAX_CONNECTIONS_PER_IP) und, für die UNIX-Socket-Gegenstellen, die von seinem Ratenlimit ausgenommen sind und nie verdrängt werden, für alle zusammen (MAX_LOCAL_CONNECTIONS);pepsi-httpdtut dasselbe je Client-Adresse. Die Prüfung erfolgt, bevor eine Verbindung einen Platz der globalen Obergrenze belegt, sodass eine Quelle, die ihren Anteil erreicht hat, sich nicht zusätzlich um weitere anstellen kann.Raten und Fluten werden über Sitzungen hinweg gezählt. Ein Token-Bucket je Bereich (
AUTH_FAILURES_PER_HOUR) wird von jedem fehlgeschlagenenAUTHverbraucht, und ist er leer, wird das nächsteAUTHabgelehnt, bevor das SASL-Backend gefragt wird; ein Bucket je uid, festgemacht an der vom Kernel gemeldetenSO_PEERCRED-uid (LOCAL_MESSAGES_PER_MINUTE), bemisst die lokale Einlieferung, und eine Sitzung darf nurMAX_MESSAGES_PER_SESSIONTransaktionen beginnen, bevor sie sich neu verbinden und damit wieder dem Ratenlimit stellen muss.Der Schub eines Absenders verzögert nicht alle anderen.
pepsi-dispatchübernimmt die Arbeit jeder Stage nach geringster virtueller Laufzeit je Absender statt nach ältestem zuerst (FAIR_SCHEDULING, standardmäßig eingeschaltet). Der Absender ist die generierte Spalteworkqueue.sender_key: bei einer Einlieferung das authentifizierte Konto, andernfalls die verbindende Adresse — eine IPv6-Gegenstelle anhand ihres /64, da einem einzelnen Host routinemäßig eines zugeteilt wird — und niemals die Umschlag-Domain von Mail von außen, die der Absender je Nachricht wählt. Worker-Zeit wird dem Absender angerechnet, der sie verursacht hat, ein Neuankömmling startet gleichauf mit dem am wenigsten bedienten Absender, sodass Stille kein Guthaben anspart, und ein reservierter Anteil nach ältestem zuerst (FAIR_FIFO_PERCENT) begrenzt die Wartezeit jeder einzelnen Nachricht. Die Buchführung liegt nur im Speicher des Dispatchers; sie gewährt nichts und ändert keine Berechtigung.Die eingehende Verifizierung hat eine Arbeitsgrenze, die kein Weg um DMARC herum ist. Höchstens
MAX_DKIM_SIGNATURESSignaturen werden verifiziert, diejenigen zuerst, die mit derFrom:-Domain übereinstimmen können, und SPF und jede Signatur laufen nebenläufig unterVERIFY_TIMEOUT, DMARC unter einer zweiten Frist. Eine Prüfung, der die Zeit ausgeht, wirdtemperrorfür diese Prüfung: DMARC berücksichtigt einen temporären Fehler nur bei einem Identifier, der mit derFrom:-Domain übereinstimmt, und das DNS, das DMARC selbst liest, ist das dieser Domain, sodass ein Fälscher, der sein eigenes DNS verlangsamt, nichts gewinnt als sein eigenestemperror.Schlüsselmaterial von Fremden hat eine Größenobergrenze. Jedes OpenPGP-Zertifikat, das von außen eintrifft — WKD, ein Keyserver, DANE, ein geernteter oder gegossipter Header, ein angehängter Schlüssel, ein eingefügter —, durchläuft einen Parser, der einen RSA-Schlüssel über 8192 Bit ablehnt, und der Verifizierer entfernt ein solches Zertifikat auch aus seinem Vertrauensspeicher. S/MIME hat dieselbe Grenze bei 4096 Bit, aus dem Crate
rsa.Die Aufbewahrung hängt nicht von der Web-Schicht ab. Die Audit- und Mail-Logs werden von
pepsi-httpd pruneauspepsi-log-prune.timerbereinigt — als Kontopepsi-httpd, die eine Dienstrolle, dieDELETEauf ihnen hält, was die Logs für alles, was Mail verarbeitet, nur anhängend hält —, und TLS-Report-Zähler und abgelaufene Datensätze des MX-Caches durch eigene Timer, alle vonpepsi.targetbenannt.Die Warteschlange hat eine Obergrenze, und die Prüfung lässt sich weder aushungern noch durch Aufteilen umgehen.
pepsi-ingresslehnt mit452 4.3.1beiRCPT,DATA/BDATund nach dem Ende der Daten ab, solange die Warteschlange bei[pepsi] MAX_QUEUE_ROWS/MAX_QUEUE_BYTESsteht oder das Dateisystem der Datenbank unterMIN_FREE_SPACEliegt. Die Messung (pepsi.queue_usage()) liest nur zwei generierte Größenspalten, sodass die Ingress-Rolle mit den geringsten Rechten dafür keinen Zugriff auf eingereihte Mail erhält, und sie wird je Prozess zwischengespeichert (QUEUE_CHECK_INTERVAL), sodass eine Flut vonRCPT-Befehlen keine Abfragen kostet. Bytes werden je gespeichertem Nachrichtentext gezählt: Nachrichtentexte liegen inpepsi.workqueue_bodyund werden von jedem Datensatz geteilt, den eine Aufteilung, ein Klon oder eine Listen-Auffächerung erzeugt, sodass eine große Nachricht an viele Empfänger ihren Nachrichtentext nicht mehr über die Warteschlange vervielfachen kann — die Verstärkung, die die Aufteilung je Empfänger früher bot. Der eine Verstärker, der noch unter der Kontrolle eines Absenders bleibt, ein Beitrag an eine große Liste, hält seine Auffächerung zurück (pepsi-stage-list-post,QUEUE_THROTTLE_DELAY), solange die Warteschlange voll ist. Das Löschen des letzten Datensatzes, der auf einen Nachrichtentext verweist, löscht diesen (ein Trigger), und der Fremdschlüssel bedeutet, dass keine Rolle — dasDELETEder Web-Schicht aufworkqueue_bodyeingeschlossen — einen Nachrichtentext löschen kann, den eine eingereihte Nachricht noch trägt.Mailinglisten begrenzen, wozu ein Absender sie bringen kann. Die angenommenen Beiträge eines Absenders an eine Liste werden je Stunde gezählt (
SENDER_POSTS_PER_HOUR), in einer SQL-Anweisung, die entscheidet und festhält, sodass parallele Worker nicht beide den letzten Platz nehmen können; der Überschuss wird zurückgehalten, statt mit der Mitgliederzahl vervielfacht zu werden. Die Moderationswarteschlange ist je Liste gedeckelt (MAX_HELD_MESSAGES), indem der neue Beitrag verworfen wird, sodass eine Flut die Beiträge, die ein Moderator noch nicht gesehen hat, nicht hinausspülen kann.Automatische Ausgaben haben eine Gesamtsumme, und sie lässt sich nicht durch Wettläufe aushebeln.
pepsi-stage-auto-paybucht eine Zahlungsforderung gegen zwei Budgets in dem einen Aufruf vonauto_pay_try_spend, der sie auch festhält: das der Nachricht (MAX_TOTAL, im Datensatzorigin_nonce, gesperrt mitFOR UPDATE) und die gleitenden 24 Stunden des zahlenden Wallets (MAX_TOTAL_PER_DAY, das Buchauto_pay_spend, unter einer transaktionsgebundenen Advisory-Sperre, die am Wallet festgemacht ist — Forderungen für verschiedene Nachrichten teilen keinen Datensatz, sodass ohne sie jede dieselbe Summe läse). Eine Ablehnung durch eines der beiden schreibt nichts in das andere; eine Zahlung, die sich nicht abwickeln lässt, wird beiden durchauto_pay_refundgutgeschrieben, das nur die erste Sperre nimmt, sodass sich die beiden nicht verklemmen können. Der Wallet-Schlüssel ist derjenige, aus dem der Helper zahlt (uid:<n>oder das gemeinsame Konto und die Wallet-Datenbank), sodass zwei Absender sich keinen Tag teilen können, es sei denn, sie teilen sich ein Wallet.Eine Zahlungsaufforderung antwortet einem Absender einmal je Zeitfenster. Die Aufforderung ist eine automatische Antwort an einen unverifizierten Umschlagabsender, daher beansprucht
pepsi-stage-anti-spamsie neben der Unterdrückung nach RFC 3834 überpayment_request_should_send—INSERT … ON CONFLICT DO UPDATE … WHERE, das in einer Anweisung entscheidet und festhält, wie esvacation_should_replytut —, festgemacht am geschützten Postfach und am Absender, mitBLOCK_RESPONSE_SUPPRESSals Zeitfenster. Schlägt das Beanspruchen fehl, unterbleibt die Antwort; die Nachricht wird so oder so zurückgehalten.
15.9. Wogegen Pepsi sich nicht verteidigt¶
Die Liste wird an einer Stelle geführt, Was Pepsi nicht schützt, zusammen mit den verbleibenden Risiken, die jeder Abschnitt über Angreifer benennt: root und ein böswilliger Operator, Verkehrsanalyse und Metadaten, Klartext in der Warteschlange, Signaturen gegenüber einer kompromittierten Pipeline, die eigene Kompromittierung von Korrespondenten und ein Netzwerkangreifer, der zusätzlich im DNS lügen kann.
15.10. Eine Schwachstelle melden¶
Bitte melden Sie Sicherheitsprobleme vertraulich per E-Mail an Christian Grothoff unter grothoff@gnunet.org, verschlüsselt mit dem unter https://grothoff.org/christian/grothoff.asc veröffentlichten OpenPGP-Schlüssel mit dem Fingerabdruck D842 3BCB 326C 7907 0339 29C7 939E 6BE1 E29F C3CC. Verwenden Sie für eine Schwachstelle nicht den öffentlichen Bugtracker, und lassen Sie vor der Offenlegung Zeit für eine Behebung. SECURITY.md im Quellbaum beschreibt die vollständige Richtlinie.
Gewöhnliche Fehler gehören in den öffentlichen Bugtracker unter https://bugs.gnunet.org/view_all_bug_page.php?project_id=35.
15.11. Siehe auch¶
Schlüsselverwaltung — Identitäten, Verwahrung, Entdeckung und die Vertrauensleiter. Das Secure-Link-Ausweichportal — das Portal in voller Ausführlichkeit. Die administrative API — die API-Oberfläche und ihre Geltungsbereiche. Interoperabilität mit Clients — was andere Clients tatsächlich lesen können, was wiederum begrenzt, welcher Schutz in der Praxis erreichbar ist. Testsuite — das Fuzzing und die adversarialen Korpora hinter den Parser-Aussagen.