85.1.8. pepsi-stage-dkim-sign

DKIM-sign a message in the Pepsi pipeline

Section du manuel:

1

85.1.8.1.1. Nom

pepsi-stage-dkim-sign - l’étape de signature DKIM du pipeline Pepsi.

85.1.8.1.2. Synopsis

pepsi-stage-dkim-sign [GLOBAL-OPTIONS] worker

85.1.8.1.3. Description

pepsi-stage-dkim-sign est un programme d’étape exécuté par pepsi-dispatch(1) comme un worker persistant lisant les identifiants de message sur l’entrée standard. Il ajoute en tête un en-tête DKIM-Signature par algorithme de [pepsi] DKIM_ALGORITHMS (par défaut deux : un RSA, un Ed25519) aux octets bruts du message et fait avancer la ligne vers son NEXT_STAGE (une étape de remise). La signature ne fait qu”ajouter en tête des lignes d’en-tête, de sorte que les signatures existantes plus bas dans le message ne sont pas perturbées. Il ne touche pas au state de la ligne, de sorte que d’éventuels paramètres state.dsn RFC 3461 sont préservés pour que les étapes ultérieures les honorent.

Le domaine de signature (d=) est SIGNING_DOMAIN s’il est configuré, sinon le domaine de l’en-tête From: du message. Les clés sont les clés DKIM par domaine sous le [pepsi] KEY_DIR partagé (provisionnées par pepsi-setup(1)), en utilisant les sélecteurs de la section [pepsi].

Cette étape existe afin que la construction du message et la signature DKIM soient des étapes séparées. En particulier, pepsi-stage-bounce(1) construit un DSN non signé et pointe son NEXT_STAGE ici, de sorte qu’un rebond généré soit signé au nom du domaine de son postmaster avant la remise.

La signature est fail-closed : un message n’est jamais avancé non signé. Ce qui se passe à la place dépend de la partie en cause :

  • Aucun domaine de signature ne peut être déterminé (pas de SIGNING_DOMAIN et pas d’en-tête From: utilisable), ou ce déploiement n’a aucun répertoire de clés pour le domaine sous [pepsi] KEY_DIR : le message a atteint dkim-sign pour un domaine pour lequel personne ici ne signe. Avec un BOUNCE_STAGE, il y est réacheminé avec une raison de DSN dans state.bounce (statut 5.7.1 pour un domaine sans clés, 5.6.0 pour un message sans From: utilisable), après avoir d’abord été scindé en une ligne par destinataire afin que le NOTIFY de chaque destinataire soit respecté ; le diagnostic nomme le domaine, jamais le répertoire de clés. Sans lui, il est déplacé vers l’état terminal failed, avec la raison dans state.last_error. Un message à expéditeur nul (lui-même un DSN ou une réponse automatique) est toujours mis en échec plutôt que réacheminé : on ne fait jamais rebondir un rebond, et en échec il reste visible pour l’opérateur.

  • Le répertoire de clés existe mais une clé ne peut être lue ou utilisée — une rotation restée à moitié faite, une permission modifiée, un fichier de clé endommagé : c’est l’hôte qui est en cause, non le message. L’erreur est réessayée (voir pepsi-dispatch(1)) : le message reste paused à cette étape avec la raison dans state.last_error et est réessayé, après une minute puis à des intervalles qui doublent jusqu’à une heure, jusqu’à ce qu’il soit resté en file plus longtemps que le MAX_LIFETIME de l’étape (par défaut 120 h) ; ce n’est qu’alors qu’il est abandonné. Le faire échouer immédiatement ferait rebondir chaque message sortant, et les rebonds, qui sont eux aussi signés ici, seraient alors abandonnés sans que personne n’en soit averti.

La section propre de l’étape et [pepsi] sont vérifiées au démarrage du worker : une configuration qui ne s’analyse pas fait refuser au worker de démarrer (sortie 78), et le dispatcher retient la file jusqu’à sa correction.

85.1.8.1.3.1. Couverture du corps

La signature couvre toujours strictement l’intégralité du corps : tout changement du corps en transit l’invalide. Pepsi n’émet jamais le tag de longueur de corps (l=) de la RFC 6376, et aucune option ne permet de le lui faire émettre.

