14. Modèle de menace

Ce chapitre énonce ce que Pepsi protège, contre qui, et jusqu’où — les affirmations. Le chapitre suivant, Modèle de sécurité, décrit les mécanismes qui les soutiennent (la séparation des comptes, les droits de la base de données, les programmes setuid, la garde des clés) et c’est celui qu’il faut lire après une compromission. Chaque affirmation nomme ici le mécanisme sur lequel elle repose, et chaque résidu — une chose que Pepsi ne protège pas — est énoncé comme tel plutôt que laissé à la déduction du lecteur.

14.1. Comment lire ce chapitre

Une affirmation de sécurité comporte trois parties : un actif (ce qui mérite d’être protégé), un adversaire (qui le veut, et ce qu’il peut déjà faire) et un mécanisme (ce qui se tient entre les deux). Le chapitre est organisé de la même façon :

Deux termes sont employés au sens strict. Tient signifie que l’affirmation est imposée par un mécanisme que l’adversaire considéré ne peut pas désactiver. Résiduel signifie que l’adversaire l’obtient, et le texte le dit à dessein.

14.2. Actifs

Actif

Où il se trouve

La propriété qui importe

Courrier en transit

pepsi.workqueue (headers, state) et pepsi.workqueue_body (le corps, partagé par les enregistrements des destinataires d’un message), en clair ; supprimés à la remise.

Confidentialité et intégrité pendant la mise en file ; il doit atteindre le destinataire auquel il était adressé et personne d’autre.

Courrier remis

Le Maildir du destinataire, le serveur LMTP ou le MTA suivant. Il n’appartient plus à Pepsi après la remise.

La part de Pepsi : remettre dans la bonne boîte aux lettres en tant que le bon utilisateur, jamais dans celle d’un autre — et, pour un utilisateur disposant d’une clé client, ne jamais classer lisible un message arrivé chiffré (pepsi-stage-reencrypt).

Messages secure-link stockés

pepsi.secure_message.ciphertext, scellé sous une clé dérivée du PIN.

Confidentialité vis-à-vis de quiconque ne détient pas le PIN.

Clés privées de bout en bout

crypto_identity.private_wrapped, enveloppée par AEAD sous la clé de chiffrement de clés (KEK) de secrets.d.

Jamais divulguées ; utilisées uniquement par les deux étapes cryptographiques et pepsi-keys.

Clés de signature de domaine et secrets de service

Les clés DKIM sur le système de fichiers (groupe pepsi) ; la clé SRS, la clé Pepsi-Origin, le poivre secure-link, la clé de désabonnement des listes, les identifiants du smarthost et du LMTP, les jetons OAuth — chacun dans son propre fragment secrets.d.

Confidentialité ; chacun lisible uniquement par le compte qui l’utilise.

Identité d’envoi et réputation

Ce que les clés ci-dessus permettent de faire : signer pour le domaine (DKIM, ARC), réécrire les expéditeurs (SRS), se porter garant d’un courrier comme étant le nôtre (Pepsi-Origin).

Personne en dehors du pipeline ne doit pouvoir envoyer du courrier portant l’autorité de notre domaine ; le serveur ne doit devenir ni un relais ouvert ni une source de backscatter.

Signatures par utilisateur

Les signatures OpenPGP/S/MIME que pepsi-stage-encrypt produit avec la clé d’un utilisateur détenue par le serveur.

Une signature au nom d”alice n’est produite que sur du courrier qu”alice a soumis.

Indicateurs de sécurité

X-Pepsi-Crypto, Authentication-Results, les étiquettes d’objet [decrypted] / [verified], et les clés de state sur lesquelles d’autres étapes s’appuient (state.signature_verified, state.spam, state.local_origin).

Intégrité : un lecteur ou une étape ultérieure peut s’y fier.

Configuration et politique

pepsi.conf (0644, sans secrets), pepsi.config_override, pepsi.settings, la whitelist.

Intégrité : seul l’opérateur décide de la politique, et chaque utilisateur seulement de sa propre part de celle-ci.

Métadonnées de correspondance

Les enveloppes dans la file d’attente, mail_log, event_log, la whitelist, le cache des clés de correspondants, dns_address, tls_session.

Confidentialité vis-à-vis des tiers extérieurs ; voir Ce que Pepsi ne protège pas pour ce qui est visible par qui.

Vie privée vis-à-vis des tiers

Ce qui quitte l’hôte de sa propre initiative : les recherches de découverte de clés (DNS, WKD, VKS, LDAP), les rapports TLS, la télémétrie sur consentement explicite.

Divulguer le minimum, et rien à quoi l’opérateur n’a pas explicitement consenti.

Argent

Les wallets GNU Taler pilotés par pepsi-helper-auto-pay ; les identifiants du backend marchand de [pepsi-payments].

Un wallet n’est dépensé que par son propriétaire ou, automatiquement, dans la limite du budget fixé par l’opérateur, et uniquement en réponse à une demande concernant du courrier que nous avons réellement envoyé.

Identifiants de la surface d’administration

admin_account (Argon2id), api_token (condensats), admin_session ; les comptes de listes dans list_user.

Impossibles à retrouver à partir de la base de données ; impossibles à rejouer.

Disponibilité

Le pipeline dans son ensemble.

Le courrier continue de circuler face à des entrées hostiles ; un échec diffère plutôt qu’il ne perd ou ne divulgue. Voir Disponibilité.

14.3. Principaux et confiance accordée à chacun

Principal

Confiance

L’opérateur (root, ou quiconque peut modifier /etc/pepsi)

Entièrement digne de confiance, et ceci est énoncé avec précision : l’opérateur configure le pipeline, détient tous les secrets et voit chaque message qui transite en garde par la passerelle. Aucune affirmation de ce manuel ne tient face à un opérateur qui modifie le serveur. Les affirmations sur les données au repos (clés enveloppées, texte chiffré secure-link, condensats d’identifiants) tiennent face à un lecteur passif des données stockées — une sauvegarde volée, un administrateur de la seule base de données — jamais face à quelqu’un qui contrôle le système en fonctionnement.

Un administrateur de base de données sans root sur l’hôte

Un adversaire passif au sens de ce modèle : il peut lire et modifier toutes les tables, il ne peut pas lire secrets.d ni les clés DKIM. Ce que cela lui rapporte est exactement Un attaquant qui a la base de données — plus, puisqu’il peut écrire, le contrôle de la file d’attente et de toutes les clés de state auxquelles se fient les étapes en aval.

Le pipeline (le compte pepsi et chaque étape qu’il exécute)

Digne de confiance pour traiter le courrier, et donc une partie de ce que vise un attaquant. Chaque étape peut lire et réécrire chaque message en file ; voir Un composant compromis pour ce que vaut une étape compromise.

Utilisateurs shell locaux

Hostiles les uns envers les autres et envers Pepsi. Un utilisateur peut soumettre du courrier en son propre nom (via sendmail, authentifié par SO_PEERCRED), gérer son propre espace de noms de whitelist, lire sa propre boîte aux lettres, et rien d’autre.

Utilisateurs limités au courrier (submission SMTP, IMAP, pas de shell)

Hostiles les uns envers les autres. Leurs leviers sont la submission (sous les identités que USERNAME_MAP leur autorise), les adresses de contrôle (pepsi-stage-edit-settings, le self-service du wallet, les commandes de clés) et tout ce que l’opérateur leur permet de surcharger pour leur propre adresse.

Hôtes relais de confiance (MYNETWORKS, certificats client)

Dignes de confiance pour soumettre du courrier d’origine locale. Un hôte compromis est un émetteur compromis pour chaque adresse au nom de laquelle il peut envoyer.

Administrateurs délégués (jetons d’API et comptes de console à portée restreinte, y compris own:<address>)

Hostiles au-delà de leur portée : un principal ne peut jamais émettre un identifiant plus puissant que lui-même (scope_escalation), et un principal own: n’atteint qu’une seule adresse. Voir L’API d’administration.

Membres, propriétaires et modérateurs de listes

Hostiles au-delà de leurs listes. Un système de comptes distinct (pepsi.list_user) qui n’accorde rien du côté de l’administration, et inversement.

Expéditeurs distants

Hostiles. Tout ce qu’ils envoient — enveloppe, en-têtes, structure MIME, texte chiffré, clés, en-têtes Autocrypt, DSN, demandes de paiement — est une entrée contrôlée par l’attaquant.

MTA destinataires distants

