85.1.45. pepsi-keydisc

find a correspondent’s public key or certificate

Section du manuel:

1

85.1.45.1.1. Nom

pepsi-keydisc - découverte de clés asynchrone, un service par méthode.

85.1.45.1.2. Synopsis

pepsi-keydisc [GLOBAL-OPTIONS] serve METHOD

pepsi-keydisc [GLOBAL-OPTIONS] probe ADDRESS

85.1.45.1.3. Description

pepsi-keydisc trouve la clé OpenPGP ou le certificat S/MIME d’un correspondant, note d’où il vient et ce que vaut cette source, et le met en cache dans pepsi.peer_key (voir pepsi-keys(1)). Trouver une clé est facile ; savoir s’il faut y croire est tout le problème : chaque résultat porte donc sa source et l’indication de si la consultation qui l’a produit était validée DNSSEC, et la politique de l’étape consommatrice décide de ce qui est suffisant.

La découverte est asynchrone et vit hors des étapes. Une étape qui a besoin d’une clé absente du cache ne fait jamais d’E/S réseau propre : elle valide le travail qu’elle peut conserver, met le message en pause et met une demande en file dans pepsi.key_request ; une instance de pepsi-keydisc répond à la demande et, dans le même aller-retour qui stocke la clé, libère chaque message garé sur cette adresse

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

Ce qui est validé au moment du garage relève de la décision de l’étape, non de ce programme. En particulier, pepsi-stage-decrypt(1) gare un message S/MIME avec le texte clair déchiffré validé — la signature imbriquée survit comme une couche MIME ordinaire — mais gare un message OpenPGP inchangé, parce qu’une signature OpenPGP ne vit qu’à l’intérieur du flux de paquets et que valider le texte clair détruirait la preuve même que la clé est allée chercher.

Cela supprime entièrement « un serveur de clés lent immobilise un worker d’étape » plutôt que de le borner. La demande est indexée sur l”adresse : cent messages vers un même correspondant coûtent donc une seule consultation et sont libérés ensemble. Une demande qui se conclut sans clé libère ceux qui l’attendent exactement comme une demande qui a trouvé une clé : les services trouvent les clés, les étapes décident du courrier.

Ce n’est pas une étape. Il se connecte à la base de données partagée via la section [pepsi-postgres] et se configure entièrement par [pepsi-keydiscovery] (voir pepsi.conf(5)).

85.1.45.1.4. Une instance par méthode

Il y a un binaire et une section de configuration, mais un processus par méthode de découverte, démarré depuis l’unité gabarit systemd pepsi-keydisc@.service :

pepsi-keydisc@dane

OPENPGPKEY DNS (RFC 7929) et SMIMEA (RFC 8162), sous une partie locale hachée. Les deux sont refusés sans le bit AD DNSSEC du résolveur.

pepsi-keydisc@wkd-advanced

Le Web Key Directory à openpgpkey.<domain>, sous /.well-known/openpgpkey/<domain>/hu/<hash>.

pepsi-keydisc@wkd-direct

Le Web Key Directory au domaine lui-même, sous /.well-known/openpgpkey/hu/<hash>.

pepsi-keydisc@vks

Les serveurs de clés vérificateurs de VKS_SERVERS, à /vks/v1/by-email/<address>.

pepsi-keydisc@ldap

L’annuaire LDAP configuré ; uniquement dans un build comportant la fonctionnalité ldap.

pepsi.target démarre les quatre premières, qui constituent les SOURCES par défaut.

C’est ce découpage qui procure l’isolation : un bind LDAP bloqué ne peut pas caler WKD, et désactiver une méthode se fait par systemctl mask --now pepsi-keydisc@vks (en la retirant aussi de SOURCES) plutôt que par le redémarrage de quelque chose de partagé. Les deux méthodes Web Key Directory sont des rangs distincts, et donc des instances distinctes qui s’exécutent simultanément — jamais une chaîne de repli, puisque n’exécuter la méthode directe que « si l’avancée échoue » accepterait silencieusement la réponse la plus faible chaque fois que la plus forte serait simplement lente.

C’est un écart délibéré au §3.1 de draft-koch-openpgp-webkey-service, qui exige que la méthode avancée soit essayée en premier et que la directe ne soit utilisée que là où le sous-domaine openpgpkey n’existe pas du tout. L’ordre du draft porte sur la confiance plutôt que sur la vitesse : le /.well-known/openpgpkey/ de l’apex est servi par ce qui fait tourner le site web du domaine, souvent une partie différente de son opérateur de courrier ; un domaine qui publie correctement sous openpgpkey.<domain> voit donc tout de même son apex consulté ici — et une réponse venue de là est stockée au rang wkd-direct, au-dessus de LDAP, de VKS et de tout ce qui est moissonné dans le courrier. Là où cela compte, SOURCES peut lister wkd-advanced sans wkd-direct.

