85.1.45. pepsi-keydisc¶
find a correspondent’s public key or certificate
- Handbuchabschnitt:
1
85.1.45.1.1. Name¶
pepsi-keydisc - asynchrone Schlüsselentdeckung, ein Dienst je Methode.
85.1.45.1.2. Übersicht¶
pepsi-keydisc [GLOBAL-OPTIONS] serve METHOD
pepsi-keydisc [GLOBAL-OPTIONS] probe ADDRESS
85.1.45.1.3. Beschreibung¶
pepsi-keydisc findet den OpenPGP-Schlüssel oder das S/MIME-Zertifikat eines Korrespondenten, hält fest, woher es kam und wie viel diese Quelle wert ist, und legt es in pepsi.peer_key ab (siehe pepsi-keys(1)). Einen Schlüssel zu finden ist leicht; zu wissen, ob man ihm glauben soll, ist das ganze Problem, daher trägt jedes Ergebnis seine Quelle und die Angabe, ob die erzeugende Abfrage DNSSEC-validiert war, und die Richtlinie der verbrauchenden Stage entscheidet, was gut genug ist.
Die Entdeckung ist asynchron und lebt außerhalb der Stages. Eine Stage, die einen Schlüssel braucht, den sie nicht im Cache hat, betreibt nie eigene Netzwerk-E/A: Sie committet, was sie behalten darf, pausiert die Nachricht und reiht eine Anfrage in pepsi.key_request ein; eine pepsi-keydisc-Instanz beantwortet die Anfrage und gibt im selben Round-Trip, in dem sie den Schlüssel speichert, jede an dieser Adresse geparkte Nachricht frei:
stage cache hit -> use the key
fresh negative -> the no-key path, at once
cache miss -> park: commit, pause, enqueue
pepsi-keydisc@... LISTEN key_request -> look up -> store the key AND
release every parked message
Was das Parken committet, ist die Entscheidung der Stage, nicht dieses Programms. Insbesondere parkt pepsi-stage-decrypt(1) eine S/MIME-Nachricht mit committetem entschlüsseltem Klartext — die geschachtelte Signatur überlebt als gewöhnliche MIME-Schicht —, parkt eine OpenPGP-Nachricht aber unverändert, weil eine OpenPGP-Signatur nur innerhalb des Paketstroms lebt und das Committen des Klartexts genau den Beleg zerstören würde, für dessen Prüfung der Schlüssel geholt wird.
Das beseitigt „ein langsamer Keyserver hält einen Stage-Worker fest“ vollständig, statt es zu begrenzen. Der Schlüssel der Anfrage ist die Adresse, sodass hundert Nachrichten an einen Korrespondenten eine Abfrage kosten und gemeinsam freigegeben werden. Eine Anfrage, die sich ohne Schlüssel erledigt, gibt ihre Wartenden genauso frei wie eine, die einen Schlüssel gefunden hat: Die Dienste finden Schlüssel, die Stages entscheiden über Mail.
Es ist keine Stage. Es verbindet sich mit der gemeinsamen Datenbank über den [pepsi-postgres]-Abschnitt und wird vollständig durch [pepsi-keydiscovery] konfiguriert (siehe pepsi.conf(5)).
85.1.45.1.4. Eine Instanz je Methode¶
Es gibt ein Binärprogramm und einen Konfigurationsabschnitt, aber einen Prozess je Entdeckungsmethode, gestartet aus der systemd-Vorlagen-Unit pepsi-keydisc@.service:
pepsi-keydisc@daneDNS-
OPENPGPKEY(RFC 7929) undSMIMEA(RFC 8162), unter einem gehashten Local-Part. Beide werden ohne das DNSSEC-AD-Bit des Resolvers abgelehnt.pepsi-keydisc@wkd-advancedDas Web Key Directory unter
openpgpkey.<domain>, unter/.well-known/openpgpkey/<domain>/hu/<hash>.pepsi-keydisc@wkd-directDas Web Key Directory an der Domain selbst, unter
/.well-known/openpgpkey/hu/<hash>.pepsi-keydisc@vksDie verifizierenden Keyserver aus
VKS_SERVERS, unter/vks/v1/by-email/<address>.pepsi-keydisc@ldapDas konfigurierte LDAP-Verzeichnis; nur in einem Build mit dem
ldap-Feature.
pepsi.target startet die ersten vier, die die Standard-SOURCES bilden.
Die Aufteilung ist es, die die Isolation erkauft: Ein hängendes LDAP-Bind kann WKD nicht aufhalten, und eine Methode abzuschalten ist systemctl mask --now pepsi-keydisc@vks (und ihr Streichen aus SOURCES) statt eines Neustarts von etwas Gemeinsamem. Die beiden Web-Key-Directory-Methoden sind getrennte Ränge, und daher laufen getrennte Instanzen gleichzeitig — nie eine Rückfallkette, denn „direct“ nur „falls advanced fehlschlägt“ auszuführen würde die schwächere Antwort stillschweigend immer dann annehmen, wenn die stärkere bloß langsam war.
Das ist eine bewusste Abweichung von draft-koch-openpgp-webkey-service §3.1, der verlangt, dass zuerst die advanced-Methode versucht wird und die direct-Methode nur dort verwendet wird, wo die Sub-Domain openpgpkey überhaupt nicht existiert. Bei der Reihenfolge des Entwurfs geht es um Vertrauen und nicht um Geschwindigkeit: Das Apex-/.well-known/openpgpkey/ wird von dem bedient, was die Website der Domain betreibt, oft eine andere Partei als ihr Mailbetreiber, sodass auch bei einer Domain, die korrekt unter openpgpkey.<domain> veröffentlicht, hier ihr Apex abgefragt wird — und eine Antwort von dort wird im Rang wkd-direct gespeichert, über LDAP, VKS und allem, was aus Mail geerntet wurde. Wo das zählt, kann SOURCES wkd-advanced ohne wkd-direct auflisten.
Eine Instanz, deren Methode nicht in [pepsi-keydiscovery] SOURCES erscheint, verweigert den Start, mit einer Meldung, die nennt, was SOURCES auflistet. Das ist keine Pedanterie: Die Rang-Gnadenfrist-Stoppregel wartet nur auf die von SOURCES benannten Methoden, sodass eine nicht gelistete Instanz eine Anfrage beantworten würde, auf die niemand wartet.
Schlüssel aus eingehender Mail zu ernten (application/pgp-keys-Teile, S/MIME-Signiererzertifikate, der Autocrypt:-Header) ist kein Entdeckungsdienst und hat keine Instanz. Es läuft inline dort, wo die Nachricht ohnehin ist, in pepsi-stage-autocrypt-learn(1), kostet keine Netzwerk-E/A und schreibt über denselben Pfad — sodass ein geernteter Schlüssel ebenfalls alles freigibt, was an dieser Adresse geparkt ist. inbound wird folglich in SOURCES abgelehnt.
Dasselbe gilt für gossip, die Schlüssel, die jene Stage aus den Autocrypt-Gossip:-Feldern in einer verschlüsselt angekommenen Nachricht liest (Autocrypt Level 1 §5.3). Es ist eine zweite Inline-Sprosse und keine Variante von inbound, weil diese Schlüssel Dritten gehören statt dem Absender selbst und die Leiter sie auseinanderhalten können muss.
Beachten Sie, wo „inline“ liegt: pepsi-stage-decrypt(1) entnimmt die Zertifikate, die eine Nachricht mitführt, um ihre Signatur zu prüfen, und verwirft sie dann — es speichert nichts. Eine Pipeline ohne eine pepsi-stage-autocrypt-learn-Stage lernt überhaupt nie einen Schlüssel aus Mail, wie viel Material auch eintrifft, und keine noch so ausgefeilte [stage-decrypt]-Konfiguration ändert daran etwas.
85.1.45.1.5. Welcher Schlüssel einen Konflikt gewinnt¶
Ein Schlüssel, dessen Fingerabdruck für dieselbe Adresse und dasselbe Protokoll bereits gespeichert ist, ist überhaupt kein Konflikt: Der Datensatz wird an Ort und Stelle aktualisiert (Quelle, Gültigkeit, Ablauf und Autocrypt-Stichtag werden fortgeschrieben, wobei sich die Quelle nur verbessern kann), und die Antwort lautet refreshed. Das geschieht vor jedem der folgenden Tests, sodass das Wiedersehen eines angehefteten Schlüssels weiterhin das über ihn Bekannte auffrischt.
Wenn ein entdeckter Schlüssel von allem abweicht, was für diese Adresse und dieses Protokoll gespeichert ist, entscheidet der Speicher in einem serverseitigen Schritt, in dieser Reihenfolge:
ein gepinnter Datensatz wird nie verdrängt, was auch immer eintrifft;
eine strikt besser eingestufte Quelle ersetzt alles Gespeicherte — sodass WKD einen geernteten Schlüssel verdrängt, ein Operatoreintrag WKD verdrängt und ein gegossipter Schlüssel überhaupt nichts verdrängt;
das Neueste gewinnt innerhalb der Autocrypt-Stufe (unten);
andernfalls wird der gespeicherte Schlüssel behalten und der neue nicht gespeichert, und die Antwort wird als
rejectedgemeldet.
85.1.45.1.5.1. Das Neueste gewinnt in der Autocrypt-Stufe¶
Die Peer-State-Aktualisierung von Autocrypt Level 1 ist jüngstes gewinnt: Ein Client speichert den Schlüssel aus der jüngsten Nachricht, die er mit einem Autocrypt:-Header gesehen hat. Regel 2 allein kann das nicht ausdrücken — sie würde den zweiten je für eine Adresse gesehenen Autocrypt-Schlüssel ablehnen und den ersten für immer behalten, sodass ein Korrespondent, der seinen Client neu installiert, das Gerät wechselt oder seinen Schlüssel rotiert, weiterhin Mail erhielte, die an einen Schlüssel verschlüsselt ist, den er nicht mehr besitzt — genau das Unlesbare-Mail-Versagen, das Autocrypt vermeiden soll.
Regel 3 lässt daher einen neueren Schlüssel gewinnen, und sie ist so eingegrenzt, dass sie immer nur zwischen Behauptungen derselben Art entscheiden kann. Sie gilt, wenn alles davon zutrifft:
die eingehende Quelle ist
inboundodergossip; undjeder derzeit für diese Adresse und dieses Protokoll gespeicherte Schlüssel ist ebenfalls
inboundodergossip— nichts, was ein Eigentümer oder ein Operator veröffentlicht hat, steht je auf dem Spiel; unddie eingehende Quelle rangiert mindestens so hoch wie die gespeicherte, sodass
inboundinboundersetzt undgossipgossipersetzt, aber die Vorstellung durch einen Dritten den Datensatz nie von dem wegnehmen kann, was der Eigentümer der Adresse über sich selbst gesagt hat; unddas Wirksamkeitsdatum der eingehenden Nachricht ist strikt neuer als das gespeicherte.
Das Wirksamkeitsdatum ist der Date:-Header der Nachricht, so gekappt, dass er nie in der Zukunft liegen kann, und der Empfangszeitpunkt, wenn der Header fehlt oder nicht parsbar ist — Level 1s eigene Definition. Bei gleichem Datum gewinnt daher das erste (eine erneut zugestellte Nachricht kann den Datensatz nicht umkippen), und ein Schlüssel, der überhaupt kein Wirksamkeitsdatum trägt — jede Netzwerkmethode —, verdrängt nie etwas.
Der Preis der Regel ist das, was sie einem Angreifer in die Hand gibt. Das Ernten ist auf den From:-Header geschlüsselt, den der Absender schreibt, und die eingehende Authentifizierung ist fail-open, sodass ein gefälschtes From: einen Schlüssel verdrängen kann, der selbst nur auf Vertrauen beim ersten Gebrauch beruhte. Das ist Autocrypts eigener Kompromiss — es verteidigt gegen passives Sammeln, nicht gegen einen aktiven Angreifer auf dem Pfad — und er ist oben nach allen vier Seiten begrenzt: Nichts, was die Domain (WKD, DANE), ein Keyserver, ein Verzeichnis oder der Operator veröffentlicht hat, kann angetastet werden, und ein gepinnter Datensatz ebenso wenig. Ein Operator, der das nicht will, setzt [pepsi-keydiscovery] MIN_TRUST über die Stufe oder pinnt die Schlüssel, auf die es ankommt, mit pepsi-keys peer pin (siehe pepsi-keys(1)); beachten Sie, dass eine Adresse, für die diese Installation eine Identität hält, aus dieser Identität beantwortet wird und überhaupt nie aus dem Peer-Cache.
Eine Ersetzung nach Regel 3 wird als rotated statt replaced gemeldet und schreibt in derselben Anweisung einen key.peer.rotate-Datensatz ins Audit-Log, sodass eine Rotation nicht ohne ihren Eintrag geschehen kann. Der einzige Aufrufer, der eine auslösen kann — das eingehende Lernen, pepsi-stage-autocrypt-learn(1) —, benachrichtigt außerdem die lokalen Empfänger der Nachricht per Mail und kann die Regel mit ACCEPT_ROTATION = expired einschränken (jeder gespeicherte Schlüssel muss als widerrufen oder abgelaufen festgehalten sein); dann wird eine abgelehnte Rotation als held gemeldet und als key.peer.rotate.held protokolliert. Die Netzwerkmethoden erreichen Regel 3 nie.
Derselbe Fingerabdruck, erneut gesehen, ist eine Auffrischung, kein Konflikt, und der Datensatz übernimmt die bessere der beiden Quellen. Eine solche Auffrischung wird gesondert gemeldet: Ein aus Gossip gespeicherter Schlüssel, den nun die eigene Nachricht seines Inhabers (Quelle inbound) trägt, wird als promoted gemeldet und als key.peer.promote protokolliert, nie als Rotation — die Beförderung aus dem Gossip nach Autocrypt Level 1 §5.3.
85.1.45.1.6. Befehle¶
- serve METHOD
Führt die Dienstinstanz einer Methode bis zur Unterbrechung aus. METHOD ist eines von
dane,wkd-advanced,wkd-direct,vksoderldap— der Instanzname der systemd-Vorlage — und muss in[pepsi-keydiscovery] SOURCESerscheinen.Die Instanz lauscht mit
LISTENauf demkey_request-Kanal, dessen Nutzlast die Anfrage-ID ist, beantwortet jede Anfrage, die diese Methode noch schuldet, und macht beim Start und bei jeder Wiederverbindung des Listeners einen Durchlauf, sodass nichts verloren geht, was während ihrer Abwesenheit aufkam. Zwischen den Benachrichtigungen tickt sie einen kurzen Settle-Durchlauf: Ein Rang-Gnadenfrist-Fenster ist ein Zeitpunkt, also muss etwas auf die Uhr sehen, und eine Installation, die überhaupt eine Instanz betreibt, hat daher den Durchlauf. Eine einzelne fehlgeschlagene Abfrage wird auf der Anfrage festgehalten (was das kurzeERROR_TTLstatt des langenNEGATIVE_TTLauswählt) und beendet nie den Listener.- probe ADDRESS
Führt den gesamten Fächer für eine Adresse einmal aus und gibt aus, was jede Methode gefunden hat — das Protokoll, den Fingerabdruck, die Größe und
dnssecbei einer AD-validierten DANE-Antwort — oder den Fehler, auf den sie stieß, oder dass sie nichts gefunden hat.Bewusst nur lesend: Es speichert nichts, weder den Schlüssel noch einen Negativ-Cache-Eintrag. Ein Operator, der „warum hat dieser Korrespondent keinen Schlüssel“ diagnostiziert, will die Antwort jeder Methode sehen und nicht, dass eine Diagnose einen negativen Eintrag schreibt, der dann die echte Abfrage einen Tag lang unterdrückt. Um eine Adresse nachzuschlagen und das Ergebnis zu speichern, verwenden Sie
pepsi-keys peer refresh --address(siehe pepsi-keys(1)).Eine durch
ALLOW_DOMAINS/DENY_DOMAINSausgeschlossene Adresse wird als solche gemeldet und nicht abgefragt.
85.1.45.1.7. Privilegien¶
Die Dienste laufen unter ihrem eigenen unprivilegierten Konto pepsi-keydisc — nicht dem Dienstbenutzer pepsi und nicht pepsi-crypto. Dieser Daemon parst Schlüsselmaterial, das aus dem offenen Internet geholt wurde (ein feindlicher WKD-Inhalt, eine Keyserver-Antwort, ein DNS-Eintrag), was ihn zum wahrscheinlichsten Prozess hier macht, in den eingebrochen wird, sodass pepsi-setup(1) ihm die engste Datenbankrolle der Installation gibt:
SELECT/INSERT/UPDATE/DELETEaufpepsi.peer_key— den Cache zu füllen ist seine ganze Aufgabe, und die Konfliktregel muss einen Datensatz ersetzen können;SELECT/INSERT/UPDATEaufpepsi.key_request— die Arbeitswarteschlange, die er beantwortet;EXECUTEauf denkeydisc_*-Funktionen und den beidencrypto_*-Helfern, die sie aufrufen;auf
pepsi.workqueueSELECTplus einUPDATE (status, timeout)auf Spaltenebene — und sonst nichts.
Diese beiden Spalten reichen genau aus, um einen geparkten Datensatz von paused zurück auf pending zu bewegen, was innerhalb von keydisc_resolve/keydisc_sweep geschieht. Mit einer Berechtigung auf Tabellenebene könnte dieses Konto die Empfänger oder den Nachrichtentext einer Nachricht umschreiben; mit diesen beiden Spalten kann es eine Nachricht nur aufwecken. Es hat überhaupt keinen Zugriff auf pepsi.crypto_identity und damit auf keinen privaten Schlüssel.
Das Programm trägt kein setuid- oder setgid-Bit und ist in das Multi-Call-pepsi-Binärprogramm eingefaltet. Als root gestartet wird es vor dem Verbinden zum Konto pepsi-keydisc (die Konfiguration und ein etwaiges @inline-secret@-Fragment werden zuerst als aufrufender Benutzer gelesen), sodass es zum Testen aus einer root-Shell ohne sudo -u gestartet werden kann.
85.1.45.1.8. LDAP ist ein Feature zur Kompilierzeit¶
Der LDAP-Client liegt hinter dem Cargo-Feature ldap und ist standardmäßig aus: Er fügt eine Abhängigkeit hinzu, und eine Installation ohne Verzeichnis sollte keine linken. Ein Build ohne das Feature verweigert den Start von pepsi-keydisc@ldap mit einer entsprechenden Meldung, statt für immer stillschweigend „kein Schlüssel“ zu antworten, und pepsi-setup(1) lehnt auf einem solchen Build eine Konfiguration ab, die ldap in SOURCES auflistet — ebenso wie eine, die ldap ohne LDAP_URL und LDAP_BASE auflistet.
85.1.45.1.9. Globale Optionen¶
Diese globalen Optionen stehen vor dem Unterbefehl (ein nachgestelltes Flag wird abgelehnt).
- -c FILE, –config FILE
Liest die Konfiguration aus FILE, statt die Standardorte zu durchsuchen.
- -L LOGLEVEL, –log LOGLEVEL
Setzt die Log-Ausführlichkeit. LOGLEVEL ist eines von
error,warn,info,debugodertrace(Standardwert:info).- -v, –verbose
Zeigt Log-Meldungen aus allen Quellen, einschließlich Drittanbieter-Bibliotheken.
- -h, –help
Gibt eine Verwendungsübersicht aus und beendet sich.
- -V, –version
Gibt die Version aus und beendet sich.
85.1.45.1.10. Signale¶
- SIGINT, SIGTERM
Nimmt keine neuen Anfragen mehr an und beendet sich. Nichts geht verloren: Eine unbeantwortete Anfrage bleibt
pendinginpepsi.key_requestund wird vom Start-Durchlauf der nächsten Instanz aufgenommen oder erledigt sich an ihrer eigenen Frist.
85.1.45.1.11. Exit-Status¶
- 0
Sauberes Herunterfahren oder ein abgeschlossenes probe.
- 1
Ein Fehler ist aufgetreten: eine fehlerhafte Konfiguration, eine Methode, die
[pepsi-keydiscovery] SOURCESnicht auflistet,ldapin einem Build ohne das Feature oder eine fehlgeschlagene Datenbankverbindung. Einzelne fehlgeschlagene Abfragen sind keine Fehler — sie werden auf der Anfrage festgehalten, und der Dienst läuft weiter.
85.1.45.1.12. Beispiele¶
pepsi.target startet die Standardmenge von Methoden (dane wkd-advanced wkd-direct vks). Ohne das Target starten Sie sie von Hand:
systemctl enable --now pepsi-keydisc@dane pepsi-keydisc@wkd-advanced \
pepsi-keydisc@wkd-direct pepsi-keydisc@vks
Eine Methode abschalten, ohne die Konfiguration der anderen anzufassen:
systemctl mask --now pepsi-keydisc@vks
(danach [pepsi-keydiscovery] SOURCES = dane wkd-advanced wkd-direct setzen, damit die Stoppregel aufhört, auf eine Antwort zu warten, die nie kommt). mask statt disable: pepsi.target startet eine bloß deaktivierte Instanz wieder.
Herausfinden, was jede Methode über eine Adresse sagt, ohne etwas zu ändern:
pepsi-keydisc -c /etc/pepsi/pepsi.conf probe bob@example.com
Eine Instanz im Vordergrund mit vollem Logging ausführen, um zu sehen, warum eine Adresse keinen Schlüssel liefert:
pepsi-keydisc -c /etc/pepsi/pepsi.conf -L debug serve wkd-advanced
85.1.45.1.13. Siehe auch¶
pepsi-keys(1), pepsi-setup(1), pepsi.conf(5), pepsi.state(7), pepsi-dispatch(1), pepsi-config(1)
85.1.45.1.14. Fehler¶
Melden Sie Fehler an den Pepsi-Issue-Tracker.