Non dignes de confiance pour la confidentialité, sauf si le TLS est authentifié (DANE ou une politique MTA-STS appliquée) ; voir L’adversaire réseau.

Sources de clés des correspondants (leur DNS, leur hôte WKD, un serveur VKS, un annuaire LDAP)

Dignes de confiance exactement dans la mesure où l’échelle de confiance les classe (Gestion des clés). Un domaine peut toujours parler au nom de ses propres utilisateurs.

Helpers externes que l’opérateur raccorde (milters, le serveur LMTP, le smarthost, les wallets et exchanges Taler)

Dignes de confiance pour ce que l’opérateur leur confie : un milter voit le message en clair, un serveur LMTP le classe, un smarthost le relaie.

14.4. Frontières de confiance

L’autorité change de mains en un petit nombre d’endroits, et chaque mécanisme de Modèle de sécurité se situe sur l’un d’eux

  remote MTAs,        local users           browser / API client
  senders             (sendmail)            (operator, delegate)
     |                    |                          |
=====|====================|=========== B1: network ==|=================
     v                    v                          v
+--------------+  SO_PEERCRED           +-----------------------+
| pepsi-ingress|<- B2: submission       |      pepsi-httpd      |
|  (auth, SPF, |   identity             |  B6: scopes, CSRF     |
|  DKIM, DMARC)|                        +-----------+-----------+
+------+-------+                                    | setup_task
       | workqueue row                              v  (a request)
=======|===== B3: queue (pepsi.workqueue)   +-----------------------+
       v                                    | pepsi-setup apply     |
+---------------------+   B4: setuid/gid    |  (root; B7: re-checks)|
| stages as `pepsi`   |------------------+  +-----------------------+
| (dispatcher, ARC,   |                  |
|  SRS, dkim-sign ...)|                  v
+----+----------------+     +-----------------------------+
     |                      | helpers: become the user    |
     | B5: key custody      | (maildir, .forward, wallet) |
     v                      +-----------------------------+
+---------------------+
| encrypt / decrypt   |  crypto_identity.private_wrapped
| (setuid pepsi-crypto)|  + KEK in secrets.d
+---------------------+

B1 — le réseau. Tout ce qui arrive est hostile tant que l’ingress ne l’a pas authentifié, et même alors seul le verdict est digne de confiance, jamais le contenu. En sortie, un saut n’est digne de confiance que dans la mesure où son TLS est authentifié.

B2 — l’identité de submission. L’ingress décide qui a soumis un message : SASL, un certificat client, MYNETWORKS ou SO_PEERCRED sur le socket local. USERNAME_MAP décide ensuite quelles identités From:/d’enveloppe cet émetteur peut utiliser. C’est le seul endroit où la question « cette personne peut-elle envoyer au nom de cette adresse ? » est posée.

B3 — la file d’attente. Tout ce qui suit l’ingress est une seule table que chaque étape du pipeline peut lire et écrire. Les verdicts que l’ingress a enregistrés (state.auth, state.local_origin) et ceux qu’ajoutent les étapes ultérieures (state.spam, state.signature_verified) sont des données non authentifiées à l’intérieur de cette frontière : on s’y fie parce que seul le code du pipeline peut les écrire. C’est la plus grande hypothèse de confiance unique de la conception, et c’est pourquoi Un composant compromis traite une étape pepsi comme équivalente à la file d’attente.

B4 — les helpers. Trois tâches exigent d”être un utilisateur local précis (écrire dans un Maildir, agir sur un ~/.forward, piloter un wallet). Chacune est un petit helper setuid-root joignable uniquement via une étape setgid, qui abandonne entièrement ses privilèges au profit de l’utilisateur cible avant de toucher à quoi que ce soit qui appartient à cet utilisateur.

B5 — la garde des clés. Seul pepsi-crypto détient à la fois les clés privées enveloppées et la KEK. Les étapes cryptographiques franchissent cette frontière à chaque message ; rien d’autre dans le pipeline ne le fait.

B6 — la surface d’administration. Chaque requête est authentifiée (SO_PEERCRED, jeton bearer, ou mot de passe + session) et autorisée au regard de l’unique portée du point d’accès.

B7 — l’applicateur privilégié. Le tier web ne détient aucune autorité root. Il enregistre ce qui doit se produire dans pepsi.setup_task ; pepsi-setup apply, qui s’exécute en root et est démarré séparément, revalide chaque tâche au regard de l’autorité du demandeur et l’exécute. Supprimer le paquet pepsi-httpd-admin supprime l’applicateur, ce qui rend cette frontière absolue.

14.5. Adversaires

14.5.1. Un observateur réseau passif

Peut : lire le trafic entre Pepsi et les autres MTA, et entre les clients et Pepsi.

Arrêté par : STARTTLS opportuniste dans les deux sens, TLS sur la submission (obligatoire, sauf sur le socket UNIX local), HTTPS sur le portail et la console. Les messages chiffrés de bout en bout restent chiffrés sur le réseau quel que soit le transport.

Obtient malgré tout : les métadonnées de transport — quels hôtes communiquent, quand et en quelle quantité — et le texte en clair de tout saut dont le pair n’offre aucun TLS.

14.5.2. Un attaquant réseau actif

Peut : intercepter, modifier et supprimer du trafic ; retirer STARTTLS ; présenter son propre certificat ; et, avec un résolveur menteur sur le chemin, falsifier le DNS.

Arrêté par : uniquement ce que le déploiement et le pair ont configuré. Avec les valeurs par défaut, un attaquant actif peut dégrader un saut sortant en clair ou l’intercepter avec son propre certificat, sauf si le domaine destinataire publie une politique MTA-STS enforce (respectée par défaut) ou des enregistrements DANE TLSA (que la valeur par défaut DANE = warn se contente de journaliser). Voir L’adversaire réseau pour le profil durci qui ferme cette brèche.

Obtient malgré tout : un déni de service, toujours. Avec DANE strict, une attaque est convertie en report plutôt qu’en divulgation — la meilleure réponse disponible, pas une réponse complète.

14.5.3. Un expéditeur distant

Peut : envoyer n’importe quels octets sur le port 25 : des en-têtes From: falsifiés, du MIME malformé, du texte chiffré et des signatures hostiles, des en-têtes Autocrypt et Autocrypt-Gossip, des DSN falsifiés, des demandes de paiement falsifiées et des en-têtes de sécurité Pepsi falsifiés.

Arrêté par :

  • Les analyseurs sont traités comme la surface d’attaque. Chaque étape analyse des entrées hostiles ; l’analyseur le plus sensible, le code CMS/S/MIME de pepsi-crypto, est soumis au fuzzing (Suite de tests).

  • Les indicateurs falsifiés sont supprimés. Chaque en-tête X-Pepsi-* est retiré du courrier entrant avant que l’étape de déchiffrement n’écrive les siens, et les étiquettes d’objet [decrypted] / [verified] sont supprimées avant que les nôtres ne soient ajoutées.

  • Les clés récoltées ne peuvent pas usurper l’identité de nos utilisateurs. Les clés apprises du courrier entrant atterrissent sur les échelons inférieurs de l’échelle de confiance, et pour toute adresse dont nous détenons une identité, notre propre clé préempte toutes les clés en cache (lookup_own_first). Un From: alice@our-domain falsifié portant un en-tête Autocrypt: ne peut donc ni rediriger le chiffrement destiné à Alice ni faire vérifier une signature comme étant la sienne.

  • Le gossip est borné. Uniquement depuis du courrier arrivé chiffré, uniquement au sujet d’adresses To:/Cc:, jamais au sujet de nos propres utilisateurs, jamais en déplaçant une meilleure clé, et jamais en mesure de produire un verdict de signature valid. Une clé issue du gossip ne quitte l’échelon inférieur que lorsque son propriétaire annonce lui-même la même clé, ce qui la promeut à l’échelon inbound (audité comme key.peer.promote, jamais comme une rotation) et ne peut toujours pas produire valid.

  • Une rotation de clé n’est jamais silencieuse. Remplacer la clé récoltée d’un correspondant par une plus récente (la règle « la plus récente l’emporte » d’Autocrypt) écrit un enregistrement d’audit dans la même instruction et envoie un e-mail aux destinataires locaux du message qui l’a provoquée ; sous ACCEPT_ROTATION = expired, elle est refusée tant que la clé stockée est vivante.

  • Les boucles sont rompues. Le courrier à expéditeur nul ne rebondit jamais, les réponses automatiques suivent la suppression RFC 3834 (pepsi-common::autoreply), les chaînes .forward portent une garde anti-boucle, le nombre de sauts est plafonné, et un message qui est passé MAX_SELF_HOPS fois par l’ingress de cet hôte est refusé.

  • Pas de backscatter. Une adresse d’un domaine servi dont rien sur l’hôte ne se porte garant est refusée au RCPT (VERIFY_RECIPIENTS), et un rebond ne va qu’à un expéditeur d’enveloppe que le message a authentifié (BOUNCE_UNAUTHENTICATED), de sorte qu’aucun courrier n’est envoyé à un expéditeur falsifié pour le compte du spammeur.

  • Les demandes de paiement sont vérifiées. pepsi-stage-auto-pay ne paie que pour du courrier dont il peut prouver que nous l’avons envoyé (le HMAC Pepsi-Origin), et uniquement dans la limite du budget par message et — lorsque MAX_TOTAL_PER_DAY est défini — du budget quotidien du wallet payeur. Un correspondant peut malgré tout fixer le prix de chaque demande jusqu’à ce que ces budgets laissent ; borner la perte est le rôle de la limite quotidienne, pas de la preuve.

  • L’authentification est enregistrée, pas imposée, par défaut. SPF, DKIM et DMARC entrants sont fail-open : seul un échec DMARC certain sous DMARC_ENFORCE entraîne un rejet. Un message usurpé est donc remis avec un Authentication-Results honnête plutôt que refusé.