Une instance dont la méthode n’apparaît pas dans [pepsi-keydiscovery] SOURCES refuse de démarrer, avec un message nommant ce que SOURCES liste effectivement. Ce n’est pas de la pédanterie : la règle d’arrêt à délai de grâce par rang n’attend que les méthodes nommées par SOURCES, de sorte qu’une instance non listée répondrait à une demande que personne n’attend.

Moissonner des clés dans le courrier entrant (parties application/pgp-keys, certificats de signataire S/MIME, en-tête Autocrypt:) n’est pas un service de découverte et n’a pas d’instance. Cela s’exécute en ligne là où le message se trouve déjà, dans pepsi-stage-autocrypt-learn(1), ne coûte aucune E/S réseau et écrit par le même chemin — de sorte qu’une clé moissonnée libère elle aussi tout ce qui est garé sur cette adresse. inbound est par conséquent refusé dans SOURCES.

Il en va de même de gossip, les clés que cette étape lit dans les champs Autocrypt-Gossip: à l’intérieur d’un message arrivé chiffré (Autocrypt Level 1 §5.3). C’est un second échelon en ligne plutôt qu’une variante d”inbound, parce que ces clés sont celles de tiers plutôt que celle de l’expéditeur, et que l’échelle doit pouvoir les distinguer.

Notez où se situe l”« en ligne » : pepsi-stage-decrypt(1) extrait les certificats que porte un message afin de vérifier sa signature, puis les abandonne — il ne stocke rien. Un pipeline sans étape pepsi-stage-autocrypt-learn n’apprend jamais aucune clé depuis le courrier, quelle que soit la quantité de matériel qui arrive, et aucune configuration de [stage-decrypt] n’y change quoi que ce soit.

85.1.45.1.5. Quelle clé l’emporte en cas de conflit

Une clé dont l”empreinte est déjà stockée pour la même adresse et le même protocole n’est pas du tout un conflit : la ligne est mise à jour sur place (sa source, sa validité, son expiration et sa date d’effet Autocrypt sont reportées, la source ne pouvant que s’améliorer) et la réponse est refreshed. Cela se produit avant chacun des tests ci-dessous, de sorte que revoir une clé épinglée rafraîchit tout de même ce que l’on sait d’elle.

Lorsqu’une clé découverte diffère de tout ce qui est stocké pour cette adresse et ce protocole, le magasin décide en une seule étape côté serveur, dans cet ordre :

  1. une ligne épinglée n’est jamais délogée, quoi qu’il arrive ;

  2. une source strictement mieux classée remplace tout ce qui est stocké — de sorte que WKD déloge une clé moissonnée, une entrée d’opérateur déloge WKD, et une clé apprise par gossip ne déloge rien du tout ;

  3. le plus récent l’emporte à l’intérieur du palier Autocrypt (ci-dessous) ;

  4. sinon la clé stockée est conservée, la nouvelle n’est pas stockée, et la réponse est rapportée comme rejected.

85.1.45.1.5.1. Le plus récent l’emporte dans le palier Autocrypt

La mise à jour de l’état des pairs d’Autocrypt Level 1 est le plus jeune l’emporte : un client stocke la clé du plus jeune message qu’il ait vu portant un en-tête Autocrypt:. La règle 2 seule ne peut exprimer cela — elle refuserait la deuxième clé Autocrypt jamais vue pour une adresse et garderait la première à jamais, de sorte qu’un correspondant qui réinstalle son client, change d’appareil ou renouvelle sa clé continuerait de recevoir du courrier chiffré vers une clé qu’il ne détient plus, ce qui est exactement l’échec du courrier illisible qu’Autocrypt existe pour éviter.

La règle 3 laisse donc une clé plus récente l’emporter, et elle est confinée de sorte qu’elle ne puisse jamais trancher qu’entre des prétentions de même nature. Elle s’applique lorsque toutes ces conditions sont réunies :

  • la source entrante est inbound ou gossip ; et

  • chaque clé actuellement stockée pour cette adresse et ce protocole est aussi inbound ou gossip — rien de ce qu’un propriétaire ou un opérateur a publié n’est jamais en jeu ; et

  • la source entrante se classe au moins aussi haut que la source stockée, de sorte qu”inbound remplace inbound et que gossip remplace gossip, mais que la présentation par un tiers ne puisse jamais retirer la ligne à ce que le propriétaire de l’adresse a dit de lui-même ; et

  • la date d’effet du message entrant est strictement plus récente que celle qui est stockée.

