9. Fonctionnalités prises en charge¶
Les fonctionnalités de niveau protocole et politique que Pepsi prend en charge, avec la RFC qui régit chacune : la machinerie SMTP et d’authentification, la cryptographie de bout en bout, et la surface d’exploitation qui entoure les deux. L”Index des RFC met en correspondance chaque RFC avec le code et la configuration ; les chapitres par programme indiquent quel programme réalise chaque fonctionnalité.
9.1. Réception SMTP entrante¶
pepsi-ingress est un serveur SMTP conforme aux normes (RFC 5321) avec ces extensions :
Sécurité du transport. En clair, mise à niveau STARTTLS (RFC 3207) et TLS implicite (RFC 8314, par exemple le port submissions 465) — sélectionnés par listener via
MODE. Des listeners de soumission (RFC 6409,SUBMISSION = yes) peuvent être liés — voir Soumission de messages.Transfert du corps du message.
DATAclassique et CHUNKING/BDAT (RFC 3030) pour les messages volumineux.BINARYMIMEn’est pas annoncé, et unMAIL FROMportantBODY=BINARYMIMEest refusé par501 5.5.4.Annonce et application de SIZE (
MAX_MESSAGE_SIZE; taille excessive → 552).PIPELINING et ENHANCEDSTATUSCODES.
8BITMIME (RFC 6152) et SMTPUTF8 (RFC 6531) — voir Contenu internationalisé et 8 bits.
DSN (RFC 3461) — voir Notifications d’état de remise.
SMTP AUTH (RFC 4954) pour la soumission — voir Authentification du client.
Politique d’acceptation : le courrier n’est accepté que pour
ACCEPTED_DOMAINS(les autres sont rejetés comme relayage avec550) ; la boîte aux lettres<Postmaster>sans domaine est toujours acceptée (RFC 5321 §4.5.1) ; les destinataires qui sont des jetons SRS valides sont décodés en sens inverse et relayés (voir Sender Rewriting Scheme (SRS)).Durabilité : le client ne reçoit
250qu’une fois la ligne validée en base ; si la base de données est indisponible, le client reçoit un451transitoire et réessaie.
9.2. En-têtes de trace¶
L’ingress ajoute en tête un en-tête Received: RFC 5321 §4.4 à chaque message avant l’authentification (de sorte que le sceau ARC le couvre). La clause with note le type de transmission — SMTP, ESMTP, ESMTPS, ESMTPA ou ESMTPSA — conformément à la RFC 3848 (l”ESMTPA nu, authentifié mais non sur TLS, est la socket UNIX de soumission locale), et une clause for nomme le destinataire pour les transactions à destinataire unique.
9.3. Authentification du client¶
Chaque listener peut authentifier le client connecté et marquer son courrier comme d’origine locale (ce qui lui permet de relayer plus loin vers n’importe quel domaine et appose ESMTPSA dans la trace Received:). Une session est authentifiée lorsque l’un quelconque de quatre mécanismes par listener réussit :
l’IP du pair est dans le
MYNETWORKSdu listener (considérée digne de confiance d’emblée) ;un échange SMTP AUTH réussit (RFC 4954,
PLAIN/LOGIN), vérifié auprès d’un backend SASL configuré (SASL_TYPE/SASL_PATH; la socket auth-client de Dovecot aujourd’hui).AUTHn’est annoncé et accepté que via TLS — une tentative en clair est refusée par504 5.5.4, le code que la RFC 4954 §4 prescrit pour un mécanisme qui « exige une couche de chiffrement » (le §1 déprécie l’ancien538) ;un certificat client TLS présenté correspond à une clé/épingle de CA configurée (
TLS_AUTH_CLIENT) ;le pair d’une socket UNIX est identifié par
SO_PEERCRED(AUTH_PEERCRED, la socket de soumission locale) et son login est associé à au moins une adresse. Celui-ci porte un nom d’utilisateur, il alimente donc la même application deUSERNAME_MAPqu’une session SASL.
Ceci est distinct de l”Authentification à la bordure ci-dessous, qui vérifie le message (SPF/DKIM/DMARC) indépendamment de la manière dont — ou du fait que — le client lui-même s’est authentifié. Voir pepsi-ingress.
9.4. Soumission de messages¶
Un listener marqué SUBMISSION = yes se comporte comme un Message Submission Agent (RFC 6409) plutôt que comme un MX :
l’authentification est obligatoire — un
MAIL FROMnon authentifié est refusé par530 5.7.0(donc l’indicateur exige l’un des mécanismes d’authentification client ci-dessus, et TLS sur tout écouteur qui n’est pas une socket locale du domaine UNIX) ; etdes corrections de soumission sont appliquées à chaque message accepté : un
Date:manquant (§8.2) et unMessage-ID:manquant (§8.3) sont ajoutés avant le stockage du message ; etl”application de l’identité de soumission (§6) via
USERNAME_MAP: leMAIL FROMd’enveloppe et l’en-têteFrom:doivent être des adresses que l’utilisateur authentifié a le droit d’utiliser (sinon550), et unFrom:autorisé qui diffère de l’identité canonique de l’utilisateur obtient un en-têteSender:qui la nomme (§8.1).
L’indicateur n’a aucun effet sur un listener MX.
9.5. Authentification à la bordure¶
Avant de stocker un message, l’ingress l’authentifie afin que le destinataire final puisse encore voir le verdict que Pepsi a atteint à la bordure (la redirection cassera les signaux SPF/DKIM originaux) :
SPF (RFC 7208) sur l’identité
MAIL FROMen utilisant l’IP du client.Vérification de signature DKIM (RFC 6376 / RFC 8463).
Évaluation DMARC (RFC 7489), avec application facultative au moment SMTP (
DMARC_ENFORCE→550sur un échec certain sous une politiquequarantine/reject). L’authentification est sinon fail-open : une erreur DNS ou d’analyse est notée et le courrier accepté.iprev / FCrDNS (RFC 8601) — le PTR du client confirme-t-il en sens direct vers son IP.
Un en-tête Authentication-Results (RFC 8601) notant tout ce qui précède, estampillé du nom d’hôte de l’ingress comme
authserv-id.
9.6. Authenticated Received Chain (ARC)¶
L’étape pepsi-stage-arc implémente ARC (RFC 8617). Elle vérifie toute chaîne ARC entrante et scelle le message en tant que notre ADMD en ajoutant en tête un ensemble ARC-Authentication-Results / ARC-Message-Signature / ARC-Seal, signé avec l’unique ARC_ALGORITHM (ARC permet une signature par saut). Cela permet à un destinataire en aval de faire confiance au verdict de bordure après que la redirection a cassé l’alignement SPF/DKIM. Le scellement est fail-open. Comme ARC préserve l’authentification d’un expéditeur en amont à travers le saut de redirection, il ne s’applique qu’au courrier que Pepsi reçoit : les soumissions d’origine locale (state.local_origin) sont ignorées — elles sont authentifiées comme domaine auteur par l’étape de signature DKIM — de sorte que le pipeline généré place ARC sur la branche entrante, après la scission state.local_origin.
9.7. Sender Rewriting Scheme (SRS)¶
L’étape pepsi-stage-srs réécrit l’expéditeur d’enveloppe en une adresse locale d’un SRS_DOMAIN contrôlé par Pepsi qui encode en HMAC l’expéditeur original (le MAC tronqué est encodé en base32, RFC 4648), de sorte que SPF réussit au saut suivant. L’expéditeur nul et une adresse déjà dans le domaine SRS sont laissés inchangés ; une adresse déjà SRS est re-signée sous la forme compacte SRS1. L’ingress effectue le sens inverse : un rebond renvoyé à une adresse SRS valide est vérifié et relayé vers l’expéditeur original, tandis qu’un jeton falsifié ou expiré est rejeté (550).
9.8. Chiffrement et signature de bout en bout¶
pepsi-stage-encrypt chiffre un message soumis localement pour chaque destinataire, en OpenPGP (PGP/MIME, RFC 3156) ou en S/MIME (CMS, RFC 8551 / 5083), signé avec la clé propre de l”auteur du From:. La signature est placée à l’intérieur du texte chiffré.
ENABLE_PEP (activé par défaut) est un préréglage de valeurs par défaut calqué sur le projet pretty Easy privacy : chaque expéditeur local d’un domaine servi obtient automatiquement une clé OpenPGP, un message n’est signé que lorsqu’il est chiffré (SIGN = encrypted-only), et le courrier en clair porte la clé de l’expéditeur dans un en-tête Autocrypt: mais aucune signature. Toute option écrite explicitement l’emporte toujours ; ENABLE_PEP = no rend la signature opportuniste et désactive la création automatique de clés.
Chaque destinataire chiffré obtient son propre texte chiffré sur sa propre ligne de file d’attente : il n’y a aucune divulgation de la liste des destinataires, aucune clé de contenu partagée entre destinataires, et une fuite de Bcc est structurellement impossible. Les destinataires qui partagent une issue partagent une ligne.
Les clés des destinataires proviennent du magasin de Gestion des clés, alimenté par la couche de découverte (WKD, VKS, DANE, LDAP, moisson depuis le courrier entrant). L’étape elle-même ne fait aucune E/S réseau : un destinataire dont la clé n’est pas en cache met le message en pause jusqu’à ce qu’un service de découverte règle la demande, et un destinataire ayant une entrée récente de cache négatif prend aussitôt le chemin sans clé.
Ce qu’il advient d’un destinataire sans clé relève de la politique — ON_NO_KEY vaut cleartext, secure-link ou bounce, et prend sa valeur par défaut d”ENCRYPT plutôt que d’en porter une propre. ENCRYPT = required ne se dégrade jamais en clair, et aucune issue en clair n’est jamais silencieuse — chacune est journalisée et notée par destinataire dans state.crypto.out, avec le conteneur de contenu réellement employé et l’indication d’une éventuelle rétrogradation.
L’étape s’exécute avant la signature DKIM, de sorte que DKIM couvre les octets réellement transmis ; voir Configuration pour comprendre pourquoi cet ordre n’est pas facultatif.
9.9. Déchiffrement et vérification côté serveur¶
pepsi-stage-decrypt en est la moitié entrante. Pour un message adressé à un destinataire que cet hôte sert, elle ouvre tout texte chiffré que le message porte, vérifie toute signature qu’il porte, note le verdict et retire tout indicateur de sécurité que l’expéditeur aurait falsifié. L’utilisateur lit du courrier ordinaire dans son client habituel, sans aucun greffon. L’exception est le courrier chiffré vers une clé que l’utilisateur a enregistrée depuis son propre client de messagerie (une clé sous garde du client, dont Pepsi ne détient jamais la moitié privée) : celui-ci est transmis sans être ouvert, pour que le client le déchiffre.
Elle ne s’exécute que pour les destinataires que cet hôte sert, et n’essaie même pas pour les autres. Le déchiffrement réécrit le corps, ce qui invalide la signature DKIM de l’expéditeur ; c’est sans conséquence pour un message sur le point d’être classé dans une boîte aux lettres ici, et ce ne l’est pas pour un message relayé plus loin. Un message comportant les deux sortes de destinataires est scindé, de sorte que la copie réacheminée soit celle qui est arrivée.
Le vocabulaire des verdicts est précis. valid signifie que la signature est saine et que la clé était ancrée — une chaîne X.509 vers une autorité de certification configurée, ou une clé OpenPGP issue d’une source de découverte classée. valid-untrusted signifie saine avec une clé qui n’a pu être rattachée à rien, ce qui est l’état de la majeure partie du courrier signé du monde et n’est jamais rapporté comme valid. invalid, unverifiable et none complètent l’ensemble, et lorsqu’un message porte plusieurs signatures sur le même contenu, le pire d’entre eux l’emporte. (Une signature faite à l’extérieur du chiffré est une classe à part : elle ne répond du message que lorsque rien ne signe le texte en clair, et jamais mieux alors que valid-untrusted — voir pepsi-stage-decrypt.) Seul valid positionne state.signature_verified, ce à quoi pepsi-stage-check-whitelist conditionne une ligne de liste blanche.
Les deux chemins d’échec remettent par défaut, conformément à la posture fail-open de Pepsi sur le SPF, le DKIM et le DMARC entrants : un message que nous n’avons pas pu ouvrir est remis toujours chiffré (l’utilisateur détient peut-être la clé dans son propre client), et une mauvaise signature est un verdict noté plutôt qu’un échec de remise (les listes de diffusion qui réécrivent les corps en produisent sur du courrier parfaitement légitime). Des routes de quarantaine et de rebond existent pour les déploiements qui les veulent.
Le résultat parvient à l’utilisateur de deux façons : un en-tête X-Pepsi-Crypto à la grammaire documentée et — par défaut — un [decrypted][verified] ajouté en tête du Subject dans l’ordre d’imbrication, ce qui est pour la plupart des utilisateurs le seul signal qu’ils verront jamais. Les deux reposent sur la même discipline : chaque champ X-Pepsi-* et chacune de nos propres marques de sujet est retiré du courrier entrant avant que le nôtre ne soit ajouté. Un en-tête privé est falsifiable par définition, et ce retrait est toute la base sur laquelle on peut y croire.
Le matériel de clé qu’un message porte — certificats de signataire S/MIME, parties application/pgp-keys, en-têtes Autocrypt — est lu en mémoire pour vérifier la signature de ce message-là, parce que la plupart du courrier signé porte le certificat qui l’a signé. Lorsque ce n’est pas le cas, le message se met en pause sur une demande de découverte plutôt que de bloquer sur un serveur de clés, de sorte que même le premier message d’un nouveau correspondant obtienne un verdict réel.
L’étape de déchiffrement n’en stocke rien. Tout l’apprentissage des clés de pairs est une étape à part entière, pepsi-stage-autocrypt-learn, placée après le verdict de spam — ce qui est précisément ce qui rend applicable la règle « ignorer les messages que le MUA croit être du spam » du §5.3 d’Autocrypt Level 1 (LEARN_FROM_SPAM, désactivé par défaut). Un pipeline entrant sans cette étape vérifie parfaitement bien les signatures et n’apprend jamais la clé d’un correspondant.
Ce que l’étape de déchiffrement ouvre est déposé en clair — sauf si le destinataire a enregistré sa propre clé de client de messagerie, auquel cas pepsi-stage-reencrypt, placée immédiatement avant la remise locale, le scelle avec cette clé une fois que toutes les étapes qui le lisent ont terminé. Une politique par destinataire (ON_NO_CLIENT_KEY) peut refuser le courrier d’un utilisateur qui n’a pas de telle clé plutôt que de le déposer lisible.
9.10. Gestion, découverte et publication des clés¶
Aucune des deux étapes cryptographiques ne fait d’E/S réseau propre. Toutes deux puisent dans un magasin de clés du schéma partagé, alimenté dans deux directions : par pepsi-keydisc — une instance de service par méthode de découverte, de sorte qu’un serveur de clés lent ne retarde personne — et par pepsi-stage-autocrypt-learn à partir du courrier qui arrive. Une étape qui a besoin d’une clé dont elle ne dispose pas valide ce qu’elle peut conserver, met le message en pause et met une demande en file ; le service qui répond libère, dans le même aller-retour, chaque message garé sur cette adresse.
Sources de découverte, chacune une consultation distincte exécutée simultanément avec son propre rang sur une échelle de confiance : le Web Key Directory (formes avancée et directe), DANE
OPENPGPKEY(RFC 7929) etSMIMEA(RFC 8162) — acceptés seulement lorsque le bit AD du résolveur indique que la réponse a été validée par DNSSEC —, le protocole VKS d’un serveur de clés vérificateur, et LDAP. Au-dessous de toutes sur l’échelle se trouvent les deux échelons qui ne sont pas des services de découverte et n’ont aucune instance : le matériel moissonné dans le courrier entrant (certificats de signataire S/MIME, partiesapplication/pgp-keyset en-têtesAutocrypt), et — un échelon plus bas encore, tout en bas — la clé d’un tiers introduite parAutocrypt-Gossip:dans un message arrivé chiffré. Les deux sont écrits sur place par pepsi-stage-autocrypt-learn ; nommer l’un ou l’autre dansSOURCESest refusé.Publication des clés de nos propres utilisateurs : les points de terminaison WKD servis par pepsi-httpd, les enregistrements
OPENPGPKEY/SMIMEAimprimés par pepsi-keys et pepsi-setup, et — uniquement sur demande, par identité — le téléversement vers un serveur de clés vérificateur, dont pepsi-stage-vks-confirm répond au courrier de confirmation.Garde. Le matériel privé est stocké enveloppé sous une clé de chiffrement de clés qui réside hors de la base de données, et n’est atteignable que par l’unique rôle de base sous lequel s’exécutent les étapes cryptographiques.
Un cache négatif fait que seul le premier message vers ou depuis un correspondant inconnu doit jamais attendre.
Gestion des clés est le chapitre consacré à tout cela ; la référence des options est pepsi.conf(5).
9.11. Le repli par lien sécurisé¶
La plupart des correspondants ne publient aucune clé : ENCRYPT = required ne laisserait sinon que « le faire rebondir » ou « l’envoyer en clair ». Le portail de lien sécurisé est la troisième réponse : pepsi-stage-secure-link dépose le message sur le serveur — chiffré sous un PIN fraîchement généré — et envoie au destinataire un lien, tandis que le PIN lui parvient par un autre canal (par défaut il est envoyé à l”expéditeur, qui le transmet). Le destinataire lit le message dans un navigateur, peut télécharger ses pièces jointes et, là où l’opérateur l’autorise, répondre.
La clé de contenu est un Argon2id sur le PIN, un sel par message et un poivre qui ne réside que dans secrets.d, et seul le texte chiffré AEAD est stocké — de sorte qu’une base de données volée ne donne rien, un serveur volé ne donne rien, et aucun administrateur ne peut lire un message stocké. Le sujet voyage à l’intérieur du texte chiffré avec le corps ; l’expéditeur, le destinataire, les horodatages et les compteurs de cycle de vie sont en clair parce que la remise, l’expiration et la question « est-ce arrivé ? » en ont besoin. Un PIN perdu est un message perdu, et la voie de récupération est que l’expéditeur renvoie.
Les messages sont rendus en texte, jamais en HTML, de sorte qu’il n’y a aucun assainisseur à contourner et aucun contenu distant qui appelle l’extérieur ; un PIN erroné est borné par un verrouillage par jeton et une limitation de débit par source ; et une réponse est injectée sans state.local_origin et adressée uniquement à l’expéditeur stocké, de sorte que le formulaire public ne puisse pas devenir un relais ouvert. Voir Le portail de repli par lien sécurisé pour le déroulement, le modèle de menace et la configuration.
9.12. Signature DKIM sortante¶
L’étape pepsi-stage-dkim-sign ajoute en tête des signatures DKIM (RFC 6376) — par défaut à la fois une signature RSA-2048 et une Ed25519 (RFC 8463), ou l’une des deux seulement selon [pepsi] DKIM_ALGORITHMS — sous le SIGNING_DOMAIN ou le domaine du From: du message. Gmail, Microsoft 365 et Yahoo ne vérifient pas Ed25519, et Google indique une telle signature comme fail dans ses rapports agrégés DMARC ; DMARC réussit malgré tout grâce à la signature RSA. La signature couvre toujours le corps entier : le tag de longueur de corps l= de la RFC 6376 n’est jamais émis, car les vérificateurs stricts rejettent d’emblée une telle signature au lieu de tolérer la couverture plus courte. La signature est fail-closed : un message dont le domaine de signature n’a pas de clés, ou qui ne peut pas être signé, est marqué failed au lieu d’être transmis non signé. Comme la sélection d’en-têtes se fait de bas en haut, la signature ne perturbe pas les signatures existantes.
9.13. Notifications d’état de remise¶
Pepsi implémente DSN de bout en bout (RFC 3461 / RFC 3463 / RFC 3464) :
L’ingress annonce
DSNet valide/stockeRET/ENVID(surMAIL FROM) etNOTIFY/ORCPT(surRCPT TO) sousstate.dsn.Les étapes de relais propagent ces paramètres à un saut suivant qui annonce aussi
DSN, et les omettent sinon (RFC 3461 §6 — Pepsi ne devient pas le relais responsable du DSN).pepsi-stage-bounce émet le rapport sous forme de
multipart/reportRFC 3464 avec des codes d’état étendus RFC 3463 :un rapport d”échec lorsque
NOTIFYdemandeFAILURE(la valeur par défaut en son absence) —NOTIFY=NEVERabandonne silencieusement ;un rapport de succès (
Action: delivered) uniquement lorsque leORIGINATE_SUCCESS_DSNglobal est activé et queNOTIFY=SUCCESSa été demandé ;un rapport de délai (
Action: delayed) lorsque leDELAY_DSN_AFTERd’une étape de relais s’écoule sur un message encore en file d’attente qui a demandéNOTIFY=DELAY(envoyé au plus une fois).
Un message à expéditeur nul (un rebond) n’est jamais lui-même renvoyé en rebond (RFC 5321 §6.1).
9.14. Contenu internationalisé et 8 bits¶
L’ingress annonce 8BITMIME (RFC 6152) et SMTPUTF8 (RFC 6531) et note la déclaration BODY=. Comme les capacités d’un saut suivant sont inconnues jusqu’après la connexion, le client SMTP sortant décide par saut :
Si le saut prend en charge l’extension, Pepsi ré-annonce
BODY=8BITMIME/SMTPUTF8.Sinon il rétrograde : les parties MIME terminales 8 bits sont ré-encodées vers un codage de transfert 7 bits (RFC 2045 — quoted-printable pour
text/*, base64 sinon), d’après les octets constatés plutôt que d’après l’encodage déclaré ; les champs d’en-tête UTF-8 sont réécrits en mots encodés RFC 2047, aux trois endroits où le §5 de la RFC 2047 en autorise un (un champ non structuré, une phrase de nom d’affichage, l’intérieur d’un commentaire) ; un paramètreContent-Type/Content-Dispositionnon ASCII — le nom de fichier d’une pièce jointe, dans n’importe quelle partie MIME — prend la forme RFC 2231 (filename*=UTF-8''caf%C3%A9.txt), et unboundary=non ASCII, qui n’a pas de forme encodée, est remplacé par un nouveau délimiteur ASCII, lignes de délimitation comprises.Les octets rétrogradés sont ensuite retestés : la conversion se fait au mieux sur un message que personne n’a validé, et le §3 de la RFC 6152 interdit « en toutes circonstances » d’offrir du contenu 8 bits à un saut qui n’a pas annoncé
8BITMIME; un corps encore 8 bits après coup est donc un échec permanent (rebond).Une adresse non ASCII (d’enveloppe, ou à l’intérieur d’une adresse d’en-tête) qui ne peut être représentée vers un saut non-
SMTPUTF8est un échec permanent (rebond).
Le ré-encodage du corps casse nécessairement une signature DKIM/ARC couvrant le corps ; c’est inévitable (les capacités sont liées tardivement) et rare.
9.15. Relais sortant¶
Deux étapes de relais interchangeables envoient le courrier vers l’extérieur :
pepsi-stage-relay-to-internet — remise directe vers le MX (RFC 5321 §5) : sa propre résolution
MXavec ordonnancement par préférence et sélection d’adresse Happy-Eyeballs (mise en cache par adresse danspepsi.dns_address), MX implicite via les enregistrements d’adresse (§5.1) et gestion du Null MX (RFC 7505). Le TLS est authentifié par MTA-STS (RFC 8461) avec vérification de l’identité du certificat (RFC 6125) ; les politiquesenforceexigent STARTTLS vers un MX listé.pepsi-stage-relay-to-smarthost — relais via un smarthost configuré, choisi par domaine de destinataire (ou un attrape-tout), avec transport par MTA (
plain/tls/starttls), vérification de certificat et la suite SMTP AUTH (RFC 4954) complète —PLAIN/LOGIN,CRAM-MD5,SCRAM-SHA-1/-SHA-256(avec liaison de canal-PLUSfacultative),OAUTHBEARER/XOAUTH2, SASLEXTERNAL(certificat client TLS),NTLM,GSSAPI/Kerberos et leDIGEST-MD5déprécié — ou négociationauto. Voir Authentification du client et l”index des RFC.
Les deux étapes appliquent la protection contre les boucles, en comptant les en-têtes Received: du message par rapport à leur propre option MAX_HOP_COUNT — c’est un réglage d’étape de relais, dans la section [stage-<name>] de chaque étape, et non un réglage de l’ingress, de sorte qu’un message est arrêté au moment où il est sur le point d’être renvoyé plus loin plutôt qu’à son arrivée.
Les deux étapes authentifient le TLS du saut suivant avec DANE (RFC 7672, DANE = off|warn|strict) — en résolvant les enregistrements TLSA du saut et en les comparant à la chaîne présentée, avec priorité sur MTA-STS — et, lorsque [pepsi-tlsrpt] SEND_REPORTS est activé (il est désactivé par défaut), notent chaque session TLS sortante pour le TLS Reporting (RFC 8460) ; pepsi-tlsrpt expédie les rapports agrégés quotidiens. Elles partagent une sémantique de réessai : un échec transitoire met le message en pause avec un backoff exponentiel (RETRY_INITIAL/ RETRY_MAX_INTERVAL/RETRY_FACTOR) jusqu’à MAX_LIFETIME, après quoi il rebondit ou est mis en échec ; un échec permanent achemine vers BOUNCE_STAGE (ou marque la ligne failed).
9.16. Remise locale¶
Pour les destinataires d’un domaine local, trois étapes classent le courrier sur l’hôte au lieu de le relayer (chacune transfère les destinataires qu’elle ne peut traiter à son NEXT_STAGE, de sorte qu’elles se composent) :
pepsi-stage-relay-to-maildir — écrit le message directement dans le
Maildir/new/de chaque utilisateur local. La localité est décidée parLOCAL_DOMAINSetTARGETS(plages d”uidpasswd) ; l’écriture privilégiée est faite par lepepsi-helper-maildir-writersetuid-root, atteint via le bit set-group-idpepsi-maildirpropre à l’étape.pepsi-stage-relay-to-lmtp — remet le message à un Mail Delivery Agent local (typiquement Dovecot) via LMTP (RFC 2033), qui exécute le filtre Sieve (RFC 5228) de chaque destinataire lors du classement du courrier. Elle remet tous les destinataires en une transaction et achemine plus loin chacun que le MDA rejette selon son état RFC 3463. N’a besoin d’aucun helper privilégié (le MDA abandonne ses privilèges).
pepsi-stage-dot-forward — traite le fichier
~/.forwardde chaque utilisateur local (le mécanisme sendmail/Postfix classique) via lepepsi-helper-dot-forwardsetuid-root, qui abandonne ses privilèges au profit de l’utilisateur avant de le lire : les adresses redirigées redémarrent le pipeline, les directives|pipe//files’exécutent en tant que l’utilisateur, et un destinataire sans~/.forwardpasse sans changement.
pepsi-stage-aliases complète celles-ci en étendant les destinataires d’enveloppe à travers une table de style virtual(5) de Postfix (adresse complète ou attrape-tout @domain, étendue transitivement) avant la remise locale.
pepsi-stage-vacation répond au courrier qui arrive pendant l’absence d’un destinataire, et marque le sujet de la copie transmise afin qu’à son retour il voie à quel courrier il a été répondu pour lui. C’est la seule action de filtrage utilisateur que Pepsi met en œuvre lui-même plutôt que de la laisser au Sieve d’un MDA, parce que la réponse est un nouveau message qui doit être signé et relayé — et parce que décider de ne pas en envoyer une relève de la connaissance du pipeline : la RFC 3834 dit de ne jamais répondre à un rebond, au courrier de liste de diffusion, à tout ce qui est déjà marqué automatique, ni à une adresse de service, et Pepsi y ajoute son propre « ne jamais répondre au spam » et une limitation de débit par correspondant. Les dates de congé sont de la configuration par adresse ordinaire, de sorte que la même option est un jour férié national à portée globale et le congé d’une personne dans sa propre ligne pepsi.settings.
pepsi-stage-route les complète différemment : au lieu de remettre, elle choisit un saut suivant par destinataire d’après le domaine de ce destinataire, scindant le message lorsque ses destinataires divergent. C’est ce qui permet à Pepsi de se placer devant un système de courrier existant — les domaines situés derrière la passerelle vont vers ce système, le reste vers l’internet — et c’est là que pepsi-setup prouve qu’aucun domaine servi par cet hôte ne peut être réacheminé vers son propre ingress. Voir Microsoft Exchange comme passerelle.
9.17. Listes de diffusion et archives¶
Le sous-système de listes de diffusion de Pepsi est une réimplémentation de GNU Mailman 3 : son modèle de données, son architecture rule/chain et handler/pipeline, son API REST, son vocabulaire de commandes par e-mail et les noms de ses modèles de notification relèvent de la conception du projet GNU Mailman, dont la Free Software Foundation détient les droits, et les fichiers repris de GNU Mailman, Postorius ou HyperKitty restent sous licence GNU General Public License (vendor/PEPSI-VENDORING.md les recense). Le test d’acceptation est qu’un mailmanclient, un Postorius ou un HyperKitty non modifié le pilote. Voir Listes de diffusion, Archives et L’API REST de GNU Mailman 3.
Cinq étapes de pipeline plutôt qu’un démon dédié : routage, envoi, remise par membre, commandes par e-mail et traitement des rejets – un message de liste est ainsi un enregistrement de file ordinaire que ARC, DKIM, SRS, TLS et les étapes de relais traitent déjà. Comment le sous-système est agencé en donne la carte.
Neuf adresses par liste et aucun fichier d’alias. Mailman écrit des tables Postfix ou Exim pour les neuf adresses de chaque liste et recharge le MTA ; Pepsi route sur le destinataire à l’intérieur de son propre pipeline, il n’y a donc rien à régénérer et rien qui puisse se désynchroniser.
Une modération au vocabulaire d’amont : dix-huit règles, quatre chaînes terminales (
accept,hold,reject,discard) et un pipeline de dix-sept gestionnaires, sous les noms que l’API REST rapporte.En-têtes ``List-*`` (RFC 2369) et ``Archived-At`` (RFC 5064) sur chaque message de liste.
Désabonnement en un clic (RFC 8058) –
List-Unsubscribe-Postet une URIhttps:scellée par une clé, honorée par un uniquePOST. Amont ne l’a pas : c’est le seul endroit où Pepsi va plus loin.Les résumés dans les deux formats : le résumé simple de la RFC 1153 et le
multipart/digestMIME, par abonné, avec numérotation de volume et de livraison, un seuil de taille et une minuterie périodique.Le traitement des rejets avec la logique de score d’amont, l’attribution VERP sur chaque copie (les dix-sept détecteurs heuristiques sont donc le recours et non le mécanisme), des messages de sonde facultatifs, des avertissements puis, à terme, le désabonnement.
Des archives – une réimplémentation de HyperKitty qui conserve son hachage de Message-ID et son schéma d’URL, de sorte que les liens d’archives existants survivent à une migration – avec recherche plein texte, recherche de sous-chaîne par trigrammes facultative, import et export mbox, et une suppression définitive (
purge) qui laisse une pierre tombale.La migration depuis Mailman 2.1 et 3 : la
config.pckde 2.1 est lue par une machine pickle restreinte, incapable d’instancier une classe (un pickle non fiable est donc une donnée et non du code), et un serveur Mailman 3 est lu via sa propre API REST.Aucun JavaScript sur aucune page de liste ou d’archives, et aucun
403sur une surface publique : un refus est le même404qu’obtient une liste inexistante, car un403révèle qu’elle existe.
9.18. Courrier non sollicité¶
Pepsi filtre après avoir accepté un message ; tout ce qui suit décide donc du sort d’un courrier déjà dans la file. Trois mécanismes coopèrent au travers d’un unique verdict, state.spam, et d’une unique table, pepsi.whitelist :
La liste blanche des correspondants. pepsi-stage-auto-whitelist consigne les destinataires du courrier qu’envoient vos utilisateurs ; pepsi-stage-check-whitelist reconnaît leurs réponses (éventuellement uniquement lorsque DKIM, une signature vérifiée ou un scelleur ARC nommé s’en porte garant) et définit
state.spam = false. L’opérateur et les utilisateurs peuvent la gérer à la main avec pepsi-whitelist.Péage à l’envoi (pepsi-stage-anti-spam) : le courrier de toute autre personne est retenu jusqu’à ce que son expéditeur paie un petit montant GNU Taler.
Confirmation à l’envoi (pepsi-stage-secretary) : le courrier de toute autre personne est retenu jusqu’à ce que son expéditeur réponde une fois à un défi. La réponse le place en liste blanche, de sorte qu’un correspondant n’est sollicité qu’une seule fois, pour toujours.
Les milters après mise en file (SpamAssassin, rspamd, ClamAV, …) et le filtre de langue peuvent aussi définir ou éclairer le verdict ; voir pepsi-stage-milter.
9.18.2. Péage à l’envoi et confirmation à l’envoi comparés¶
Tous deux retiennent le courrier des expéditeurs inconnus et tous deux envoient à l’expéditeur une réponse automatique à expéditeur nul, dans sa langue. Ils diffèrent par ce que l’expéditeur doit prouver :
Péage à l’envoi |
Confirmation à l’envoi |
|
|---|---|---|
L’expéditeur prouve |
qu’il dépensera de l’argent pour ce message |
qu’il peut lire le courrier à l’adresse depuis laquelle il a écrit |
Arrête |
le courrier de masse à tout volume, y compris depuis des boîtes aux lettres fonctionnelles |
le courrier de masse provenant d’adresses qui ne peuvent pas recevoir (la plus grande partie) |
N’arrête pas |
un expéditeur disposé à payer |
un spammeur disposant d’une boîte aux lettres fonctionnelle, qui peut automatiser la réponse |
Dissuasion |
économique : chaque message a un coût |
juridique uniquement : |
Nécessite |
un backend marchand GNU Taler, et un wallet du côté de l’expéditeur |
rien d’autre qu’une réponse |
Disons-le clairement : la confirmation à l’envoi est le filtre le plus faible. Elle vaut tout de même d’être utilisée, car elle coûte une réponse à un correspondant légitime et rien à l’exploitation d’un serveur de messagerie — et les deux se combinent. Lorsque les deux sont activés, le secrétaire s’exécute en premier et son UNCHALLENGEABLE_STAGE pointe vers le péage, de sorte que le courrier auquel on ne peut pas demander de répondre (un envoi de liste, un expéditeur non authentifié ou falsifié, un expéditeur au-delà du plafond quotidien de défis) est invité à payer à la place, tandis qu’un expéditeur confirmé atteint le péage avec state.spam = false et le franchit sans y être touché.
La confirmation à l’envoi prend garde au backscatter, puisque son défi va à l’expéditeur d’enveloppe et que l’expéditeur d’enveloppe du spam est généralement falsifié : elle ne défie qu’un expéditeur qui a passé SPF ou DMARC (REQUIRE_AUTHENTICATED) et dont le From: est cette même adresse, au plus MAX_CHALLENGES_PER_SENDER fois par jour, tous utilisateurs confondus ; elle ne répond jamais à ce que la RFC 3834 interdit de répondre ; et son défi ne cite rien du message retenu hormis un court aperçu de son objet (SUBJECT_HINT_LENGTH, huit caractères par défaut).
9.18.3. Filtres anti-spam et secrétaire¶
Par défaut, les milters de notation du spam (SpamAssassin via spamass-milter, rspamd, MIMEDefang, …) restent avant la vérification de liste blanche, et l’assistant de pepsi-setup génère cet ordre :
… → milter-spamassassin → check-whitelist → secretary → local
UNCHALLENGEABLE_STAGE = local
Un message que le filtre rejette n’atteint jamais le secrétaire, de sorte que le spam évident ne provoque aucun défi vers son expéditeur falsifié — c’est du backscatter évité. Ce qui atteint le secrétaire a passé le filtre, c’est pourquoi l’assistant fixe UNCHALLENGEABLE_STAGE au propre NEXT_STAGE du secrétaire : exécuter à nouveau le filtre reviendrait à analyser deux fois.
L’alternative consiste à placer le filtre après le secrétaire, de sorte qu’il n’analyse que le courrier qui n’a pas pu être soumis à un défi :
… → check-whitelist → secretary → local
└─ UNCHALLENGEABLE_STAGE → milter-spamassassin → local
La modification de configuration, pour un filtre en [stage-milter-spamassassin] :
Retirez le filtre de la chaîne principale : définissez le
NEXT_STAGEde l’étape qui pointait vers lui au propreNEXT_STAGEdu filtre (normalementcheck-whitelist).Dans
[stage-secretary], définissezUNCHALLENGEABLE_STAGE = milter-spamassassin.Dans
[stage-milter-spamassassin], définissezNEXT_STAGEauNEXT_STAGEdu secrétaire (icilocal).
Exécutez ensuite pepsi-setup check (ou run) pour valider le graphe.
Le compromis joue dans les deux sens :
Pour : le temps CPU du filtre n’est dépensé que pour le courrier qui en a besoin. Le courrier en liste blanche et celui des expéditeurs confirmés — l’essentiel d’une boîte aux lettres personnelle — n’est jamais analysé.
Contre : les défis partent désormais sans analyse. Le spam que le filtre aurait rejeté provoque d’abord un défi, soit davantage de backscatter vers des expéditeurs falsifiés (borné par
REQUIRE_AUTHENTICATEDet le plafond quotidien, mais pas nul) ; et le spam dont l’expéditeur confirme effectivement — le seul type que la confirmation à l’envoi ne peut pas arrêter — est remis sans que le filtre ne l’ait jamais vu.
Quelle que soit la disposition choisie, les milters antivirus et de politique restent sur le chemin principal (avant la vérification de liste blanche) : un correspondant confirmé ou en liste blanche peut tout de même envoyer un logiciel malveillant, depuis un compte compromis ou à son insu, et le verdict d’un filtre de politique s’applique à tout le monde.
9.19. Fonctionnalités opérationnelles¶
Pipeline à table unique avec récupération après plantage : les lignes
runningorphelines sont réinitialisées au démarrage du dispatcher ; les enfants en vol sont réinitialisés à l’arrêt.Pools de workers élastiques et pipelinés, et chien de garde : chaque étape exécute des processus workers persistants démarrés à la demande jusqu’à son
PARALLELISMet récupérés aprèsWORKER_IDLE_TIMEOUT, recyclés aprèsMAX_MESSAGES; chaque worker reçoit jusqu’àQUEUE_LIMITmessages à la fois (capacité en volQUEUE_LIMIT × PARALLELISM, sans coût de connexion supplémentaire), et un worker dépassantMAX_RUNTIMEsur son message en tête de file est tué (timeout) et remplacé.Outillage de file d’attente : pepsi-queue liste, supprime, remet à une autre étape et débloque en masse les messages.
Surveillance de santé : pepsi-status imprime un résumé en lecture seule de la file d’attente, des messages bloqués, des compteurs cumulatifs de remise/échec et des issues TLS sortantes récentes (aussi en
--json), adapté à une exécution via SSH.Provisionnement et vérification DNS : pepsi-setup installe le schéma, génère les clés et imprime/valide le DNS (DKIM, SPF, MTA-STS).
Serveur HTTP : pepsi-httpd sert, sur un ou plusieurs listeners TLS (sélectionnés par SNI) ou en clair, le fichier de politique MTA-STS (
/.well-known/mta-sts.txt), une page Prometheus/metrics(sur un listenerADMIN = yesuniquement — elle décrit le pipeline), les points de terminaison Web Key Directory qui publient les identités propres de ce déploiement (/.well-known/openpgpkey/…, formes directe et avancée), le portail Le portail de repli par lien sécurisé, et le document d”autoconfiguration du courrier ci-dessous.Autoconfiguration des clients de messagerie (draft-ietf-mailmaint-autoconfig) : un client à qui l’on ne donne que
fred@example.orgrécupèrehttps://autoconfig.example.org/mail/config-v1.1.xmlet se configure lui-même — hôte de soumission, port, sécurité de transport et authentification, et de même pour le serveur de boîtes aux lettres. La moitié soumission est dérivée du listener de soumission en fonctionnement, de sorte qu’elle ne peut pas dériver du serveur qu’elle décrit ; la moitié boîte aux lettres nomme le MDA auquel le déploiement s’associe. Servi publiquement et sans authentification, comme l’exige le brouillon, parce qu’un client doit le lire avant de pouvoir savoir comment s’authentifier.API et console d’administration : sur un listener explicitement marqué
ADMIN = yes— et jamais sur un listener qui les transporterait en clair hors de l’hôte —, le même serveur expose l”L’API d’administration sous/api/v1(file d’attente, santé, configuration, magasin de clés, journaux d’audit et de courrier) et la La console d’administration de navigateur sous/ui, qui est cliente exactement de cette API. Partout ailleurs, ces chemins répondent le même404qu’un chemin inconnu. L’authentification se fait par identifiants de pair sur socket UNIX, par cookie de session ou par jeton porteur, et chaque principal porte des portées ; tout cela est audité.Métriques :
pepsi-dispatchnote le débit par étape, les kills/timeouts, les plantages et le temps de traitement ainsi que les totaux globaux d’étapes/messages (vidés dans la base de données en une transaction environ une fois par minute), exportés parpepsi-httpdaux côtés des jauges actives/en pause en direct lues depuis la file d’attente.Journalisation structurée via
tracingà des niveaux configurables.
Pour les normes que Pepsi n’implémente pas (BINARYMIME entre autres), voir RFC applicables pas encore implémentées.
9.20. Stabilité des fonctionnalités¶
Chaque fonctionnalité décrite dans ce chapitre est suivie dans une unique table Stabilité des fonctionnalités, qui note — par fonctionnalité — la RFC qui la régit, si elle dispose d’un test automatisé (et de quel type : U unitaire, I intégration, I/U les deux), si elle a été vérifiée à la main, et à quel point elle est largement déployée et exercée selon une télémétrie d’usage anonyme et sur consentement explicite (pepsi-telemetry).
Cette table est générée : contrib/update-feature-stability.sh fusionne le registre maintenu à la main contrib/feature-registry.tsv (colonnes nom, RFC, test et test-manuel) avec un GET /telemetry/report de pepsi-telemetry (les comptes de déploiement et d’usage). Voir Étendre le pipeline pour savoir comment la maintenir à jour quand vous ajoutez une fonctionnalité, écrivez un test ou rafraîchissez les comptes de télémétrie.