Obtient malgré tout : l’exécution de code dans une étape si un bogue d’analyse existe (voir Un composant compromis) ; un message dans la boîte aux lettres du destinataire avec un From: falsifié si l’opérateur n’applique pas DMARC ; une clé de son choix utilisée pour chiffrer le courrier vers une adresse pour laquelle nous ne détenons aucune identité, aux échelons trust-on-first-use, sauf si MIN_TRUST les exclut — y compris en remplaçant la clé récoltée d’un correspondant par une plus récente sous un From: falsifié, ce qui est audité et annoncé aux destinataires mais pas empêché, sauf avec ACCEPT_ROTATION = expired. Il apprend aussi, par les réponses au RCPT, quelles adresses d’un domaine vérifié existent — comme auprès de tout MTA qui refuse les utilisateurs inconnus — à raison de dix essais par connexion (MAX_INVALID_RECIPIENTS). Lorsque cette adresse est l’une des nôtres — un utilisateur servi pour lequel nous ne détenons aucune identité, qu’un From: falsifié peut atteindre chaque fois que DMARC n’est pas appliqué sur notre propre domaine — la clé implantée n’est pas empêchée non plus, mais elle n’est pas invisible : pepsi-keys peer unclaimed liste chaque adresse servie dont le chiffrement est décidé par une clé récoltée ou issue du gossip, comme liste de travail que l’opérateur doit confirmer et convertir en identités (Repérer les utilisateurs qui apportent leur propre clé).

14.5.4. Un utilisateur shell local

Peut : exécuter tout programme exécutable par tous, se connecter aux sockets accessibles à tous et lire les fichiers lisibles par tous — y compris pepsi.conf, ce qui explique qu’il ne contienne aucun secret.

Arrêté par :

  • Submission en son propre nom uniquement. Le socket de submission local peut être en mode 0666 parce que l’ingress tire l’identité de l’appelant de SO_PEERCRED et la transmet à USERNAME_MAP ; un utilisateur ne peut envoyer que sous les identités associées à son login.

  • Aucun programme privilégié n’est joignable depuis un compte ordinaire, à l’exception de pepsi-whitelist, délibérément exécutable par tous, qui impose son propre espace de noms par login. Les autres programmes setuid/setgid sont en mode 2550 ou 4750, exécutables uniquement par pepsi ou par un groupe dédié — et 2550 plutôt que 2755 constitue toute la défense : l’exécution par tous donnerait à chaque compte le groupe qui ouvre l’accès à un helper setuid-root.

  • Les programmes setuid assainissent avant de faire confiance. pepsi-whitelist abandonne immédiatement ses privilèges au profit de l’utilisateur appelant, efface les variables d’environnement qui orientent la configuration et libpq, refuse -c, et ne reprend ses privilèges qu’autour de sa connexion à la base de données. L’analyse des boîtes aux lettres a lieu dans un processus distinct qui ne peut jamais retrouver le privilège (setresuid sur les trois identifiants).

  • Les helpers deviennent entièrement l’utilisateur. Les helpers Maildir, .forward et wallet fixent chaque uid et gid à l’utilisateur cible et refusent root, de sorte que les fichiers d’un utilisateur sont touchés avec l’autorité de cet utilisateur et aucune autre.

Obtient malgré tout : la topologie de la configuration (domaines servis, graphe du pipeline, noms d’hôtes) depuis pepsi.conf, lisible par tous.

14.5.5. Un utilisateur limité au courrier

Peut : soumettre du courrier par un canal authentifié, envoyer des messages de contrôle aux adresses de contrôle et utiliser les pages web de self-service que son déploiement active.

Arrêté par : USERNAME_MAP (au nom de quelles adresses il peut envoyer, et sur lesquelles il peut enregistrer une clé) ; les étapes de contrôle, qui fondent chaque action sur l”expéditeur d’enveloppe authentifié d’un message state.local_origin ; la liste d’autorisation EDITABLE_STAGES, qui borne ce que ses surcharges peuvent toucher (voir Les paramètres par adresse sont une frontière de confiance) ; et, pour un utilisateur qui est aussi un compte Unix, les helpers qui n’agissent qu’avec l’autorité de ce compte.

Obtient malgré tout : le contrôle du traitement de son propre courrier dans les limites de ce que l’opérateur a rendu modifiable — y compris, pour une étape modifiable, le droit d’acheminer sa propre copie d’un message ailleurs que vers la destination par défaut de l’opérateur.

14.5.6. Quelqu’un qui détient le mot de passe de messagerie d’un utilisateur

Peut : tout ce que peut l’utilisateur limité au courrier, en tant que cet utilisateur : soumettre du courrier avec un From: lié, envoyer des messages de contrôle, et généralement lire aussi la boîte aux lettres via IMAP, puisque le même mot de passe l’ouvre.

La voie de l’enregistrement de clé, et ce qui la ferme. Soumettre en tant qu’un utilisateur est ce qui enregistre une clé comme la sienne (un en-tête Autocrypt: soumis, ou la commande register), de sorte qu’un mot de passe volé suffit à faire de la propre clé de l’attaquant la face publique de l’adresse : le courrier futur des correspondants est alors chiffré pour elle, et les signatures faites avec elle sont vérifiées comme étant celles de l’utilisateur. L’avis « une nouvelle clé a été enregistrée » n’est qu’une atténuation partielle — le même mot de passe permet de lire la boîte aux lettres où il arrive et de le supprimer.

Un utilisateur qui enrôle un second facteur (otp enroll, voir Gestion des clés) ferme cette voie. Dès lors, toute modification de ses clés qu’un utilisateur peut effectuer — une commande d’enregistrement ou de retrait de clé par e-mail, une génération, un enregistrement ou une révocation dans la console web sous own:<address>, un pepsi-keys non privilégié — exige un code TOTP à six chiffres en cours de validité issu de son application d’authentification, et l’en-tête Autocrypt: d’un message ordinaire n’enregistre plus rien du tout. Il en va de même pour le remplacement ou la suppression du second facteur lui-même.

Arrêté par (avec un second facteur) : le code (RFC 6238, un pas de 30 secondes, ±1 pas de décalage) ; la protection contre le rejeu (un code est accepté une seule fois, pour un pas postérieur au dernier utilisé) ; un verrouillage après dix codes erronés consécutifs que seul l’opérateur lève (pepsi-keys otp reset, DELETE /api/v1/otp/{address}) ; et la garde — le secret est enveloppé sous la KEK du magasin de clés et la table otp_key est accordée à pepsi-crypto seul, de sorte que ni pepsi-httpd ni une étape ordinaire ne peuvent lire un secret, réinitialiser un compteur ou supprimer un enrôlement.

