15. Modèle de sécurité¶
Ce chapitre dit contre quoi Pepsi se défend, contre quoi il ne se défend pas, et ce qu’un attaquant obtient de chacune des choses qu’il aurait pu prendre — il est donc aussi utile après une compromission qu’avant.
Pepsi est un MTA qui détient des clés privées et peut lire du courrier, ce qui le rend digne d’être attaqué. La conception pose sans cesse une question : lorsque ce composant est compromis, qu’est-ce qui reste hors de portée ? Là où la réponse est « rien », cela est dit ici plutôt que laissé à découvrir.
Les affirmations — quels actifs, contre quels adversaires, dans quel mode de garde — sont énoncées dans Modèle de menace ; ce chapitre expose les mécanismes qui les sous-tendent.
15.1. Ce que Pepsi présuppose¶
Si l’un de ces points est faux, les garanties ci-dessous ne tiennent pas :
L’hôte n’est pas déjà compromis. Rien ici ne défend contre root sur la machine. Root peut lire chaque clé, chaque secret et chaque message.
PostgreSQL authentifie par identifiants de pair sur une socket locale. Chaque composant se connecte sous son propre utilisateur système, et la base n’accorde à ce rôle que ce dont le composant a besoin. Un déploiement qui passe à l’authentification par mot de passe avec un compte partagé jette l’essentiel de Un attaquant qui a la base de données.
Le système d’exploitation applique la propriété des fichiers et le bit setuid. La frontière de garde des clés privées est un
GRANTde base de données fondé sur l’uid effectif ; un montagenosuidou un compte partagé la fait s’effondrer.Un résolveur DNS validant. Pepsi fait confiance au bit AD plutôt que de valider DNSSEC lui-même (voir pepsi-stage-relay-to-internet). Un résolveur menteur défait DANE et rétrograde MTA-STS en confiance au premier usage.
Le matériel de certificat TLS n’est pas fourni par l’attaquant. L’intégration certbot écrit dans
/etc/letsencrypt; un attaquant qui peut y écrire peut usurper le serveur.
15.2. La séparation des privilèges¶
Garantit
Un composant compromis (ce que chaque compte compromis atteint), Un utilisateur shell local (aucun rôle accessible depuis un compte ordinaire) et Quelqu’un qui détient une copie de la base de données (ce qu’un rôle de service peut écrire).
Pepsi n’est pas un seul programme. Chaque composant s’exécute sous son propre compte, et la séparation est appliquée par la base de données autant que par le système de fichiers.
Du côté de la base de données, deux sortes de lignes sont tracées, et il vaut la peine d’être exact sur les deux.
Le rôle du pipeline est large à dessein. pepsi (le dispatcher et chaque étape ordinaire) et pepsi-crypto détiennent un droit global unique : SELECT, INSERT, UPDATE et DELETE sur chaque table du schéma pepsi, l’usage de chaque séquence, EXECUTE sur chaque fonction, et la même chose en privilèges par défaut pour les objets créés ultérieurement. Chaque étape lit et écrit la file, et à elles toutes les étapes utilisent chaque table du pipeline, de sorte que le moindre privilège par étape exigerait un compte par étape ; la conséquence est énoncée dans Modèle de menace plutôt que dissimulée. Le droit est ensuite repris là où le pipeline n’a rien à faire :
crypto_identity:SELECT/INSERT/UPDATEuniquement sur les colonnes autres queprivate_wrapped(DELETEn’a pas de granularité par colonne et reste accordé) ; seulpepsi-cryptodétient cette colonne ;config_override:SELECTseulement — l’écrire revient au rôlepepsi-config(pepsi-httpdécrit via une connexion[pepsi-admin] CONFIG_DBqui s’authentifie sous ce rôle) ;setup_tasketsetup_task_log: rien ; seulpepsi-configpeut faireINSERTd’une tâche ;secure_messageetsecure_access:INSERT,DELETEetSELECTde chaque colonne saufciphertextsursecure_message;SELECTetDELETEsursecure_access;admin_account,admin_sessionetapi_token: rien ;event_logetmail_log: en ajout seul (niUPDATEniDELETE).
Les services qui ne sont pas le pipeline obtiennent ce qu’ils utilisent. Un audit de chaque instruction que chacun peut émettre — son propre crate et chaque bibliothèque qu’il lie, en suivant les appels vers les fonctions stockées — fixe leurs droits, et pepsi-setup refuse de terminer si PostgreSQL est en désaccord :
Rôle |
Accordé |
Notamment pas |
|---|---|---|
|
|
Tout autre message de la file, les réglages, les listes blanches, les clés, tout ce qui concerne une liste au-delà de son adresse. Un bogue d’analyse dans le processus qui écoute sur le port 25 atteint une seule table, pour y ajouter. |
|
Sa table |
Tout le reste, de sorte qu’un collecteur partageant une base de données avec un pipeline ne peut pas atteindre le courrier. |
|
Les tables d’identifiants d’administration, le portail, les tables de clés (pas |
Les en-têtes ou le corps d’un message en file — la console liste des enveloppes et n’a aucun chemin de lecture vers le contenu, et ne peut pas non plus faire pointer le |
Une table qu’ajoute un correctif de schéma ultérieur n’est à la portée de pepsi-ingress ou de pepsi-telemetry qu’une fois qu’un correctif la leur accorde : leurs privilèges par défaut sont révoqués eux aussi.
Les rôles extérieurs à cet ensemble sont plus étroits encore : pepsi-keydisc, pepsi-whitelist et pepsi-config ne détiennent chacun des droits que sur quelques tables nommées. pepsi-crypto détient le droit global plus private_wrapped. Le propriétaire du schéma, pepsi-owner, sous lequel pepsi-setup se connecte lui-même, détient tout par propriété.
Composant |
S’exécute sous |
Ce qu’il peut atteindre |
|---|---|---|
|
|
|
|
|
Le droit du pipeline ci-dessus : la file, les paramètres, les statistiques, la liste blanche, et les tables de lien sécurisé sans leur |
|
|
L’accès ordinaire au pipeline dont dispose chaque étape, plus le matériel de clé privée enveloppé et la clé de chiffrement de clés. Rien d’autre ne détient les deux : le propriétaire du schéma |
|
l’opérateur appelant |
Installé en |
|
|
Les tables du deuxième tableau ci-dessus — lecture et écriture, et non lecture pour l’essentiel : ses gestionnaires modifient directement le routage de la file et les métadonnées de clés. Aucun en-tête ni corps de message, aucun matériel de clé privée, aucun paramètre ni liste blanche ; aucune possibilité d’écrire la surcouche de configuration ni de mettre en file une tâche de configuration, sinon par sa connexion |
|
|
La file de demandes de découverte et |
|
utilisateur appelant (setuid) |
|
|
|
|
Le quota de boîte aux lettres tient la mesure et le refus séparés. Seul pepsi-helper-maildir-writer peut regarder à l’intérieur d’un Maildir (mode 0700 ; c’est le seul programme qui devient l’utilisateur), et seuls les membres de pepsi-maildir peuvent l’exécuter — un groupe dont pepsi-ingress ne fait délibérément pas partie et dont pepsi-setup avertit si quelqu’un l’y ajoute. Le processus qui écoute sur le port 25 peut donc lire une mesure prise par quelqu’un d’autre et refuser un destinataire sur cette base, et rien d’autre : pepsi-setup lui révoque l’accès en écriture à pepsi.mailbox_quota et lui laisse SELECT. Les deux routes vers la comptabilité lui sont fermées, et aucune de ces fermetures ne repose sur l’autre.
Les étapes cryptographiques sont setuid, non setgid : l’authentification par pair de PostgreSQL se fonde sur l”uid effectif, de sorte qu’une étape qui n’aurait gagné qu’un groupe se connecterait toujours en tant que pepsi et se verrait refuser la colonne de clés. Elle doit être pepsi-crypto.
15.2.1. Tous les programmes portant un bit¶
Garantit
Un utilisateur shell local : un compte local n’obtient aucun rôle Pepsi, et chaque helper n’agit qu’avec l’autorité de l’utilisateur cible (frontière B4).
Un binaire multi-appel ne peut pas porter un bit setuid ou setgid propre à chaque programme ; chacun de ceux-ci est donc installé comme exécutable à part entière plutôt que comme lien symbolique vers le binaire pepsi partagé (la liste faisant autorité est STANDALONE_BINARIES dans le Makefile ; les modes sont appliqués par make install et, sous Debian, par dpkg-statoverride depuis le postinst). Tout ce qui n’est pas listé ici est non privilégié.
Programme |
Propriétaire et mode |
Pourquoi |
|---|---|---|
|
|
Devient le destinataire pour écrire dans un |
|
|
Le bit setgid est le seul verrou sur l’exécution de ce helper ; le mode est donc |
|
|
Le même helper, afin qu’un balayage |
|
|
Abandonne entièrement ses privilèges pour devenir l’utilisateur avant de lire son |
|
|
Le même verrou, pour ce helper. |
|
|
Pilote le wallet sous le compte qui le possède. |
|
|
Le même verrou, pour ce helper. |
|
|
Lit les fichiers de jetons OAuth à accès restreint par groupe qu’écrit |
|
|
Exécutable par tous à dessein : tout utilisateur local peut gérer son propre espace de noms, et ce qui l’empêche de toucher à celui d’un autre est la règle interne au programme, non le mode du fichier. |
|
|
L’authentification par pair se fonde sur l’uid effectif : l’étape doit donc être le rôle auquel |
|
|
Voir le tableau ci-dessus. Un site peut le rendre setuid |
|
|
La moitié non privilégiée du couple |
15.3. La frontière de garde des clés privées¶
Garantit
Garde par la passerelle (custody = local, la clé de MTA) (les clés ne sont jamais divulguées à un lecteur passif ni à une étape ordinaire ; frontière B5).
Le matériel de clé privée réside dans crypto_identity.private_wrapped et est protégé deux fois :
Par droit. pepsi-setup accorde cette unique colonne au rôle pepsi-crypto et la révoque de tout compte de service, pepsi-httpd compris ; en dehors de pepsi-crypto, seul le propriétaire du schéma pepsi-owner peut la lire, par propriété. C’est au niveau colonne et c’est absolu : PostgreSQL refuse une instruction qui ne fait que nommer la colonne, fût-ce à l’intérieur d’un test IS NULL, de sorte qu’il n’y a aucun chemin de lecture à restreindre par la suite.
Note
Cette absoluité a une arête pratique. Une requête de listage ne peut pas rapporter « cette identité a-t-elle une moitié privée ? » sous la forme private_wrapped IS NOT NULL : c’est refusé pour tout rôle autre que pepsi-crypto, et l’erreur de PostgreSQL, permission denied for table crypto_identity, ne nomme aucune colonne. Le listage dérive l’indicateur de wrap_key_id à la place, qu’un CHECK de table maintient exactement en phase.
Par enveloppement. Ce qui est stocké n’est pas une clé privée mais un chiffré AEAD, lié au (address, protocol, purpose) de cette ligne en données associées, sous une clé de chiffrement de clés qui réside hors de la base, dans un fragment secrets.d/*.secret lisible du seul pepsi-crypto — et non de pepsi-owner, de sorte que l’unique autre rôle capable de lire la colonne ne détient toujours rien qu’il puisse ouvrir. Déplacer un bloc d’une ligne à une autre échoue à l’authentification.
15.4. Un second facteur pour la gestion des clés¶
Garantit
Quelqu’un qui détient le mot de passe de messagerie d’un utilisateur (un mot de passe de messagerie volé ne permet pas d’enregistrer une clé pour un utilisateur qui a enrôlé un second facteur).
Un utilisateur peut enrôler un secret TOTP (RFC 6238 : HMAC-SHA-1, six chiffres, pas de 30 secondes — ce qu’implémente toute application d’authentification) pour son adresse ; dès lors, chaque modification de ses clés qu”il peut effectuer exige un code en cours de validité :
Par e-mail, chaque commande de clé sauf
status—generate,register,publish,retireetotp enroll/otp removeeux-mêmes — prend le code sous la forme d’un mot de six chiffres dans leSubject:. L’en-têteAutocrypt:ou la clé jointe d’un message ordinaire n’enregistre plus rien pour cette adresse.Dans la console web et l’API, les requêtes
generate-identity,register-client-keyetrevoke-identityd’un principalown:<address>portent un champotp.pepsi-httpdne peut pas le vérifier — il ne détient aucun droit sur la table — et le transmet tel quel ;pepsi-setup applyle vérifie avant d’agir, pour une tâche admise au seul titre deown:<address>. Un opérateur détenantkeys:writen’est pas sollicité.Un
pepsi-keysnon privilégié (une installation setuid) exige--otp CODEpour chaque modification d’identité et chaque export de clé privée ; la propriété est vérifiée d’abord, de sorte qu’un utilisateur ne peut jamais faire compter des échecs contre l’enrôlement d’un autre.
Garde. Le secret est enveloppé exactement comme une clé privée — AES-256-GCM sous la KEK du magasin de clés, lié à (address, totp, second-factor) — et la table otp_key est accordée au seul rôle pepsi-crypto : pepsi-setup la révoque de tous les autres rôles, pepsi du pipeline compris, et le vérifie auprès de PostgreSQL. Une étape qui analyse du courrier hostile ne peut donc ni lire un secret, ni réinitialiser un compteur, ni supprimer un enrôlement (ce qui désactiverait la protection). pepsi-keys wrap rotate ré-enveloppe les secrets avec les clés privées.
Tentatives. Le code est vérifié par le processus capable de désenvelopper le secret ; la décision revient à la fonction SQL otp_attempt, sous le verrou de l’enregistrement. Un code correspondant à un pas de temps postérieur au dernier accepté est accepté et remet le compteur d’échecs à zéro ; le même code une seconde fois, ou un code plus ancien, est un rejeu et compte comme un échec, tout comme un code erroné. Au bout de dix échecs consécutifs, l’enrôlement est verrouillé et toute tentative est refusée sans vérification. Une commande sans aucun code est refusée sans être comptée. Chaque enrôlement porte un numéro de série jamais réutilisé, de sorte qu’une tentative vérifiée contre un secret remplacé entre-temps est écartée plutôt qu’imputée au nouveau.
L’opérateur lève un verrouillage en supprimant l’enrôlement (pepsi-keys otp reset, ou DELETE /api/v1/otp/{address}, qui met en file une tâche reset-otp que seul keys:write peut demander) ou en le remplaçant (pepsi-keys otp enroll exécuté par l’opérateur n’exige aucun code) ; un nouvel enrôlement commence toujours à zéro échec.
Ce qu’il ne fait pas. Il ne protège pas le premier enrôlement (il n’y a encore aucun code à demander), ni une réponse d’enrôlement que l’utilisateur a laissée dans sa boîte aux lettres. Il ne soumet pas à contrôle les modifications synchrones d’indicateurs de la console web (publication WKD, demande de téléversement vers un serveur de clés, clé MTA principale), que pepsi-httpd effectue lui-même. Et il n’apporte rien contre l’opérateur, qui peut le réinitialiser.
15.5. Un attaquant qui a la base de données¶
Garantit
Quelqu’un qui détient une copie de la base de données.
Supposons un vidage complet — une sauvegarde volée, une réplique, une injection SQL dans un chemin en lecture seule.
Ils obtiennent :
chaque message actuellement en file, en clair :
pepsi.workqueuecontient lesheadersetpepsi.workqueue_bodyle corps, non chiffrés. Pepsi est un pipeline ; un message en vol doit être lisible par l’étape qui le traitera. Les messages remis sont supprimés : il s’agit donc de l’arriéré, non de l’historique.le graphe complet de la correspondance : expéditeurs, destinataires, horodatages, paramètres par adresse, listes blanches, les tables
origin_nonce,auto_pay_spend,payment_request_replyetdns_address.chaque clé et certificat publics, ainsi que toutes les métadonnées de clés.
les verdicts ARC/DKIM/DMARC et les issues des sessions TLS.
Ils n’obtiennent pas :
la moindre clé privée, ni le moindre secret de second facteur. Les blocs enveloppés sont des chiffrés AEAD et la clé de chiffrement de clés n’est pas dans la base.
le moindre corps de message à lien sécurisé.
secure_message.ciphertextest en AES-256-GCM sous une clé dérivée par Argon2id du PIN du destinataire, d’un sel par message et d’un poivre côté serveur détenu dans la configuration, non dans la base. Le PIN lui-même n’est jamais stocké sous aucune forme — pas même sous forme de hachage. Un voleur de base doit forcer un PIN qu’il ne peut pas vérifier hors ligne sans disposer aussi du poivre.le moindre identifiant utilisable. Les mots de passe d’administration sont des empreintes Argon2id ; les jetons d’API sont stockés sous forme de condensats, de sorte qu’un jeton ne peut pas être récupéré depuis une sauvegarde et n’est affiché qu’une seule fois.
les clés de signature DKIM, qui résident dans le système de fichiers, et les secrets SRS et Pepsi-Origin, qui résident dans
secrets.d.
Avertissement
La file est l’exposition. Un opérateur qui juge importante la confidentialité du courrier en vol devrait chiffrer la base au repos et traiter les sauvegardes en conséquence — Pepsi ne peut pas chiffrer la file pour lui-même, car chaque étape aurait besoin de la clé, ce qui revient à ne pas en avoir.
15.6. Un attaquant qui a le fichier de configuration¶
Garantit
Quelqu’un qui détient la configuration ou un secret.
/etc/pepsi/pepsi.conf est en mode 0644 à dessein : des étapes non privilégiées le lisent. Il ne doit donc contenir aucun secret, et n’en contient pas — chaque secret réside dans un fragment secrets.d/<reader>.secret tiré par une directive @inline-secret@ et appartenant à l’unique compte qui en a besoin.
Un accès en lecture au fichier de configuration livre la topologie : noms d’hôtes, domaines servis, graphe du pipeline, adresses de smarthost, chemins de fichiers, fonctionnalités activées. C’est du renseignement, non une compromission.
Un accès en lecture à un fragment ``secrets.d`` livre exactement ce que détient son unique lecteur, et rien de plus — la clé SRS, ou le mot de passe du smarthost, ou la clé de chiffrement de clés, ou le poivre du lien sécurisé, ou la clé de désabonnement des listes. C’est pourquoi ils sont séparés : il n’existe aucun fichier unique dont la divulgation serait totale.
Un accès en écriture au fichier de configuration équivaut à root. Il nomme les programmes que chaque étape exécute. Traitez-le comme un fichier appartenant à root, car c’en est un.
Avertissement
Deux pièges autour d”@inline-secret@, qui tous deux ressemblent à une configuration qui fonctionne :
la directive termine sa section — tout ce qui est écrit après elle n’atterrit dans aucune section ;
un fragment illisible ne fait qu”avertir. L’option se lit alors comme non définie, et une fonctionnalité tourne silencieusement sans son secret. Vérifiez en faisant un
statdu fragment sous le compte lecteur, non en lisant le journal.
15.7. Un attaquant qui compromet un worker d’étape¶
Garantit
Un composant compromis, y compris son résidu en matière de signature.
Supposons qu’un bogue d’analyseur donne l’exécution de code à l’intérieur d’une étape — le cas réaliste, puisque analyser des entrées hostiles est le métier des étapes.
Une étape sous l’utilisateur ``pepsi`` (la plupart) obtient la file : lire, modifier, rediriger ou supprimer tout message en vol, et lire la liste blanche et les paramètres. Elle n’obtient pas les clés privées de bout en bout enveloppées, et ne peut pas devenir un autre compte.
Elle obtient cependant la possibilité de faire signer du courrier, et cela mérite d’être dit sans détour, car c’est le plus grand résidu de la conception :
pepsi-stage-dkim-signs’exécute souspepsiet lit les clés DKIM de domaine via le groupepepsi, de sorte que tout processuspepsipeut signer au nom de chaque domaine servi — directement, ou en modifiant un enregistrement en file avant que l’étape de signature ne le voie.pepsi-stage-encryptsigne au nom de l’auteurFrom:de l’enregistrement. Ingress appliqueUSERNAME_MAPà la submission, mais l’enregistrement en file est ensuite modifiable par chaque étapepepsi, et rien en aval ne peut savoir si ingress a pris cette décision. Une étapepepsicompromise peut donc réécrire leFrom:d’une submission en file (ou injecter un message avecworkqueue_inject) et la faire signer avec la clé de bout en bout de n’importe quel utilisateur local.
L’intégrité d’une signature produite par Pepsi — DKIM ou OpenPGP/S/MIME par utilisateur — n’est donc qu’aussi forte que l’ensemble du rôle de pipeline pepsi, et non aussi forte que pepsi-crypto. Ce que la séparation borne effectivement, c’est la divulgation des clés : un détecteur de langue compromis peut faire signer des messages tant qu’il s’exécute, mais il ne peut pas emporter une clé privée, de sorte que les dégâts cessent quand l’étape est corrigée et n’exigent pas de renouveler les clés. Voir Modèle de menace pour la place de ce point parmi les autres garanties.
Une étape ``pepsi-crypto`` obtient tout ce que possède ce compte : elle peut déchiffrer le courrier entrant adressé au déploiement et signer au nom de n’importe quelle identité locale. C’est la cible de plus grande valeur du système, raison pour laquelle les deux étapes cryptographiques sont petites, ne font aucune E/S réseau (la découverte est un programme distinct), et font l’objet du fuzzing décrit dans Suite de tests.
``pepsi-ingress`` voit chaque message à son arrivée et décide qui l’a soumis ; il peut donc admettre du courrier au nom de n’importe quel utilisateur local. Son rôle de base de données peut ajouter à la file sans rien en lire, de sorte que ce qui est déjà en file, les paramètres, les listes blanches et les clés restent hors de sa portée.
``pepsi-httpd`` obtient la surface web et ses droits en base — les enveloppes et le routage de la file, jamais les en-têtes ni le corps d’un message. Il ne peut notamment pas écrire de configuration ni exécuter d’étapes de configuration privilégiées : l’assistant du navigateur n’agit pas, il insère une ligne dans pepsi.setup_task décrivant ce qui devrait être vrai, et le pepsi-setup apply démarré séparément et tournant en root décide de le faire ou non. Une couche web compromise peut donc demander une reconfiguration mais non l’effectuer — et l’applicateur valide chaque tâche plutôt que de se fier à sa description.
Cette dernière défense peut être rendue absolue, ce qui est la bonne posture sur tout déploiement administré depuis un terminal. Les deux unités systemd de l’applicateur forment leur propre paquet Debian, pepsi-httpd-admin (pepsi ne fait que le Recommends, de sorte qu”apt remove pepsi-httpd-admin réussit et laisse le serveur de courrier tourner) ; une installation depuis les sources obtient le même levier avec make install INSTALL_ADMIN_UNITS=no. Une fois ces unités parties, rien ne vide pepsi.setup_task, de sorte qu’une insertion dedans est inerte quoi qu’il advienne de pepsi-httpd — la boucle est rompue au mécanisme plutôt qu’à l’interface, et aucun chemin de code de la couche web ne peut démarrer un processus root. La console le détecte, reste fermée si elle ne peut pas trancher, et sert ses pages de configuration initiale en lecture seule. pepsi-setup reste dans le paquet de base, donc administrer l’hôte à la main n’est pas affecté. Voir La scission comme levier de durcissement.
15.8. Défenses spécifiques¶
15.8.1. L’identité de submission¶
Garantit
Un utilisateur shell local et Un utilisateur limité au courrier (frontière B2) : un expéditeur soumettant du courrier n’envoie qu’au nom des identités que l’opérateur lui a associées.
Ingress décide qui a soumis un message par l’un de quatre moyens — SASL via le backend configuré, un certificat client TLS, une adresse dans MYNETWORKS, ou les identifiants SO_PEERCRED de la socket de submission locale, qui est la façon dont sendmail s’authentifie sans mot de passe et la raison pour laquelle cette socket peut être accessible en écriture à tous. Quel que soit le moyen, USERNAME_MAP décide ensuite quelles adresses From: et d’enveloppe cette identité peut utiliser (RFC 6409 §6/§8.1), et une identité associée à rien peut s’authentifier mais pas envoyer. Parler au nom d’une adresse — enregistrer une clé pour elle, ou gérer ses clés par e-mail — exige davantage que d’être autorisé à envoyer en son nom : le From: doit être exactement une boîte aux lettres nommée par une entrée concrète (sans joker) de la table de correspondance (state.origin.from_bound). Une autorisation *@example.org permet à un compte d’envoyer au nom de chaque collègue, jamais d’implanter une clé pour eux.
La décision est consignée dans le state du message (local_origin, l’identité authentifiée) et crue par chaque étape ultérieure — c’est précisément pourquoi Un composant compromis considère qu’une étape pepsi compromise est capable de la falsifier.
15.8.2. Rescellement du courrier déchiffré pour le stockage¶
Garantit
Garde par le client (custody = client, la clé de MUA) (le courrier que la passerelle a ouvert est classé lisible par le seul client de l’utilisateur) et Les garanties en un coup d’œil (la colonne Courrier classé).
pepsi-stage-decrypt doit produire du texte en clair : les étapes de spam, de langue, de liste blanche et d’apprentissage de clés qui le suivent lisent le contenu. pepsi-stage-reencrypt s’exécute une fois qu’elles l’ont fait, juste avant la remise locale, et scelle le message pour la clé custody = client active la plus récente du destinataire. Le mécanisme repose sur quatre choix :
Il ne requiert aucun privilège. Sceller pour une clé publique n’utilise aucun matériel privé, de sorte que l’étape s’exécute sous
pepsi, est intégrée au binaire multi-appel, et ne lit que les colonnes publiques decrypto_identitysous le droit ordinaire du pipeline. Elle ne nomme jamaisprivate_wrapped, et obtenir l’identitépepsi-cryptone lui apporterait rien qu’elle utilise.Il ne signe rien. Une signature de la passerelle dirait seulement « la passerelle a scellé ceci », indiscernable d’une signature d’auteur dans un client, et exigerait l’identité setuid. Le verdict sur la signature de l’expéditeur est celui que decrypt a consigné, dans
state.crypto.inet dansX-Pepsi-Crypto— qui reste sur le bloc extérieur et que decrypt retire de chaque message entrant avant d’écrire le sien, de sorte qu’il est toujours le nôtre quand le client le lit.Il n’agit que sur ce que decrypt a ouvert (
state.crypto.inindique chiffré et déchiffré), et consignestate.crypto.store, ce qui l’empêche aussi de jamais sceller un enregistrement deux fois. Le courrier arrivé en clair n’est pas scellé.Un refus ne renvoie rien de ce qui était chiffré. Sous
ON_NO_CLIENT_KEY = bounce, le bloc d’en-têtes du DSN est réduit aux champs d’adressage, le corps est vidé etRET=HDRSest forcé, puisque l’expéditeur a chiffré le message et qu’un DSN circule en clair. Il fait rebondir le message plutôt que de le différer, car différer garderait le texte en clair plus longtemps en file.
Le résidu est celui que Un composant compromis énonce déjà : une étape pepsi compromise — l’étape de rechiffrement comprise — voit le texte en clair tant qu’il est en file et peut faire passer un message à côté de l’étape ; la garantie porte donc sur les copies déjà classées, que rien sur le serveur ne peut ouvrir.
15.8.3. Ce dont une ancre de confiance S/MIME se porte garante¶
Garantit
Ce que signifie un indicateur de sécurité, face à Un expéditeur distant : une autorité de certification à laquelle l’opérateur fait confiance pour un espace de noms ne peut pas faire apparaître valid pour un autre.
Une organisation qui échange du courrier signé avec un partenaire ne fait souvent confiance à l’autorité de certification du partenaire que pour le domaine propre de celui-ci — en la certifiant croisée avec des nameConstraints, ou en ancrant une sous-autorité du partenaire qui les porte. Si cette limite était ignorée, cette autorité partenaire pourrait émettre un certificat pour l’un des utilisateurs de ce déploiement et l’étape de déchiffrement déclarerait la signature valid. pepsi-crypto::trust fait donc courir l’état de contraintes de la RFC 5280 §6.1 le long de chaque chaîne qu’il construit :
Les contraintes de noms s’appliquent au DN du sujet, à chaque nom alternatif du sujet et à chaque attribut
emailAddressdu DN du sujet — ce dernier toujours, car cet attribut est ce à quoi un message est lié quand le SAN ne nomme aucune boîte aux lettres. Les sous-arbres permis s’intersectent le long de la chaîne (deux autorités permettant des domaines disjoints ne laissent aucune boîte aux lettres permise, et non toutes) ; les sous-arbres exclus s’accumulent. Une sous-autorité ne peut pas élargir ce que son parent lui a délégué.Les propres contraintes de l’ancre la lient (RFC 5937), de sorte qu’ancrer directement la sous-autorité du partenaire est aussi sûr que de la certifier croisée.
Les contraintes de politique (
requireExplicitPolicy,inhibitPolicyMapping,inhibitAnyPolicy) sont évaluées sur l’arbre de politiques complet, sous une forme dédupliquée dont la taille est polynomiale dans les politiques que nomme un certificat, et plafonnée (64 politiques ou correspondances par certificat) — l’arbre qui croît exponentiellement dans l’algorithme naïf est le déni de service CVE-2023-0464 d’OpenSSL. La vérification des noms est plafonnée à 220 comparaisons par certificat pour la même raison.Ce qui ne peut être respecté est refusé. Une contrainte non traitable — une forme de sous-arbre que Pepsi ne sait pas comparer, lorsqu’un nom de cette forme apparaît en dessous ; un
minimumou unmaximum; une extension indécodable, critique ou non — rend la chaînepolicy-rejectedplutôt que valide sans la limite. Là où Pepsi s’écarte de la RFC 5280 ou d’OpenSSL, c’est toujours dans ce sens : undNSNamejoker pouvant désigner un hôte exclu est refusé, et une URI sans hôte sous une contrainte d’URI est refusée.
La suite de conformité NIST PKITS (sections 4.6 et 4.8–4.13, avec chaque réglage d’entrée qu’elle liste) s’exécute dans cargo test, aux côtés d’un corpus propre pour les formes que PKITS ne couvre pas (adresses IP, formes non implémentées, une ancre à contraintes de noms).
15.8.4. Falsification d’en-têtes¶
Garantit
Ce que signifie un indicateur de sécurité, face à Un expéditeur distant.
X-Pepsi-Crypto indique au client d’un utilisateur ce que Pepsi a conclu sur la cryptographie d’un message : un en-tête falsifié est donc un mensonge dit avec notre autorité. Le courrier entrant se voit retirer l’en-tête avant que l’étape de déchiffrement puisse ajouter le sien, et les en-têtes de demande sortants sont retirés par STRIP_REQUEST_HEADERS. Un expéditeur distant ne peut pas en faire apparaître un.
Le même raisonnement couvre Authentication-Results et Pepsi-Origin : un en-tête qui signifie « le système local a vérifié ceci » n’a de sens que si le système local est la seule chose qui puisse l’écrire.
15.8.5. Destinataires inconnus et backscatter¶
Garantit
Actifs (identité d’envoi et réputation), contre Un expéditeur distant.
Un rebond va à l’expéditeur d’enveloppe que revendique un message, quel qu’il soit, et le spam le falsifie. Un MTA qui accepte du courrier pour des adresses qu’il n’a pas et le fait rebondir plus tard écrit donc à des inconnus pour le compte d’un spammeur. C’est le backscatter, et les listes noires réagissent vite. Pepsi y remédie en trois couches, chacune servant de filet sous la précédente :
Refuser au RCPT.
pepsi-ingressrépond550 5.1.1pour une adresse d’un domaine servi dont rien sur l’hôte ne se porte garant : un compte local, un alias, une liste, une adresse de service, ou le MDA ou le backend interrogé avecRCPT TO. Les sources sont déduites du graphe d’étapes (pepsi-recipients). Toute incertitude accepte, de sorte qu’une panne ne se transforme jamais en courrier refusé.Ne faire de rapport qu’à un expéditeur qui a fait ses preuves.
pepsi-stage-bouncen’envoie un DSN pour un message entrant que lorsque SPF a réussi pour le domaine de l’expéditeur d’enveloppe ou qu’une signature DKIM alignée a réussi (BOUNCE_UNAUTHENTICATED = drop, la valeur par défaut). Tout le reste est supprimé et journalisé.Refuser une boucle. Un message qui est déjà passé
MAX_SELF_HOPSfois par l’ingress de cet hôte est refusé comme boucle de routage. Une adresse de notre propre domaine relayée vers notre propre MX coûte donc un seul rebond, et non un tour du pipeline pour chaque saut que les étapes de relais autorisent.
Répondre honnêtement au RCPT indique à un expéditeur quelles adresses existent, comme le fait tout MTA qui rejette les utilisateurs inconnus. MAX_INVALID_RECIPIENTS ferme une session après dix essais erronés, si bien que la collecte coûte des connexions, que mesurent les limites de connexion. Les sondes du MDA ou du backend sont mises en cache et plafonnées à 16 en cours (au-delà, la réponse est « incertain », ce qui accepte). Un flot d’essais ne peut donc pas non plus être retourné contre le stockage du courrier. VRFY répond toujours 252 pour tout.
Le contrôle des destinataires exige un droit que pepsi-ingress n’avait pas : les colonnes de nom et d’hôte de mailing_list, qui forment ensemble l’adresse publique d’une liste et rien de plus.
15.8.6. Le changement de clé d’un correspondant¶
Garantit
Clés des correspondants, face à Un expéditeur distant : une clé récoltée peut être remplacée par une plus récente, mais jamais silencieusement.
Au sein du palier Autocrypt (les échelons inbound et gossip), la règle de conflit est celle d’Autocrypt Level 1, la plus récente l’emporte, fondée sur l’en-tête From: qu’écrit un expéditeur et sur un message que l’authentification entrante a accepté en fail-open. Un message qui ne fait que prétendre venir d’un correspondant peut donc remplacer la clé avec laquelle nous lui chiffrons. Les mécanismes qui bornent cela :
Le remplacement est consigné par l’instruction qui l’effectue.
crypto_peer_key_upsertécrit un enregistrementkey.peer.rotatedanspepsi.event_log(le journal d’audit en ajout seul qu’aucun rôle traitant le courrier ne peutUPDATEniDELETE) dans la même transaction que l’échange, en nommant l’adresse, les deux ensembles d’empreintes et les deux dates d’effet. Aucun appelant ne peut faire tourner une clé sans cet enregistrement, et une étape compromise qui voudrait en cacher un devrait être le genre d’adversaire auquel Un composant compromis concède déjà la file.Les personnes sur le point de répondre sont prévenues.
pepsi-stage-autocrypt-learnécrit à chaque destinataire d’enveloppe local du message de rotation (RESPONSE_STAGE, le modèlekey-rotation) en indiquant les deux empreintes, de sorte qu’une rotation falsifiée est quelque chose que l’utilisateur visé peut remarquer avant d’envoyer quoi que ce soit de confidentiel. Seuls les destinataires des domaines servis sont avertis, de sorte que l’avis ne peut pas être détourné en backscatter, et au plus une fois par destinataire et par message.Un opérateur peut refuser la rotation d’une clé encore valide.
ACCEPT_ROTATION = expiredn’accepte la clé plus récente que lorsque chaque clé stockée est consignée comme révoquée ou expirée, ou a dépassé l’expiration qu’énonce son propre matériel (stockée lorsque la clé a été apprise et, pour OpenPGP, uniquement reportée à plus tard, de sorte qu’une copie plus ancienne rejouée du certificat ne peut pas l’antidater) ; sinon l’offre est retenue, auditée commekey.peer.rotate.heldet signalée aux destinataires. On échange ainsi une rotation falsifiée contre une rotation authentique refusée jusqu’à ce qu’un opérateur intervienne.Le palier reste confiné. Rien de tout cela n’élargit ce qui peut être remplacé : rien de ce qu’un domaine, un annuaire, un serveur de clés ou un opérateur a publié, ni aucun enregistrement épinglé, ne peut être touché par un message, et une adresse pour laquelle nous détenons une identité est de toute façon préemptée par notre propre clé. Une adresse servie pour laquelle nous ne détenons aucune identité n’est pas préemptée, et
pepsi-keys peer unclaimedliste chacune de ces adresses ayant une clé récoltée ou issue de gossip (Repérer les utilisateurs qui apportent leur propre clé).Une promotion n’est pas une rotation. Lorsque le propriétaire d’une clé issue de gossip annonce lui-même la même clé,
crypto_peer_key_upsertfait passer l’enregistrement de l’échelongossipà l’écheloninboundet écrit un enregistrementkey.peer.promote(jamaiskey.peer.rotate, et sans avis, puisque la clé n’a pas changé). La décision est prise sous le même verrou par adresse, fondée sur les noms de source, et correspond au même verdictTrustSource::Autocrypt/Untrustedque toute clé récoltée (Une clé issue de gossip que son propriétaire confirme est promue).
pepsi-status liste les 30 derniers jours des deux sortes d’enregistrements.
15.8.7. SSRF de la découverte de clés¶
Garantit
Un expéditeur distant et Clés des correspondants : une adresse à laquelle quelqu’un écrit ne peut pas diriger le serveur vers son propre réseau.
La découverte de clés récupère des URL dérivées d’une adresse e-mail — WKD, et les serveurs de clés HTTP — ce qui est par construction une primitive de falsification de requête côté serveur. pepsi-keydisc résout lui-même le nom d’hôte et refuse toute adresse candidate située en boucle locale, en RFC 1918, en CGNAT RFC 6598, en lien-local ou en unique-local, et réapplique la vérification à chaque redirection (la redirection est le trou que laisse ouvert une conception « vérifier l’adresse puis se connecter »). La vérification n’est désactivée que par une couture de test explicite, car un serveur HTTPS bouchon écoute nécessairement sur la boucle locale.
15.8.8. Le portail de lien sécurisé¶
Garantit
Quelqu’un qui détient une copie de la base de données et l’affirmation sur les messages stockés de Le portail de repli par lien sécurisé.
Le portail rend en HTML du contenu influencé par l’attaquant (une ligne de sujet, un nom d’expéditeur) et le conditionne à un PIN. Les décisions de conception qui comptent :
le PIN n’est jamais stocké, sous aucune forme. C’est une entrée de la dérivation Argon2id de la clé de contenu, de sorte qu’un attaquant disposant de la base ne peut pas vérifier une devinette sans disposer aussi du poivre du serveur ;
le lien et le PIN voyagent séparément — le lien vers le destinataire, le PIN vers l’expéditeur pour qu’il le transmette par un autre canal. Compromettre une boîte aux lettres ne livre ni le message ni un moyen de l’obtenir ;
les PIN erronés sont comptés et verrouillent le message pour un intervalle configuré, de sorte que la cadence de devinettes en ligne est bornée ;
une réponse ne peut être adressée qu’à l’expéditeur d’origine, consigné dans la ligne. Le portail n’est pas une surface d’envoi de courrier.
15.8.9. L’API d’administration¶
Garantit
Un administrateur délégué et les frontières B6/B7.
Trois mécanismes d’authentification, un seul modèle d’identité : SO_PEERCRED sur une socket UNIX (infalsifiable, et c’est ainsi que fonctionne l’amorçage au premier démarrage lorsqu’il n’existe encore aucun compte), des jetons porteurs pour l’automatisation (stockés en condensat, comparés en temps constant), et un mot de passe plus un cookie de session pour les navigateurs (Argon2id, HttpOnly et SameSite=Strict, jeton CSRF sur chaque demande modifiante). Les routes d’administration ne se montent que sur des listeners marqués ADMIN = yes, de sorte qu’une installation de relais qui n’active jamais un tel listener n’expose pas du tout cette surface.
Les réponses ne contiennent jamais de secrets : GET /api/v1/config masque les options porteuses d’identifiants plutôt que de les omettre, de sorte qu’un opérateur puisse voir qu’un mot de passe est défini sans que le point de terminaison devienne un moyen d’en relire un.
15.8.10. Activer la télémétrie des fonctionnalités depuis la console¶
Garantit
Actifs (confidentialité envers les tiers : rien à quoi l’opérateur n’a pas consenti) et Un composant compromis (pepsi-httpd).
[pepsi] SHARE_TELEMETRY réside dans le fichier de configuration, de sorte que la console ne le modifie que comme elle y modifie toute chose : par une requête write-config qu’exécute l’applicateur de root. Ce qui rend cette offre sûre est une précondition que l’opérateur met en place à la main et que la console ne peut qu”observer : pepsi-telemetry-client doit être en cours d’exécution (en sommeil tant que la télémétrie est désactivée) et [pepsi] SYSTEM_ID doit être présent. Le démon rapporte les deux dans la table à un seul enregistrement pepsi.telemetry_client, que pepsi-httpd peut lire mais pas écrire — une console ne peut donc pas falsifier « prêt » — et l’enregistrement porte un booléen pour l’identifiant, jamais sa valeur. En son absence, la question est désactivée avec des indications et une requête d’API visant l’activation est refusée (409 telemetry_client_not_ready) ; la désactivation n’est jamais refusée.
Ce contrôle est une question d’utilité, pas la frontière. La frontière, c’est le démon : il ne croit que le fichier (par l’intermédiaire de l’unique lecteur de l’interrupteur), qu’il relit sur la notification telemetry_changed, sur SIGHUP et chaque minute ; une notification ne peut pas l’activer. Un pepsi-httpd compromis détenant CONFIG_DB pouvait déjà demander n’importe quelle configuration, cet interrupteur compris, et cela ne change pas ; ce qu’il ne peut pas faire, c’est amener un déploiement dont l’opérateur n’a jamais démarré le démon à soumettre quoi que ce soit. Rien sur ce chemin ne crée un identifiant que l’installation n’avait pas déjà : l’applicateur reporte au contraire un SYSTEM_ID existant d’un enregistrement de la console au suivant, et pepsi-setup n’en génère un que sur un oui. La désactivation écarte ce que le démon avait recueilli sans l’avoir encore envoyé.
15.8.11. Limites de ressources¶
Garantit
Disponibilité : un inconnu ne peut pas faire coûter au serveur sans limite un message, une connexion ou une adresse.
Les limites elles-mêmes sont présentées en tableau dans Disponibilité ; ce qui compte ici, c’est l’endroit où chacune est appliquée et la raison pour laquelle on ne peut pas la contourner.
Les connexions sont bornées par source, et pas seulement au total.
pepsi-ingresscompte les connexions ouvertes par plage de sources (MAX_CONNECTIONS_PER_IP) et, pour les pairs sur socket UNIX qui sont exemptés de sa limitation de débit et jamais évincés, pour l’ensemble d’entre eux (MAX_LOCAL_CONNECTIONS) ;pepsi-httpdfait de même par adresse client. Le contrôle a lieu avant qu’une connexion prenne une place sous le plafond global, de sorte qu’une source ayant atteint sa part ne peut pas en plus faire la queue pour davantage.Les essais de mots de passe et les inondations sont comptés d’une session à l’autre. Un seau à jetons par plage (
AUTH_FAILURES_PER_HOUR) est consommé par chaqueAUTHéchoué, et une fois vide, leAUTHsuivant est refusé avant que le backend SASL soit interrogé ; un seau par uid, fondé sur l’uidSO_PEERCREDrapporté par le noyau (LOCAL_MESSAGES_PER_MINUTE), mesure la submission locale, et une session ne peut entamer queMAX_MESSAGES_PER_SESSIONtransactions avant de devoir se reconnecter et repasser par la limitation de débit.La rafale d’un expéditeur ne retarde pas tous les autres.
pepsi-dispatchprend en charge le travail de chaque étape selon le plus petit temps d’exécution virtuel par expéditeur plutôt que le plus ancien d’abord (FAIR_SCHEDULING, activé par défaut). L’expéditeur est la colonne généréeworkqueue.sender_key: le compte authentifié pour une submission, sinon l’adresse qui se connecte — un pair IPv6 par son /64, puisqu’un seul hôte en reçoit couramment un — et jamais le domaine d’enveloppe du courrier venant de l’extérieur, que l’expéditeur choisit à chaque message. Le temps de worker est imputé à l’expéditeur qui l’a occasionné, un nouveau venu part à égalité avec l’expéditeur le moins servi, de sorte que le silence n’accumule aucun crédit, et une part réservée au plus ancien d’abord (FAIR_FIFO_PERCENT) borne l’attente de tout message. La comptabilité réside uniquement dans la mémoire du dispatcher ; elle n’accorde rien et ne modifie aucun droit.La vérification entrante a une borne de travail qui n’est pas un moyen de contourner DMARC. Au plus
MAX_DKIM_SIGNATURESsignatures sont vérifiées, en commençant par celles qui peuvent s’aligner sur le domaine duFrom:, et SPF et chaque signature s’exécutent en parallèle sousVERIFY_TIMEOUT, DMARC ayant une seconde échéance. Une vérification à court de temps devienttemperrorpour cette vérification : DMARC ne tient compte d’une erreur temporaire que sur un identifiant aligné sur le domaine duFrom:, et le DNS que DMARC lui-même lit est celui de ce domaine, de sorte qu’un falsificateur qui ralentit son propre DNS n’y gagne que son propretemperror.Le matériel de clé provenant d’inconnus a une taille plafonnée. Chaque certificat OpenPGP venant de l’extérieur — WKD, un serveur de clés, DANE, un en-tête récolté ou issu de gossip, une clé jointe, une clé collée — passe par un unique analyseur qui refuse une clé RSA de plus de 8192 bits, et le vérificateur écarte également un tel certificat de son magasin de confiance. S/MIME a la même borne à 4096 bits, issue du crate
rsa.La rétention ne dépend pas de la couche web. Les journaux d’audit et de courrier sont purgés par
pepsi-httpd prunedepuispepsi-log-prune.timer— sous le comptepepsi-httpd, le seul rôle de service détenantDELETEsur eux, ce qui garde les journaux en ajout seul pour tout ce qui traite le courrier — et les compteurs de rapports TLS et les entrées expirées du cache MX par leurs propres minuteries, toutes nommées parpepsi.target.La file a un plafond, et le contrôle ne peut être ni affamé ni contourné par scission.
pepsi-ingressrefuse avec452 4.3.1auRCPT, auDATA/BDATet après la fin des données tant que la file est à[pepsi] MAX_QUEUE_ROWS/MAX_QUEUE_BYTESou que le système de fichiers de la base est sousMIN_FREE_SPACE. La mesure (pepsi.queue_usage()) ne lit que deux colonnes de taille générées, de sorte que le rôle ingress au moindre privilège n’obtient pour cela aucun accès au courrier en file, et elle est mise en cache par processus (QUEUE_CHECK_INTERVAL), de sorte qu’un flot de commandesRCPTne coûte aucune requête. Les octets sont comptés par corps stocké : les corps résident danspepsi.workqueue_body, partagés par tous les enregistrements que crée une scission, un clone ou une diffusion de liste, de sorte qu’un gros message adressé à de nombreux destinataires ne peut plus multiplier son corps à travers la file — l’amplification qu’offrait autrefois la scission par destinataire. Le seul amplificateur restant sous le contrôle d’un expéditeur, un message posté sur une grande liste, retient sa diffusion (pepsi-stage-list-post,QUEUE_THROTTLE_DELAY) tant que la file est pleine. Supprimer le dernier enregistrement qui référence un corps supprime ce corps (un trigger), et la clé étrangère fait qu’aucun rôle — leDELETEde la couche web surworkqueue_bodycompris — ne peut supprimer un corps que porte encore un message en file.Les listes de diffusion bornent ce qu’un expéditeur peut leur faire faire. Les messages acceptés d’un expéditeur sur une liste sont comptés par heure (
SENDER_POSTS_PER_HOUR) dans une seule instruction SQL qui décide et consigne, de sorte que des workers parallèles ne peuvent pas prendre tous deux la dernière place ; l’excédent est retenu plutôt que multiplié par le nombre de membres. La file de modération est plafonnée par liste (MAX_HELD_MESSAGES) en rejetant le nouveau message, de sorte qu’une inondation ne peut pas évincer les messages qu’un modérateur n’a pas encore vus.Les dépenses automatiques ont un plafond agrégé, et on ne peut pas les prendre de vitesse.
pepsi-stage-auto-payimpute une demande à deux budgets dans l’unique appelauto_pay_try_spendqui la consigne aussi : celui du message (MAX_TOTAL, sur l’enregistrementorigin_nonce, verrouilléFOR UPDATE) et celui des 24 heures glissantes du wallet payeur (MAX_TOTAL_PER_DAY, le registreauto_pay_spend, sous un verrou consultatif de portée transactionnelle fondé sur le wallet — des demandes pour des messages différents ne partagent aucun enregistrement, de sorte que sans lui chacune lirait la même somme). Un refus par l’un n’écrit rien dans l’autre ; un paiement qui échoue à se régler est recrédité aux deux parauto_pay_refund, qui ne prend que le premier verrou, de sorte que les deux ne peuvent pas s’interbloquer. La clé du wallet est celle depuis laquelle le helper paie (uid:<n>, ou le compte partagé et la base de données du wallet), de sorte que deux expéditeurs ne peuvent partager une journée que s’ils partagent un wallet.Une demande de paiement ne répond à un expéditeur qu’une fois par fenêtre. La demande est une réponse automatique à un expéditeur d’enveloppe non vérifié ; outre la suppression RFC 3834,
pepsi-stage-anti-spamla réclame donc viapayment_request_should_send—INSERT … ON CONFLICT DO UPDATE … WHEREdécidant et consignant en une seule instruction, comme le faitvacation_should_reply— fondé sur la boîte aux lettres protégée et l’expéditeur, avecBLOCK_RESPONSE_SUPPRESScomme fenêtre. Un échec de la réclamation retient la réponse ; le message est retenu dans tous les cas.
15.9. Ce contre quoi Pepsi ne défend pas¶
La liste est tenue en un seul endroit, Ce que Pepsi ne protège pas, avec les résidus qu’énonce chaque section consacrée à un adversaire : root et un opérateur malveillant, l’analyse de trafic et les métadonnées, le texte en clair dans la file, les signatures face à un pipeline compromis, la compromission des correspondants eux-mêmes, et un attaquant réseau qui peut aussi mentir dans le DNS.
15.10. Signaler une vulnérabilité¶
Veuillez signaler les problèmes de sécurité en privé par courriel à Christian Grothoff, à l’adresse grothoff@gnunet.org, chiffrés avec la clé OpenPGP publiée à https://grothoff.org/christian/grothoff.asc, dont l’empreinte est D842 3BCB 326C 7907 0339 29C7 939E 6BE1 E29F C3CC. N’utilisez pas le gestionnaire de bogues public pour une vulnérabilité, et laissez le temps de publier un correctif avant toute divulgation. Le fichier SECURITY.md de l’arborescence des sources énonce la politique complète.
Les bogues ordinaires se signalent dans le gestionnaire de bogues public à l’adresse https://bugs.gnunet.org/view_all_bug_page.php?project_id=35.
15.11. Voir aussi¶
Gestion des clés — identités, garde, découverte et échelle de confiance. Le portail de repli par lien sécurisé — le portail en entier. L’API d’administration — la surface de l’API et ses portées. Interopérabilité des clients — ce que les autres clients savent réellement lire, ce qui borne la protection atteignable en pratique. Suite de tests — le fuzzing et les corpus adverses derrière les affirmations sur les analyseurs.