La date d’effet est l’en-tête Date: du message, plafonné de sorte qu’il ne puisse jamais être dans le futur, et l’heure de réception lorsque l’en-tête est absent ou inanalysable — la définition propre du Level 1. Des dates égales relèvent donc du premier arrivé (un message redistribué ne peut pas faire basculer la ligne), et une clé qui ne porte aucune date d’effet — toute méthode réseau — ne déloge jamais rien.

Le prix de la règle est ce qu’elle remet à un attaquant. La moisson est indexée sur l’en-tête From:, que l’expéditeur écrit, et l’authentification entrante est fail-open : un From: falsifié peut donc déloger une clé qui n’était elle-même jamais que de la confiance au premier usage. C’est le compromis propre à Autocrypt — il défend contre la collecte passive, non contre un attaquant actif sur le chemin — et il est borné des quatre côtés ci-dessus : rien de ce qui est publié par le domaine (WKD, DANE), par un serveur de clés, par un annuaire ou par l’opérateur ne peut être touché, ni une ligne épinglée. Un opérateur qui n’en veut pas règle [pepsi-keydiscovery] MIN_TRUST au-dessus du palier, ou épingle les clés qui comptent avec pepsi-keys peer pin (voir pepsi-keys(1)) ; notez qu’une adresse pour laquelle ce déploiement détient une identité reçoit sa réponse de cette identité et jamais du cache des pairs.

Un remplacement au titre de la règle 3 est rapporté comme rotated plutôt que replaced et écrit une ligne key.peer.rotate dans le journal d’audit dans la même instruction, de sorte qu’une rotation ne peut avoir lieu sans sa trace. Le seul appelant qui puisse en déclencher une — l’apprentissage entrant, pepsi-stage-autocrypt-learn(1) — envoie aussi un courrier aux destinataires locaux du message, et peut restreindre la règle avec ACCEPT_ROTATION = expired (chaque clé stockée doit être enregistrée comme révoquée ou expirée), auquel cas une rotation refusée est rapportée comme held et auditée comme key.peer.rotate.held. Les méthodes réseau n’atteignent jamais la règle 3.

La même empreinte revue est un rafraîchissement, non un conflit, et la ligne reprend la meilleure des deux sources. Un tel rafraîchissement est rapporté à part : une clé stockée depuis gossip que le propre message de son propriétaire (source inbound) porte désormais est rapportée comme promoted et auditée comme key.peer.promote, jamais comme une rotation — la promotion hors du gossip du §5.3 d’Autocrypt Level 1.

85.1.45.1.6. Commandes

serve METHOD

Exécute l’instance de service d’une méthode jusqu’à interruption. METHOD vaut dane, wkd-advanced, wkd-direct, vks ou ldap — le nom d’instance du gabarit systemd — et doit figurer dans [pepsi-keydiscovery] SOURCES.

L’instance fait un LISTEN sur le canal key_request, dont la charge utile est l’identifiant de demande, répond à chaque demande dont cette méthode est encore redevable, et effectue un balayage au démarrage et à chaque reconnexion du listener, de sorte que rien de ce qui a été soulevé en son absence ne soit perdu. Entre les notifications, elle cadence un court balayage de règlement : une fenêtre de grâce par rang est un instant, il faut donc que quelque chose regarde l’horloge, et un déploiement exécutant la moindre instance dispose donc de ce balayage. Une consultation isolée en échec est consignée sur la demande (ce qui sélectionne le court ERROR_TTL plutôt que le long NEGATIVE_TTL) et ne fait jamais tomber le listener.

probe ADDRESS

Exécute une fois l”intégralité de l’éventail pour une adresse et affiche ce que chaque méthode a trouvé — le protocole, l’empreinte, la taille, et dnssec pour une réponse DANE validée par AD — ou l’erreur rencontrée, ou qu’elle n’a rien trouvé.

Délibérément en lecture seule : il ne stocke rien, ni la clé ni une entrée de cache négatif. Un opérateur qui diagnostique « pourquoi ce correspondant n’a-t-il pas de clé » veut voir la réponse de chaque méthode, et non qu’un diagnostic écrive une entrée négative qui supprimerait ensuite la vraie consultation pendant une journée. Pour consulter une adresse et stocker le résultat, utilisez pepsi-keys peer refresh --address (voir pepsi-keys(1)).

Une adresse exclue par ALLOW_DOMAINS/DENY_DOMAINS est signalée comme telle et n’est pas interrogée.

85.1.45.1.7. Privilèges