Obtient malgré tout :

  • Sans second facteur, tout ce qui précède. Rien n’en impose un ; c’est le choix de l’utilisateur, et tant qu’il ne l’a pas fait, l’avis est la seule atténuation.

  • Le premier enrôlement. L’enrôlement n’exige aucun code (il n’en existe pas encore), de sorte qu’un attaquant qui y parvient le premier possède le second facteur et que l’utilisateur a besoin de l’opérateur pour le réinitialiser. La réponse d’enrôlement — qui contient le secret — arrive dans la boîte aux lettres, et un voleur ultérieur du mot de passe qui la trouve non supprimée détient les deux facteurs : la réponse dit de la supprimer.

  • Un déni de service sur la gestion des clés en self-service. Dix codes erronés la verrouillent, ce qu’un attaquant peut toujours provoquer ; la réinitialisation par l’opérateur est le remède, et le verrouillage n’affecte jamais la remise du courrier ni le chiffrement.

  • Les indicateurs de publication de la console web. Le téléversement vers un serveur de clés, l’activation ou la désactivation de la publication WKD et le choix de la clé MTA principale se font directement dans pepsi-httpd, qui ne peut pas vérifier de code, de sorte qu’un principal own:<address> les modifie sans code. Aucun d’eux ne change la clé à laquelle on fait confiance pour l’adresse ; les mêmes opérations par e-mail ou via pepsi-keys exigent bien le code.

  • La lecture de la boîte aux lettres, et l’envoi de courrier au nom de l’utilisateur — le mot de passe reste le mot de passe.

14.5.7. Un administrateur délégué

Peut : tout ce que les portées de son jeton ou de son compte autorisent — par exemple queue:read sans queue:write, ou own:<address> pour une seule adresse.

Arrêté par : l’unique portée déclarée de chaque point d’accès, imposée à partir de la table dont est généré le document OpenAPI ; scope_escalation lors de l’émission d’un identifiant ; et, pour own:, une nouvelle vérification de l’adresse sur le point d’accès et à nouveau dans l’applicateur pour chaque tâche privilégiée.

Obtient malgré tout : les données que sa portée permet de lire. queue:read voit les enveloppes et state (jamais les en-têtes ni les corps — les requêtes de la console ne sélectionnent pas ces colonnes), logs:read le graphe de correspondance, keys:read toutes les clés publiques.

14.5.8. Quelqu’un qui détient une copie de la base de données

Une sauvegarde, une réplique, une prise par injection SQL sur un chemin de lecture. Un attaquant qui a la base de données en dresse la liste complète. En bref : il obtient le courrier en file en clair et toutes les métadonnées ; il n’obtient pas de clé privée (enveloppée sous une KEK extérieure à la base de données), de corps secure-link (le PIN et le poivre sont tous deux absents), ni d’identifiant utilisable (Argon2id et condensats).

Quelqu’un qui peut écrire dans la base de données, en tant que propriétaire du schéma ou rôle du pipeline, contrôle en outre la file d’attente et les clés de state — et donc tout ce à quoi, selon B3, on se fie à cet endroit.

14.5.9. Quelqu’un qui détient la configuration ou un secret

pepsi.conf relève de la reconnaissance. Chaque fragment secrets.d est exactement une capacité : la clé SRS falsifie des adresses SRS, la clé Pepsi-Origin falsifie « nous avons envoyé ceci » (et oriente donc l’auto-pay), le poivre retire l’un des deux facteurs de la clé secure-link, la KEK ouvre les clés enveloppées si l’attaquant détient aussi la base de données. L’accès en écriture à pepsi.conf équivaut à root, car il nomme les programmes qu’exécute le pipeline. Voir Un attaquant qui a le fichier de configuration.

14.5.10. Un composant compromis

Le cas grave réaliste : un bogue d’analyse donne l’exécution de code dans un processus. La portée de chaque compte est bornée par son identité au niveau du système d’exploitation et par ses droits dans la base de données (La séparation des privilèges).

Compromis

Obtient

N’obtient pas

Une étape pepsi (la plupart d’entre elles, et le dispatcher)

Toute la file d’attente : lire, modifier, rediriger, supprimer ou injecter des messages. Chaque verdict de state. Les paramètres, la whitelist et le cache des clés de correspondants. Les clés DKIM, si bien qu’elle peut signer au nom du domaine. Et — en réécrivant le From: d’une submission en file avant que pepsi-stage-encrypt ne le voie — une signature par utilisateur de n’importe quel utilisateur local qui possède une identité.

Toute clé privée de bout en bout ou la KEK ; la colonne de texte chiffré secure-link ; la surcouche de configuration ; la file des tâches de root ; les identifiants d’administration ; un autre compte Unix.

pepsi-stage-encrypt / -decrypt (pepsi-crypto)

Tout ce qui précède, plus toutes les clés privées de bout en bout et la KEK : elle peut déchiffrer tout le courrier entrant destiné aux clés détenues par le serveur et signer au nom de n’importe quelle identité, et elle peut emporter les clés.

Les clés dont l’utilisateur conserve la moitié privée dans son propre client (custody = client).

pepsi-ingress

Chaque message à son arrivée, avant toute étape ; la clé SRS ; la clé TLS de l’écouteur ; l’adresse de chaque liste de diffusion (le contrôle des destinataires). Il décide de B2, et peut donc admettre un message au nom de n’importe quel émetteur local.

Tout message déjà en file (son rôle de base de données peut ajouter à la file et n’en lire rien) ; les paramètres, les whitelists, les clés ; toute clé privée de bout en bout ; les écritures dans mailbox_quota.

pepsi-httpd

Les enveloppes et le state de la file d’attente, et le pouvoir de remettre en file, de réacheminer, de supprimer ou d’injecter des messages ; les métadonnées des clés ; les tables de listes et d’archives ; les tables d’identifiants d’administration ; le texte chiffré du portail (mais aucun PIN). Il peut demander n’importe quelle modification privilégiée — y compris l’activation de la télémétrie des fonctionnalités, qui ne prend effet que là où l’opérateur exécute pepsi-telemetry-client ; il apprend si un SYSTEM_ID existe, jamais sa valeur.

Les en-têtes ou le corps d’un message en file ; les paramètres et whitelists des utilisateurs ; root, /etc, toute clé privée ; la surcouche de configuration, sauf si CONFIG_DB est configuré. Une requête qu’il enregistre est revalidée par l’applicateur — et reste inerte si pepsi-httpd-admin n’est pas installé.

pepsi-keydisc

Le cache des clés de correspondants et la file des requêtes de découverte ; il peut donc implanter une clé de correspondant à n’importe quel rang.

La file d’attente ; nos propres identités (qui préemptent le cache).

Un helper setuid (maildir, .forward, wallet)

Root, s’il est compromis avant d’abandonner ses privilèges. Ces helpers sont gardés petits et n’effectuent leur analyse qu”après être devenus l’utilisateur.

—

Important

Les signatures ne valent que ce que vaut le rôle du pipeline. Une signature par utilisateur dit « ce déploiement a accepté ce message de la part d”alice », et cette déclaration est faite par pepsi-stage-encrypt sur la foi d’un en-tête From: que chaque étape pepsi peut réécrire après que l’ingress l’a vérifié. La séparation des comptes protège les clés — une étape compromise ne peut en emporter aucune, si bien que les dégâts cessent lorsque l’étape est corrigée et qu’aucun renouvellement des clés n’est nécessaire — mais pas leur utilisation. Les signatures DKIM de domaine sont dans la même situation. C’est un résidu connu, consigné dans Modèle de sécurité.

14.6. Chiffrement de bout en bout : deux modes de garde

« De bout en bout » suppose un bout. Pepsi en prend deux en charge, par identité, et chaque affirmation dépend de celui dans lequel se trouve une clé.

14.6.1. Garde par la passerelle (custody = local, la clé de MTA)

Le serveur génère la clé, détient la moitié privée enveloppée, et signe et déchiffre pour le compte de l’utilisateur. L’utilisateur lit du courrier ordinaire dans n’importe quel client.

Le bout, c’est le serveur. La protection s’exerce entre domaines et au repos :

  • En transit entre déploiements : le courrier est chiffré de la passerelle (ou du client) émettrice jusqu’à la passerelle destinataire. Un adversaire réseau et chaque relais intermédiaire voient du texte chiffré.

  • Au repos face à un lecteur passif : une copie de la base de données livre les clés enveloppées mais pas la KEK ; un secrets.d volé livre la KEK mais pas les clés.

  • Pas face à l’opérateur, à root ou à une étape ``pepsi-crypto`` compromise : ils voient le texte en clair et utilisent les clés.

  • Pas face à un pipeline compromis pour les signatures — voir l’encadré ci-dessus.

  • Le courrier dans la file d’attente est en clair des deux côtés des étapes cryptographiques ; c’est ce qu’est un pipeline. Chiffrez la base de données au repos si cela importe.

  • Le courrier classé dans la boîte aux lettres est en clair — le magasin de courrier est digne de confiance — sauf si le destinataire détient aussi une clé client et que pepsi-stage-reencrypt fait partie du pipeline, auquel cas la copie classée est rescellée pour cette clé (voir ci-dessous). [stage-reencrypt] ON_NO_CLIENT_KEY = bounce (pour tout le site ou par destinataire) refuse le courrier plutôt que de le classer lisible.