Le tag existe pour qu’une signature survive à une liste de diffusion qui ajoute un pied de page, mais aucun opérateur ne peut faire utilement ce compromis. L’analyseur strict de mail-auth — celui qu’utilise le vérificateur de Pepsi, et celui qu’utilise tout déploiement mail-auth/Stalwart — rejette purement et simplement toute signature portant l > 0, au lieu de se contenter de tolérer une couverture moindre : le tag transforme donc une signature qui serait passée en une signature qui échoue. Le cas pour lequel il existe est aussi celui où la réécriture de la liste a déjà cassé SPF, si bien que sous une politique DMARC p=reject cet échec DKIM fait la différence entre remis et rejeté. La RFC 6376 §8.2 avertit par ailleurs que le contenu ajouté peut remplacer l’original aux yeux du lecteur, par restructuration MIME ou analyse HTML laxiste, et que « les signataires devraient être extrêmement circonspects quant à l’usage de ce tag ».

85.1.8.1.3.2. Couverture des en-têtes

Les en-têtes couverts par la signature (le tag h=) sont la liste SIGNED_HEADERS (par défaut : les en-têtes courants d’origine/MIME). From et Subject sont listés deux fois afin d’être sur-signés (RFC 6376 §8.15) : un en-tête nommé une fois de plus qu’il n’apparaît réellement est signé une fois de plus (à vide), de sorte qu’un attaquant qui ajoute en tête un second From: ou Subject: — les en-têtes qui changent l’origine ou le sens apparents d’un message — produit une occurrence que la signature ne couvre pas, ce qui casse la vérification. Lister dans SIGNED_HEADERS un en-tête absent du message le sur-signe de même (il ne pourra pas être ajouté ultérieurement).

85.1.8.1.4. Configuration

L’étape est câblée et configurée via sa propre section [stage-<name>] (PROGRAM = pepsi-stage-dkim-sign, un NEXT_STAGE obligatoire). Ses options — HEADER_CANONICALIZATION/BODY_CANONICALIZATION (choisies indépendamment), SIGNATURE_EXPIRATION_DAYS, SIGNED_HEADERS et SIGNING_DOMAIN — ainsi que le matériel de clé partagé [pepsi] (KEY_DIR, DKIM_SELECTOR, DKIM_ALGORITHMS) sont documentées dans pepsi.conf(5). Le hachage est toujours SHA-256 ; par défaut, une signature RSA et une signature Ed25519 sont toutes deux émises.

85.1.8.1.5. État

Entrées : aucune depuis state — le domaine de signature provient de SIGNING_DOMAIN ou de la colonne from_header, et le hachage du corps, du message chargé.

Sorties : aucune ajoutée à state. Les signatures sont ajoutées en tête de la colonne headers ; tout le state — y compris state.dsn — est préservé inchangé. La disposition de l’état est décrite dans pepsi.state(7).

Transitions : en cas de succès, avance vers NEXT_STAGE (l’étape de remise), après avoir ajouté en tête le ou les en-têtes DKIM-Signature. Si le domaine de signature ne peut être déterminé ou n’a pas de clés ici, le message est réacheminé vers BOUNCE_STAGE (une ligne par destinataire, state.bounce défini) ou, sans lui ou pour un message à expéditeur nul, déplacé vers l’état terminal failed ; si ses clés ne peuvent être utilisées, il est mis en pause pour réessai jusqu’à MAX_LIFETIME (fail-closed ; voir ci-dessus). L’étape ne termine jamais un message.

85.1.8.1.6. Commandes

Exécuté comme worker, le programme signe chaque message et le fait avancer.

85.1.8.1.7. Options globales

-c FILE, –config FILE

Lit la configuration depuis FILE au lieu du chemin de recherche par défaut.

-L LOGLEVEL, –log LOGLEVEL

Règle la verbosité de journalisation (error, warn, info, debug, trace ; par défaut info).

-h, –help

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

-V, –version

Affiche la version et quitte.

85.1.8.1.8. Code de sortie

Le résultat de chaque message est rapporté à pepsi-dispatch(1) sur la ligne de statut du worker, et non par le code de sortie.

0

Le worker s’est exécuté jusqu’à la fermeture de son entrée standard.

1

Une erreur fatale s’est produite (configuration illisible, base de données impossible à ouvrir, ou échec de l’entrée/sortie standard). La raison est écrite dans le journal.

78

Le worker a refusé de démarrer : sa section ou [pepsi] ne s’analyse pas. Le dispatcher remet en file ce qu’il lui avait confié et réessaie l’étape plus tard.

85.1.8.1.9. Voir aussi

pepsi-config(1), pepsi-stage-bounce(1), pepsi-stage-relay-to-internet(1), pepsi-stage-relay-to-smarthost(1), pepsi-dispatch(1), pepsi-ingress(1), pepsi.conf(5), pepsi.state(7), pepsi-setup(1)

85.1.8.1.10. Bogues

Signalez les bogues au gestionnaire de tickets de Pepsi.