Les services s’exécutent sous leur propre compte non privilégié, pepsi-keydisc — non l’utilisateur de service pepsi ni pepsi-crypto. Ce démon analyse du matériel de clé récupéré sur l’internet ouvert (un corps WKD hostile, une réponse de serveur de clés, un enregistrement DNS), ce qui en fait le processus le plus susceptible d’être compromis ici : pepsi-setup(1) lui donne donc le rôle de base de données le plus étroit du déploiement :

  • SELECT/INSERT/UPDATE/DELETE sur pepsi.peer_key — remplir le cache est tout son travail, et la règle de conflit doit pouvoir remplacer une ligne ;

  • SELECT/INSERT/UPDATE sur pepsi.key_request — la file de travail à laquelle il répond ;

  • EXECUTE sur les fonctions keydisc_* et les deux helpers crypto_* qu’elles appellent ;

  • sur pepsi.workqueue, SELECT plus un UPDATE (status, timeout) au niveau colonne — et rien d’autre.

Ces deux colonnes suffisent exactement à faire passer une ligne garée de paused à pending, ce qui se produit dans keydisc_resolve/keydisc_sweep. Avec un droit au niveau table, ce compte pourrait réécrire les destinataires d’un message ou son corps ; avec ces deux colonnes, il ne peut que réveiller un message. Il n’a aucune portée sur pepsi.crypto_identity, et donc aucune sur la moindre clé privée.

Le programme ne porte aucun bit setuid ou setgid et est plié dans le binaire multi-appel pepsi. Lancé en tant que root, il devient le compte pepsi-keydisc avant de se connecter (la configuration, et tout fragment @inline-secret@, est lue d’abord, en tant qu’utilisateur appelant), de sorte qu’il peut être lancé depuis un shell root pour des tests sans sudo -u.

85.1.45.1.8. LDAP est une fonctionnalité décidée à la compilation

Le client LDAP est derrière la fonctionnalité cargo ldap et est désactivé par défaut : il ajoute une dépendance, et un déploiement sans annuaire ne devrait pas en lier une. Un build sans la fonctionnalité refuse de démarrer pepsi-keydisc@ldap avec un message le disant, plutôt que de répondre silencieusement « pas de clé » à jamais, et pepsi-setup(1) rejette sur un tel build une configuration qui liste ldap dans SOURCES — comme il rejette celle qui liste ldap sans LDAP_URL et LDAP_BASE.

85.1.45.1.9. Options globales

Ces options globales précèdent la sous-commande (un indicateur placé à la fin est rejeté).

-c FILE, –config FILE

Lit la configuration depuis FILE au lieu de parcourir les emplacements par défaut.

-L LOGLEVEL, –log LOGLEVEL

Règle la verbosité de journalisation. LOGLEVEL est l’un de error, warn, info, debug ou trace (par défaut : info).

-v, –verbose

Affiche les messages de journal de toutes les sources, y compris les bibliothèques tierces.

-h, –help

Affiche un résumé d’utilisation et quitte.

-V, –version

Affiche la version et quitte.

85.1.45.1.10. Signaux

SIGINT, SIGTERM

Cesser de prendre de nouvelles demandes et quitter. Rien n’est perdu : une demande sans réponse reste pending dans pepsi.key_request et est reprise par le balayage de démarrage de l’instance suivante, ou se conclut d’elle-même à son échéance.

85.1.45.1.11. Code de sortie

0

Arrêt propre, ou un probe achevé.

1

Une erreur s’est produite : une configuration malformée, une méthode que [pepsi-keydiscovery] SOURCES ne liste pas, ldap dans un build sans la fonctionnalité, ou une connexion à la base de données échouée. Les échecs de consultation individuels ne sont pas des erreurs — ils sont consignés sur la demande et le service continue de tourner.

85.1.45.1.12. Exemples

pepsi.target démarre l’ensemble de méthodes par défaut (dane wkd-advanced wkd-direct vks). Sans la cible, démarrez-les à la main

systemctl enable --now pepsi-keydisc@dane pepsi-keydisc@wkd-advanced \
                       pepsi-keydisc@wkd-direct pepsi-keydisc@vks

Désactiver une méthode sans toucher à la configuration des autres

systemctl mask --now pepsi-keydisc@vks

(puis réglez [pepsi-keydiscovery] SOURCES = dane wkd-advanced wkd-direct, afin que la règle d’arrêt cesse d’attendre une réponse qui ne viendra jamais). mask plutôt que disable : pepsi.target redémarre une instance simplement désactivée.

Découvrir ce que chaque méthode dit d’une adresse, sans rien changer

pepsi-keydisc -c /etc/pepsi/pepsi.conf probe bob@example.com

Exécuter une instance au premier plan, avec la journalisation complète, pour voir pourquoi une adresse ne donne aucune clé

pepsi-keydisc -c /etc/pepsi/pepsi.conf -L debug serve wkd-advanced

85.1.45.1.13. Voir aussi

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

85.1.45.1.14. Bogues

Signalez les bogues au gestionnaire de tickets de Pepsi.