C’est le modèle d’une appliance passerelle S/MIME, et il est choisi à dessein : il protège les utilisateurs qui n’installeront jamais rien.

14.6.2. Garde par le client (custody = client, la clé de MUA)

Le propre client de messagerie de l’utilisateur détient la clé privée ; Pepsi ne détient que la moitié publique (enregistrée via pepsi-keys, la console, la commande de contrôle register ou le propre en-tête Autocrypt: de l’utilisateur).

Le bout, c’est le client de l’utilisateur. Pepsi publie la clé (elle devient la face publique de l’adresse dans WKD), chiffre pour elle le courrier des autres utilisateurs locaux, et laisse passer sans l’ouvrir le courrier chiffré pour elle — même lorsqu’une clé de MTA du même utilisateur figure aussi parmi les destinataires. Ce que cela apporte :

  • Face à un voleur de base de données, une étape compromise ou une étape cryptographique compromise : le courrier chiffré pour la clé client ne peut pas être lu — aucun processus du serveur n’a jamais détenu la moitié privée.

  • Pas face à un opérateur malveillant, qui peut toujours (a) substituer une autre clé dans ce que Pepsi publie (WKD, DANE, VKS) et dans la préemption own, de sorte que le courrier futur soit chiffré pour une clé de son choix ; (b) lire le courrier arrivé en clair avant que Pepsi ne le chiffre pour l’utilisateur ; et (c) supprimer ou retarder du courrier. Un utilisateur qui a besoin d’une protection contre l’opérateur doit faire chiffrer ses correspondants pour une empreinte vérifiée hors bande.

  • Les signatures que l’utilisateur produit dans son client n’appartiennent qu’à lui ; Pepsi n’ajoute aucune signature propre sur le courrier que le client a déjà signé.

  • Uniquement pour le courrier réellement chiffré pour cette clé — en transit. Un utilisateur peut détenir les deux types : la clé de MTA n’est pas retirée lorsqu’une clé de MUA apparaît, et elle continue d’ouvrir le courrier des correspondants qui l’utilisent encore. Ce courrier est en garde par la passerelle pendant qu’il est en file, avec les garanties de la garde par la passerelle.

  • Au repos, le courrier que la passerelle a ouvert est rescellé pour la clé client lorsque pepsi-stage-reencrypt s’exécute avant la remise locale : les étapes de spam, de langue et de whitelist lisent le texte en clair, et ce qui est classé est un texte chiffré que seul le client ouvre. Cela protège la copie classée contre quiconque obtient ultérieurement le magasin de courrier, la base de données ou le serveur ; cela n’annule pas le passage du texte en clair dans la file d’attente, qu’une étape compromise ou l’opérateur aurait pu lire ou détourner à ce moment-là. Le courrier arrivé en clair est classé tel qu’il est arrivé — le sceller revendiquerait une confidentialité qu’il n’a jamais eue. Aucune signature n’est ajoutée : l’étape ne détient aucune clé privée, et le verdict de signature de l’expéditeur est celui que pepsi-stage-decrypt a enregistré.

14.6.3. Choisir entre les deux

Adversaire

Garde par la passerelle

Garde par le client

Observateur réseau passif

Protégé

Protégé

Voleur de la base de données ou d’une sauvegarde

Protégé (clés enveloppées)

Protégé (aucune clé privée)

Étape ordinaire compromise

Lit le texte en clair en file ; peut obtenir des signatures ; ne peut pas prendre la clé

Ne lit le courrier que s’il est arrivé en clair ; ne peut pas signer au nom de l’utilisateur

Étape pepsi-crypto compromise

Clés prises ; tout le courrier est lisible

Protégé pour le courrier chiffré pour la clé client

Opérateur malveillant

Non protégé

Non protégé pour le courrier futur (substitution de clé) ; protégé pour le courrier déjà chiffré pour une empreinte vérifiée

Voleur du magasin de courrier après la remise

Non protégé (classé en clair)

Protégé : le courrier chiffré pour la clé client et — avec pepsi-stage-reencrypt — le courrier que la passerelle a ouvert

14.6.4. Clés des correspondants

Chiffrer pour un correspondant ne vaut que ce que vaut la provenance de la clé. L’échelle de confiance (Gestion des clés) classe chaque source, et [pepsi-keydiscovery] MIN_TRUST fixe le plancher. Le plancher par défaut est l’échelon inférieur, si bien que le courrier est chiffré pour les clés trust-on-first-use — ce qui met en échec un espion passif, et ne met pas en échec un attaquant qui contrôle la source de clés du correspondant ou peut injecter une clé récoltée avant que la vraie ne soit vue. C’est un compromis délibéré : l’alternative à une clé TOFU est le texte en clair, pas une meilleure clé. Un déploiement qui préfère le texte en clair (ou le portail secure-link) à une clé non authentifiée relève le plancher ; voir L’adversaire réseau.

Le même compromis régit le changement d’une clé. Sur les échelons trust-on-first-use, la clé la plus récente l’emporte (la mise à jour de l’état du pair d’Autocrypt Level 1), et « la plus récente » est jugée d’après un message dont l’expéditeur a écrit le From: — de sorte qu’un expéditeur distant capable de falsifier l’adresse d’un correspondant peut faire en sorte que le courrier futur destiné à ce correspondant parte vers une clé de son choix. Ce que Pepsi garantit, c’est que cela n’est jamais silencieux : chaque remplacement de ce type est un enregistrement du journal d’audit écrit par l’instruction qui l’effectue, et les destinataires locaux du message qui l’a provoqué reçoivent par e-mail les deux empreintes (Une rotation n’est jamais silencieuse). Un opérateur qui préfère refuser des rotations authentiques plutôt qu’accepter des rotations falsifiées définit ACCEPT_ROTATION = expired, sous lequel une clé stockée vivante n’est jamais remplacée par un message ; le correspondant ne peut alors plus lire notre courrier tant qu’un opérateur n’a pas accepté sa nouvelle clé à la main, ou que la clé stockée n’a pas atteint l’expiration qu’elle déclare elle-même. Cette date est lue dans le propre matériel de la clé stockée lorsqu’elle est apprise (une auto-signature OpenPGP, un notAfter X.509), et lors d’une nouvelle observation de la même clé OpenPGP elle ne fait jamais que reculer — si bien que rejouer une copie plus ancienne du certificat, dont l’auto-signature antérieure portait une expiration plus courte et échue, ne peut pas faire paraître morte une clé vivante.

Une clé issue du gossip que son propriétaire annonce ensuite lui-même est promue à l’échelon du propriétaire plutôt que remplacée (Une clé issue de gossip que son propriétaire confirme est promue). Cela n’accorde à un expéditeur distant rien de plus que ce que lui accordait déjà un From: falsifié : un en-tête Autocrypt: falsifié portant la propre clé de l’attaquant déplace purement et simplement un enregistrement issu du gossip par son rang, si bien que répéter la clé issue du gossip ne peut que confirmer ce qui était stocké. La promotion change le rang avec lequel l’enregistrement se défend, jamais le verdict que peut atteindre une signature vérifiée avec elle.

14.7. Ce que signifie un indicateur de sécurité

Un lecteur, une règle de filtrage et les étapes ultérieures agissent tous sur ce que Pepsi dit d’un message. Le sens de chaque indicateur est exact :

[decrypted], state.encrypted

Le message est arrivé chiffré pour une clé que ce déploiement détenait, et a été ouvert ici. Cela ne dit rien de qui l’a envoyé.

[verified], state.signature_verified, verdict valid

Une signature qui couvre le texte en clair a été vérifiée, et la clé est ancrée : notre propre identité, une clé épinglée manuellement, une clé DANE validée par DNSSEC, une clé WKD, ou une chaîne S/MIME menant à une ancre de confiance installée par l’opérateur — une chaîne qui reste en outre à l’intérieur de chaque borne nameConstraints et de chaque exigence de politique de certificat fixée par les AC qui la composent, y compris celles de l’ancre elle-même, si bien qu’ancrer l’AC d’un partenaire se porte garant de l’espace de noms de ce partenaire et de rien de plus. Rien de plus faible — une clé récoltée, issue du gossip, jointe ou VKS, une réponse DANE sans le bit AD, une AC que personne n’a choisie — ne peut le produire ; ces sources donnent valid-untrusted. Une signature qui ne couvre que du texte chiffré ne le produit jamais non plus, car quiconque détient un texte chiffré peut l’envelopper de sa propre signature.

Authentication-Results

Ce que l’ingress a conclu au sujet de SPF, DKIM, DMARC et ARC, tel qu’il l’a enregistré ; ce n’est pas un filtre. L’authentification entrante est fail-open.

X-Pepsi-Crypto

Le résumé de l’étape de déchiffrement. Crédible uniquement parce que chaque en-tête X-Pepsi-* entrant est d’abord retiré ; un client ne doit pas s’y fier sur du courrier qui n’est pas passé par une étape de déchiffrement Pepsi.

Chacun d’eux est écrit par le code du pipeline dans la file d’attente (frontière B3). Ils signifient ce qu’ils disent face à un expéditeur distant ; ils ne signifient rien face à une étape pepsi compromise, qui peut écrire n’importe lequel d’entre eux.

14.8. Les paramètres par adresse sont une frontière de confiance

La configuration est organisée en couches (Configuration) : le fichier INI, puis la surcouche de l’opérateur dans la base de données (globale, domain:, address:), puis pepsi.settings — la seule couche qu’un titulaire de compte peut écrire, par e-mail via pepsi-stage-edit-settings. Les couches inférieures appartiennent à l’opérateur ; celle-ci appartient à l’utilisateur, et elle est traitée comme une entrée non fiable :

  • Seules les étapes que l’opérateur énumère dans EDITABLE_STAGES peuvent être modifiées. Une modification de toute autre section est refusée et rien n’est stocké.

  • Seule la propre copie de l’utilisateur est concernée. Une surcharge est indexée sur l’expéditeur d’enveloppe pour le courrier soumis localement et sur le destinataire d’enveloppe pour tout le reste, et un message dont les surcharges des destinataires divergent est d’abord scindé, si bien que les paramètres d’un utilisateur n’agissent jamais sur la copie d’un autre utilisateur.

  • Les options d’identité sont interdites (SIGNING_DOMAIN) : le domaine au nom duquel un message est signé relève de la décision de l’opérateur.

  • Les programmes ne peuvent pas être remplacés. Le programme qui exécute une étape, son successeur par défaut NEXT_STAGE et BOUNCE_STAGE sont tirés du fichier de l’opérateur ; une surcharge de ceux-ci est sans effet (et PROGRAM est en outre restreint à une commande pepsi-stage-* par l’étape de modification).

  • Chaque modification est validée par l’analyseur propre à l’étape concernée, au regard de la configuration effective qu’elle produirait, avant d’être stockée.

  • Les secrets ne sont jamais renvoyés. La réponse cite les options effectives en masquant chaque valeur porteuse d’identifiants, à l’aide du même prédicat que l’API.

Ce qu’une étape modifiable confie bel et bien à l’utilisateur, c’est chaque option que cette étape lit dans sa propre section — y compris ses cibles de branchement (TRUE_STAGE, QUARANTINE_STAGE, RESPONSE_STAGE, SECURE_LINK_STAGE et autres). Un utilisateur peut donc acheminer son propre courrier par une autre branche que celle que l’opérateur a choisie par défaut. C’est le pouvoir voulu ; la conséquence est une règle pour l’opérateur :

Avertissement

N’énumérez jamais dans EDITABLE_STAGES une étape dont vous voulez que l’effet soit obligatoire. En particulier :

  • pepsi-stage-encrypt — ENCRYPT, SIGN et SECURE_LINK_STAGE décident si un message part en clair. Ne l’énumérez que si les utilisateurs peuvent choisir cela pour leur propre courrier.

  • pepsi-stage-dkim-sign, pepsi-stage-arc, pepsi-stage-srs — elles protègent la réputation du domaine, pas celle de l’utilisateur.

  • pepsi-stage-decrypt sur un chemin partagé — ses QUARANTINE_STAGE et ON_BAD_SIGNATURE acheminent le courrier entrant.

  • Les étapes anti-spam et de whitelist, si la politique du site est que le filtrage n’est pas facultatif.

  • Toute étape de remise ou de relais — leurs identifiants sont masqués, mais pas leurs cibles.

UNRESTRICTED_UNSAFE_STAGES = yes lève entièrement les restrictions sur PROGRAM et sur l’identité ; il transforme chaque étape modifiable en une étape que l’utilisateur contrôle entièrement.

14.9. L’adversaire réseau

Les valeurs par défaut mettent en échec un espion passif, pas un espion actif. Chaque saut utilise TLS lorsque le pair le propose, et chaque clé que l’échelle trouve est utilisée pour le chiffrement, mais un attaquant sur le chemin peut retirer STARTTLS, substituer un certificat ou (avec le résolveur) substituer une clé, partout où le pair n’a pas publié de politique qui l’interdit. C’est la posture de tout MTA opportuniste, et c’est ce qui permet au courrier de continuer à circuler vers la majorité des domaines, qui ne publient rien.

Un profil durci permet à un déploiement de résister à un attaquant actif partout où le pair a publié quelque chose à vérifier :

Paramètre

Ce qu’il ferme

Un résolveur validant sur un chemin de confiance (unbound sur l’interface de bouclage, ou systemd-resolved avec DNSSEC=yes)

Chaque vérification fondée sur DNSSEC ci-dessous dépend de l’honnêteté du bit AD ; sans résolveur validant, DANE ne trouve rien et rien ne déclenche de dégradation bruyante.

DANE = strict sur pepsi-stage-relay-to-internet (et sur chaque entrée de smarthost)

Un pair doté d’enregistrements TLSA signés par DNSSEC ne peut plus être intercepté : une non-concordance diffère au lieu de remettre.

MTA_STS laissé activé (la valeur par défaut)

Un pair doté d’une politique enforce ne peut plus être dégradé.

[pepsi-keydiscovery] MIN_TRUST = owner-confirmed (ou wkd)

Chiffrement uniquement pour des clés dont quelqu’un contrôlant la boîte aux lettres ou le domaine a prouvé la publication ; en dessous du plancher, le message part en clair ou vers le portail secure-link, selon ce que décide ENCRYPT.

DMARC_ENFORCE sur l’ingress

Le courrier qui falsifie le From: d’un domaine protégé par DMARC — y compris le vôtre — est refusé plutôt que remis avec un résultat en échec.

Publiez vos propres enregistrements TLSA, MTA-STS enforce et TLSRPT (pepsi-setup les affiche)

La même protection pour le courrier qui vous est envoyé, par les expéditeurs qui vérifient.

Même durci, un attaquant actif peut toujours provoquer un déni de service ; le profil convertit l’interception en report.

14.10. Disponibilité

L’objectif de disponibilité de Pepsi est plus étroit que son objectif de confidentialité, et il est énoncé comme une règle : face à des entrées hostiles, un message est différé plutôt que perdu, et un échec est fermé plutôt que divulgateur. Une étape qui ne peut pas décider met en attente ou fait échouer ce seul message ; elle n’arrête pas ses pairs, et elle n’envoie jamais en clair parce qu’une vérification n’a pas pu être faite. L’inondation volumétrique du réseau ou de l’hôte est hors du périmètre — elle relève de ce qui se trouve devant le MTA.

Dans ce périmètre, chaque ressource qu’un attaquant peut faire dépenser à Pepsi est bornée comme suit. Ouvert signale une lacune : elle est consignée dans docs/ROADMAP.md avec la correction retenue, et tant qu’elle n’est pas comblée, le composant n’est robuste que dans la mesure des limites qui l’entourent.

14.10.1. Ingress SMTP

Ressource

Limite

État

Connexions

MAX_CONNECTIONS (1024), plafonné en dessous de la limite de descripteurs de fichiers ; au plafond, la connexion la plus inactive de la plage d’adresses la plus chargée est évincée. Les nouvelles connexions sont limitées en débit par /32 (CONN_RATE_PER_SECOND, CONN_RATE_BURST), et une plage d’adresses peut détenir au plus MAX_CONNECTIONS_PER_IP (16) connexions à la fois. Le socket de submission local, exempté de la limitation de débit et de l’éviction, a son propre plafond (MAX_LOCAL_CONNECTIONS, 32), de sorte que les utilisateurs locaux ne peuvent pas affamer le SMTP distant.

Borné

Messages

MAX_MESSAGES_PER_SESSION (100) transactions par session, puis 421 et une reconnexion soumise à la limitation de débit. La submission locale est mesurée par uid SO_PEERCRED d’une session à l’autre (LOCAL_MESSAGES_PER_MINUTE, 60, rafale LOCAL_MESSAGE_BURST, 120), avec report par 451.

Borné

Contrôles des destinataires

MAX_INVALID_RECIPIENTS (10) destinataires inconnus par session, puis 421. Les sondes du MDA ou du backend sont mises en cache (une heure pour « oui », cinq minutes pour « non ») et plafonnées à 16 en cours ; au-delà du plafond, l’adresse est acceptée plutôt que mise en attente derrière le backend.

Borné

Taille et forme des messages

MAX_MESSAGE_SIZE (25 Mio) à MAIL SIZE=, pendant DATA et BDAT ; 1000 destinataires ; lignes de commande de 4 Kio, lignes de données de 1 Mio.

Borné

Temps

COMMAND_TIMEOUT (300 s, borne aussi la négociation TLS), DATA_TIMEOUT (180 s), tous deux par lecture.

Borné

Devinette de mots de passe

Trois tentatives AUTH échouées ferment la session avec 421, de sorte que chaque nouvelle série d’essais coûte une nouvelle connexion soumise à la limitation de débit ; d’une session à l’autre, une plage d’adresses peut échouer AUTH_FAILURES_PER_HOUR (20) fois par heure, après quoi AUTH est refusé sans que le mot de passe soit vérifié. Les destinataires SRS invalides sont plafonnés à trois par session.

Borné

Travail d’authentification

La limite de dix recherches de SPF, au plus 50 ensembles ARC, DNS_TIMEOUT par requête ; au plus MAX_DKIM_SIGNATURES (10) signatures vérifiées, en commençant par celles qui peuvent s’aligner sur From: ; SPF et DKIM simultanément sous VERIFY_TIMEOUT (20 s), puis DMARC sous un autre délai. Une vérification qui manque de temps est temperror pour cette seule vérification, ce que DMARC ignore sauf si elle est alignée — si bien que le délai ne peut pas servir à esquiver DMARC_ENFORCE.

Borné

14.10.2. La file d’attente et le pipeline

Ressource

Limite

État

Profondeur de la file et disque

[pepsi] MAX_QUEUE_ROWS (1 000 000 enregistrements), MAX_QUEUE_BYTES (désactivé par défaut) et MIN_FREE_SPACE (1 Gio sur FREE_SPACE_PATH) : pepsi-ingress répond 452 4.3.1 au RCPT, au DATA et après la fin des données tant que l’une de ces limites est atteinte, et pepsi-stage-list-post retient une diffusion au lieu d’émettre son lot suivant. Mesurées au plus toutes les QUEUE_CHECK_INTERVAL (10 s), les limites sont donc souples à hauteur des admissions d’un intervalle ; une mesure échouée admet. Le courrier que le pipeline émet lui-même (rebonds, DSN, avis) n’est jamais refusé, et une file qui dépasse déjà une limite se vide normalement.

Borné

Amplification du stockage

Le corps d’un message est stocké une seule fois dans pepsi.workqueue_body et partagé par chaque enregistrement qui en est scindé, si bien qu’un envoi à N destinataires coûte un corps et N petits enregistrements, et non N corps. Une étape qui réécrit un corps le copie pour ce seul enregistrement (copie sur écriture).

Borné

Équité

pepsi-dispatch partage le temps de worker de chaque étape entre les expéditeurs (un compte authentifié, sinon l’adresse qui se connecte, IPv6 par /64), en servant d’abord le moins récemment servi, de sorte que la rafale d’un expéditeur fait la queue derrière elle-même plutôt que devant tout le monde. [pepsi-dispatch] FAIR_FIFO_PERCENT (10) des créneaux de chaque étape vont malgré tout à ses enregistrements les plus anciens, ce qui borne la durée pendant laquelle un message peut attendre. Un expéditeur capable de se connecter depuis de nombreuses adresses compte pour de nombreux expéditeurs.

Borné

Un worker bloqué ou qui plante

[pepsi-dispatch] MAX_RUNTIME (300 s) tue le worker, et un plantage y met fin ; dans les deux cas le message reçoit une pénalité et est réessayé une minute (puis deux) plus tard, et la troisième pénalité le fait passer en timeout ou failed, deux états terminaux. Un message empoisonné est donc essayé trois fois, et non en boucle ; pepsi-failure-bouncer ne fait jamais rebondir de nouveau un enregistrement déjà à l’étape de rebond.

Borné

Nouvelles tentatives

MAX_LIFETIME (120 h) sur chaque étape, pour ses propres réessais et pour toute erreur qu’elle ne marque pas comme permanente ; PAYMENT_DEADLINE sur la barrière anti-spam.

Borné

Analyse

Profondeur MIME de 16–32 et nombre de parties ; texte en clair OpenPGP décompressé plafonné à 64 Mio pendant la lecture en flux ; trois couches de signature en clair au-dessus du texte chiffré ; chaînes X.509 de huit certificats, avec au plus 64 politiques ou correspondances de politiques par certificat dans l’arbre des politiques et 220 comparaisons nom-contre-sous-arbre par certificat lors de la vérification des contraintes de noms ; matériel de clés récolté et issu du gossip plafonné en nombre et en taille ; un certificat OpenPGP de toute autre personne portant une clé RSA de plus de 8192 bits est refusé (S/MIME s’arrête à 4096) ; la détection de langue lit au plus 20 000 caractères.

Borné

14.10.3. Boucles et amplification

  • Les boucles de courrier s’arrêtent à MAX_HOP_COUNT (30) sur les étapes de relais, à la garde de chaîne .forward, à une profondeur d’alias de 32 et à cinq sauts de liste. Un rebond ne rebondit jamais.

  • Les réponses automatiques — avis d’absence, défis du secrétariat et demande de paiement anti-spam — obéissent toutes aux règles de suppression de la RFC 3834 dans pepsi-common::autoreply, si bien qu’aucune ne répond à une liste, à un envoi en nombre, à un autre répondeur ou à un rapport de remise. Les avis d’absence sont en outre limités à un par correspondant par SUPPRESS_DAYS, les défis du secrétariat à cinq par expéditeur et par jour, et les demandes de paiement à une par expéditeur et par boîte aux lettres protégée par BLOCK_RESPONSE_SUPPRESS (une heure par défaut), le tout décidé et enregistré en une seule instruction, si bien que des workers parallèles ne peuvent pas envoyer tous les deux.

  • Les listes de diffusion multiplient un envoi par le nombre de leurs membres ; aussi, les envois d’un expéditeur vers une même liste au-delà de SENDER_POSTS_PER_HOUR (30) en une heure sont retenus pour le modérateur au lieu d’être distribués, et une liste retient au plus MAX_HELD_MESSAGES (1000) envois pour modération — au-delà, un envoi qui serait retenu est rejeté sans avis, le nouveau plutôt que le plus ancien, si bien qu’une inondation ne peut pas évacuer ce qu’un modérateur n’a pas encore vu. Les sauts de liste sont plafonnés comme ci-dessus.

  • Le courrier qu’envoient les formulaires web (inscription à une liste, réinitialisation de mot de passe) est limité à deux par heure par adresse cible, ainsi que par client, si bien que les formulaires ne peuvent pas servir à inonder la boîte aux lettres d’un tiers.

  • La découverte de clés répond une seule fois pour chaque adresse quel que soit le nombre de messages qui la demandent (les requêtes sont dédupliquées), mémorise les échecs (NEGATIVE_TTL, ERROR_TTL), plafonne une réponse à 256 Kio et à une redirection vers le même hôte, et fixe un délai à la réponse entière — un serveur qui distille les octets au compte-gouttes ne peut pas monopoliser une méthode de découverte, laquelle sert tour à tour toutes les autres recherches. Un message attend une clé au plus TIMEOUT avant de poursuivre sans elle.

14.10.4. Le tier web

Ressource

Limite

État

Connexions et clients lents

MAX_CONNECTIONS (256), au plus MAX_CONNECTIONS_PER_IP (32) depuis une même adresse client (une adresse IPv4 ou un /64 IPv6 ; les connexions passant par le socket UNIX d’un proxy inverse relèvent de la limitation du proxy) ; 30 s chacun pour les en-têtes, la négociation TLS et le corps de la requête.

Borné

Argon2id (PIN et mots de passe)

Une dérivation par CPU à la fois, hors de l’exécuteur ; une requête qui ne peut pas obtenir de créneau en quelques secondes est refusée avec 429 plutôt que mise en file. Le PIN secure-link est en outre limité à MAX_ATTEMPTS par message avec un LOCKOUT qui double ; les connexions sont limitées par compte et par source.

Borné

Corps de requête

8 Kio pour les formulaires, 1 Mio pour l’API d’administration, 8 Mio pour l’API des listes.

Borné

Données stockées

Les messages secure-link expirent (EXPIRY_DAYS). Des timers nommés par pepsi.target, indépendants du serveur web, élaguent les journaux (90 jours d”event_log, 30 de mail_log ; pepsi-log-prune.timer), les compteurs des rapports TLS (RETAIN_DAYS ; pepsi-tlsrpt-prune.timer) et les enregistrements expirés du cache d’adresses MX (toutes les heures, pepsi-origin-gc.timer).

Borné

14.10.5. Argent et boîtes aux lettres

  • L’auto-pay dépense au plus MAX_TOTAL par message que nous avons envoyé et, lorsque MAX_TOTAL_PER_DAY est défini, au plus ce montant par wallet sur toute période de 24 heures, les deux étant comptabilisés atomiquement en une seule instruction (sérialisée par wallet), si bien que des demandes simultanées — pour un message ou pour plusieurs — ne peuvent dépasser ni l’un ni l’autre, et un paiement qui ne parvient pas à être réglé est recrédité sur les deux. Sans la limite quotidienne, quelqu’un qui a reçu beaucoup de nos messages peut exiger MAX_TOTAL pour chacun d’eux ; elle devrait donc être définie partout où l’auto-pay l’est. Elle n’est délibérément pas par correspondant : les listes de diffusion et les alias rendent la partie qui exige impossible à connaître, alors que le wallet est toujours connu. Sous WALLET_SCOPE = unified, l’unique wallet appartient à tout le monde, si bien qu’un correspondant peut épuiser la journée pour tous les expéditeurs — un déni de paiement automatique (les demandes rebondissent alors comme d’habitude), pas une perte au-delà de la limite.

  • Les quotas de boîte aux lettres ne refusent que sur une mesure, jamais sur une estimation, et les trois règles qui empêchent une boîte pleine de rester fermée après que son propriétaire l’a vidée (un âge maximal pour la mesure sur laquelle agit l’ingress, une nouvelle mesure avant de refuser, et le timer pepsi-quota reconcile) se trouvent dans pepsi-quota.

14.11. Ce que Pepsi ne protège pas

  • Quoi que ce soit face à root ou à un opérateur malveillant. Voir Principaux et confiance accordée à chacun.

  • L’analyse de trafic. Qui correspond avec qui, quand et en quelle quantité est visible par quiconque peut voir le réseau, la file d’attente ou les journaux. Le chiffrement de bout en bout ne chiffre pas les enveloppes, et la protection des en-têtes ne couvre que ce que le conteneur permet.

  • Le texte en clair dans la file d’attente. Chaque étape doit lire le message qu’elle traite.

  • Les signatures face à un pipeline compromis, comme ci-dessus.

  • La compromission des correspondants eux-mêmes. Une clé publiée par un domaine compromis est une clé compromise, quel que soit le rang que l’échelle donne à ce domaine.

  • Les boîtes aux lettres des destinataires après la remise, à une chose près : pour un utilisateur disposant d’une clé client, le courrier que la passerelle a déchiffré est classé rescellé pour cette clé (Garde par le client (custody = client, la clé de MUA)).

  • La confidentialité des recherches vis-à-vis du domaine du correspondant. Découvrir une clé indique au DNS et à l’hôte WKD de ce domaine que quelqu’un ici s’apprête à écrire à l’adresse ; un serveur VKS apprend la même chose. ALLOW_DOMAINS / DENY_DOMAINS et DISCOVERY = no limitent cela (Gestion des clés). Les rapports TLS et la télémétrie sur consentement explicite ne divulguent que des décomptes agrégés ; la télémétrie est désactivée tant que l’opérateur ne l’active pas — dans un terminal, ou dans la console, qui ne peut la proposer qu’une fois que l’opérateur a démarré pepsi-telemetry-client et donné à la main un SYSTEM_ID à [pepsi].

14.12. Les garanties en un coup d’œil

« Oui » signifie que l’adversaire peut compromettre cet actif ; « Non » signifie qu’un mécanisme l’en empêche ; « Partiel » est expliqué dans la section nommée dans la première colonne.

Adversaire

Courrier en file

Clés privées E2E

Signer au nom d’un utilisateur / du domaine

Paramètres et courrier des autres utilisateurs

Argent

Identifiants

Courrier classé (utilisateurs à clé de MUA)

Réseau passif

Non

Non

Non

Non

Non

Non

Non

Réseau actif (valeurs par défaut)

Partiel (dégradation)

Non

Non

Non

Non

Non

Non

Expéditeur distant

Non

Non

Non

Non

Partiel (budget)

Non

Non

Utilisateur shell local

Non

Non

Non

Non

Non

Non

Non

Utilisateur limité au courrier

Non

Non

Non

Non

Son propre wallet uniquement

Non

Non

Mot de passe de messagerie volé

Non

Non

Partiel (sans second facteur)

Non

Son propre wallet uniquement

Non

Partiel (sans second facteur)

Administrateur délégué

Partiel (portée)

Non

Non

Partiel (portée)

Non

Non

Non

Copie de la base de données

Oui

Non

Non

Lecture seule

Non

Non

Non

Un fragment secret

Non

Non (KEK seule)

Partiel (selon le secret)

Non

Partiel (clé Origin)

Non

Non

Étape pepsi compromise

Oui

Non

Oui

Oui

Partiel (budget)

Non

Partiel (courrier futur)

Étape cryptographique compromise

Oui

Oui (garde par la passerelle)

Oui

Oui

Partiel (budget)

Non

Partiel (courrier futur)

pepsi-httpd compromis

Partiel (enveloppes, routage ; aucun contenu)

Non

Non

Partiel (routage ; aucun paramètre)

Non

Ses propres tables

Non

Opérateur / root

Oui

Oui (garde par la passerelle)

Oui

Oui

Oui

Oui

Partiel (courrier futur)

« Partiel (sans second facteur) » pour un mot de passe de messagerie volé : sans second facteur, le voleur peut enregistrer sa propre clé comme celle de l’utilisateur, ce qui fait vérifier des signatures comme étant celles de l’utilisateur et fait sceller le courrier futur — courrier classé compris — pour une clé qu’il détient ; avec un second facteur enrôlé, chaque changement de clé exige un code (Quelqu’un qui détient le mot de passe de messagerie d’un utilisateur).

Le courrier classé est un message que la passerelle a déchiffré puis rescellé pour la propre clé client du destinataire avant la remise (pepsi-stage-reencrypt, voir Garde par le client (custody = client, la clé de MUA)). « Partiel (courrier futur) » : un adversaire qui contrôle le pipeline, une étape cryptographique ou l’hôte peut lire — ou cesser de resceller — le courrier qui transite pendant qu’il a le contrôle, et ne peut jamais ouvrir une copie déjà classée, puisqu’aucun processus du serveur ne détient la clé client. Pour un utilisateur sans clé de MUA, la colonne ne s’applique pas : son courrier est classé en clair, ou refusé sous ON_NO_CLIENT_KEY = bounce.

La matrice couvre ce qui est détenu ici. La clé avec laquelle le courrier futur destiné à un correspondant est chiffré relève de Clés des correspondants : un expéditeur distant capable de falsifier le From: de ce correspondant peut remplacer une clé trust-on-first-use (Partiel — toujours audité et annoncé aux destinataires ; refusé tant que la clé stockée est vivante — non révoquée, et pas au-delà de l’expiration qu’elle déclare elle-même — sous ACCEPT_ROTATION = expired), et ne peut jamais remplacer une clé épinglée, publiée ou saisie par l’opérateur.

14.13. Voir aussi

Modèle de sécurité — les mécanismes derrière chaque « Non » ci-dessus. Gestion des clés — la garde, l’échelle de confiance, la découverte. Le portail de repli par lien sécurisé — le repli sans clé et son modèle de PIN. L’API d’administration — les portées et l’applicateur. Configuration — les couches sur lesquelles repose la frontière des paramètres.