85.1.32. pepsi-stage-secretary

hold mail from unknown senders until they confirm by replying

Section du manuel:

1

85.1.32.1.1. Nom

pepsi-stage-secretary - l’étape de confirmation à l’envoi du pipeline Pepsi.

85.1.32.1.2. Synopsis

pepsi-stage-secretary [GLOBAL-OPTIONS] worker

85.1.32.1.3. Description

pepsi-stage-secretary 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 met en œuvre la confirmation à l’envoi, à la manière du qsecretary de qmail : le courrier d’un expéditeur qu’aucune liste blanche ne connaît est retenu, et l’expéditeur est prié — dans sa langue — de confirmer en répondant. La réponse ajoute l’expéditeur à la liste blanche et libère chaque message retenu pour lui ; son courrier ultérieur est reconnu par pepsi-stage-check-whitelist(1) en amont de cette étape et n’est plus jamais retenu.

Faute de réponse avant HOLD_TIME, le courrier retenu prend le chemin d’expiration : il est réacheminé vers BOUNCE_STAGE lorsqu’une telle étape est branchée (un DSN, une quarantaine, une suppression — au choix de l’opérateur), et supprimé sinon.

Le bloc d’en-têtes est chargé, le corps jamais : un défi ne cite rien du message retenu, hormis un bref aperçu de son sujet.

85.1.32.1.4. Ce qu’elle arrête et ce qu’elle n’arrête pas

La confirmation à l’envoi est plus faible que le péage à l’envoi (pepsi-stage-anti-spam(1)). Un spammeur disposant d’une boîte aux lettres fonctionnelle peut automatiser la réponse, et la déclaration à laquelle l’expéditeur consent (PENALTY) est un moyen de dissuasion juridique, non économique. Elle reste utile : la plupart du courrier de masse provient d’adresses qui ne peuvent pas recevoir, et elle coûte à un correspondant légitime une réponse, une seule fois. Les deux se combinent — voir UNCHALLENGEABLE_STAGE.

85.1.32.1.5. Par message

L’étape décide dans cet ordre :

  1. Une réponse à un défi. Un destinataire est l’adresse de réponse <CONTROL_LOCAL_PART>-<cookie>@<domain>, où <cookie> fait 32 chiffres hexadécimaux et <domain> est l’un des LOCAL_DOMAINS (par défaut [pepsi-ingress] ACCEPTED_DOMAINS). Une ligne qui mêle adresses de réponse et destinataires ordinaires est d’abord scindée, les deux moitiés revenant à cette étape. Ensuite :

    • une réponse à expéditeur nul signifie que le défi a rebondi : son adresse ne peut pas recevoir, de sorte que les messages retenus sont réveillés et prennent le chemin d’expiration immédiatement plutôt qu’après HOLD_TIME ;

    • une réponse marquée comme automatique — Auto-Submitted: autre que no, Precedence: bulk|list|junk, un X-Auto-Response-Suppress: couvrant les réponses automatiques, un corps multipart/report — est ignorée : un répondeur d’absence qui répond au From: plutôt qu’à l’expéditeur d’enveloppe nul ne doit pas consentir au nom de son propriétaire ;

    • toute autre réponse confirme, pourvu que son expéditeur d’enveloppe ou l’addr-spec de son From: soit l’adresse ayant reçu le défi. L’expéditeur est ajouté à la liste blanche, chaque message retenu pour le défi est libéré, et le défi est supprimé. Une réponse à un cookie inconnu ou expiré, ou venant de quelqu’un d’autre, est journalisée et ne change rien.

    La réponse elle-même est consommée (supprimée) dans tous les cas.

  2. Un message à expéditeur nul (un rebond) et le courrier soumis localement (state.local_origin) passent vers NEXT_STAGE.

  3. state.spam = false (un expéditeur en liste blanche) passe ; un message libéré par une confirmation passe aussi, en recevant state.spam = false pour qu’une étape de péage à l’envoi en aval soit d’accord.

  4. state.spam = true prend aussitôt le chemin d’expiration.

  5. Un message retenu qui se réveille : abandonné (son défi a rebondi) ou au-delà de son échéance → le chemin d’expiration, le défi étant supprimé avec le dernier message qu’il retenait ; réveillé trop tôt → remis en pause jusqu’à l’échéance.

  6. Première rencontre. Les destinataires sont regroupés selon la liste blanche vers laquelle WHITELIST_NAME se développe pour chacun (un modèle {login}/{localpart} peut nommer une liste différente par destinataire), et un message dont les destinataires relèvent de plusieurs groupes est éclaté en une ligne par groupe. Le message est alors non soumis au défi, et va vers UNCHALLENGEABLE_STAGE, lorsque :

    • une règle de la RFC 3834 dit qu’il ne faut pas y répondre (les mêmes règles que celles qu’applique pepsi-stage-vacation(1) : courrier de liste de diffusion, Precedence: bulk, Auto-Submitted:, expéditeurs de service comme noreply@, rapports de remise) ;

    • l’expéditeur est aussi l’un des destinataires ;

    • From: ne nomme pas exactement une boîte aux lettres, ou son addr-spec n’est pas l’expéditeur d’enveloppe — le défi part vers MAIL FROM et la liste blanche compare From:, les deux doivent donc être une seule et même adresse ;

    • REQUIRE_AUTHENTICATED est activé et ni state.auth.spf ni state.auth.dmarc ne vaut pass ;

    • l’expéditeur a reçu MAX_CHALLENGES_PER_SENDER défis au cours des dernières 24 heures, toutes listes blanches confondues ;

    • WHITELIST_NAME ne se développe pas pour les destinataires (un modèle {login} et un destinataire sans compte), ou il n’y a aucun domaine servi où placer l’adresse de réponse.

    Sinon, le message rejoint le défi ouvert pour sa paire (liste blanche, expéditeur), ou en ouvre un et envoie le défi, puis il est mis en pause jusqu’à l’expiration du défi. Un message qui rejoint un défi existant hérite de son expiration, non d’un nouveau HOLD_TIME ; seul le message qui a ouvert un défi en envoie un.

Une ligne retenue note state.secretary : challenge (le cookie), deadline (secondes depuis l’epoch) et whitelist, puis confirmed ou abandoned ; le courrier envoyé vers UNCHALLENGEABLE_STAGE porte state.secretary.unchallengeable (la raison). Le chemin d’expiration retire entièrement state.secretary. Voir pepsi.state(7).

85.1.32.1.6. Le défi

Le défi est un message text/plain injecté à RESPONSE_STAGE, où il est signé et relayé comme tout courrier sortant. Il a l”expéditeur d’enveloppe nul et Auto-Submitted: auto-replied (RFC 3834), de sorte qu’un pair conforme — y compris un autre secrétaire Pepsi — n’y répond jamais. Il est adressé (To:) à l’expéditeur d’enveloppe ; From: est l’adresse de réponse, affichée avec l’adresse du destinataire protégé comme nom d’affichage, et Reply-To: la répète, de sorte que la fonction Répondre de n’importe quel client de messagerie l’atteint. Il s’insère dans le fil du message retenu via In-Reply-To:/References: et porte Content-Language:.

Il ne cite jamais le corps ni les pièces jointes du message retenu : un défi part vers une adresse qui, pour du spam, est généralement falsifiée, de sorte que tout ce qu’il citerait serait remis par nous à un inconnu.

85.1.32.1.6.1. Le texte

Par langue, de la plus haute priorité à la plus basse :

  1. MESSAGE_<LANG> dans la section propre de l’étape — redéfinissable par adresse, donc le texte propre d’un utilisateur via pepsi-settings(1) ou pepsi-stage-edit-settings(1) ;

  2. <TEMPLATE>.<lang>.body sous [pepsi] TEMPLATE_DIR (par défaut secretary-challenge.<lang>.body).

La première source l’emporte par langue, et non globalement : le MESSAGE_EN d’un utilisateur ne supplante pas le modèle allemand livré pour un expéditeur germanophone. La langue est choisie comme pepsi-stage-vacation(1) la choisit : state.language par confiance décroissante, puis DEFAULT_LANGUAGE, chacune par étiquette exacte, sous-étiquette primaire ou étiquette de région affinante. Les mêmes règles s’y appliquent : deux espaces dans un texte deviennent un saut de ligne, et les champs ci-dessous sont rendus tels quels quel que soit le style d’accolades employé.

Le sujet est SUBJECT_<LANG> pour la langue dans laquelle le texte a été choisi, sinon SUBJECT ; c’est aussi un texte Mustache. Si rien ne se rend (aucun texte pour la langue ni pour la langue par défaut, ou un modèle qui ne compile pas), le texte anglais intégré est envoyé et un avertissement journalisé — un défi qui n’est pas envoyé coûte son message à l’expéditeur.

85.1.32.1.6.2. Champs

{{SENDER_NAME}}

Le nom d’affichage du From: de l’expéditeur, ou son adresse.

{{RECIPIENT}}

Le ou les destinataires d’enveloppe du message retenu, séparés par des virgules.

{{SUBJECT_HINT}}

Le sujet du message retenu, décodé selon la RFC 2047 et tronqué à SUBJECT_HINT_LENGTH caractères (avec … en cas de troncature) — assez pour qu’un correspondant reconnaisse son courrier, trop peu pour transporter une charge utile. Absent pour un sujet vide ou une longueur de 0.

{{ORIGINAL_DATE}}

Le Date: du message retenu — absent lorsqu’il n’en a pas.

{{DEADLINE}}

Le moment où le défi expire, sous la forme YYYY-MM-DD HH:MM UTC.

{{REPLY_ADDRESS}}

L’adresse de réponse.

{{PENALTY}}

La PENALTY configurée — absente lorsqu’il n’y en a pas, de sorte qu’un modèle omet la phrase avec {{#PENALTY}}…{{/PENALTY}}.

85.1.32.1.6.3. L’avis de confirmation

Avec CONFIRM_NOTICE activé, un expéditeur confirmé est aussi informé que son courrier a été remis : un avis à expéditeur nul provenant de <CONTROL_LOCAL_PART>@<domain>, dont le texte provient de CONFIRM_MESSAGE_<LANG> ou de secretary-confirmed.<lang>.body (avec un repli anglais intégré) et dont le sujet est fixe.

85.1.32.1.8. Options

Toutes ces options sont redéfinissables par adresse.

NEXT_STAGE

Là où va le courrier qui passe, en liste blanche ou confirmé. Obligatoire.

RESPONSE_STAGE

Étape à laquelle le défi (et l’avis de confirmation) est injecté, afin qu’il soit signé et relayé. Obligatoire.

BOUNCE_STAGE

Le chemin d’expiration. S’il n’est pas défini, le courrier expiré est supprimé. Un défi resté sans réponse signifie en général un expéditeur falsifié, de sorte qu’un DSN ici est une seconde dose de backscatter ; l’assistant de pepsi-setup(1) le laisse non défini.

UNCHALLENGEABLE_STAGE

Là où va le courrier qui ne doit pas être soumis au défi (voir ci-dessus). S’il n’est pas défini, ce courrier prend le chemin d’expiration. Pointez-le vers une étape pepsi-stage-anti-spam(1) pour demander plutôt à ces expéditeurs de payer, vers NEXT_STAGE pour le remettre, ou vers un filtre anti-spam.

WHITELIST_NAME

L’unique liste blanche à laquelle un expéditeur confirmé est ajouté ; peut être un modèle {login} / {localpart} développé par destinataire, exactement comme dans pepsi-stage-check-whitelist(1). Obligatoire. Une étape check-whitelist placée en amont de celle-ci doit la consulter, faute de quoi un expéditeur confirmé reçoit un nouveau défi à chaque message (pepsi-setup(1) avertit).

Chaque inconnu qui répond à un défi est inscrit dans cette liste, de sorte qu’un propriétaire de compte ne peut la faire pointer que vers son propre espace de noms : une valeur qu’il définit par courrier via pepsi-stage-edit-settings(1) doit se développer, pour son adresse, en un nom <login>/... du compte auquel cette adresse se résout ({login}/friends, par exemple), ou être la whitelist que la configuration de l’opérateur nomme déjà pour lui. Un nom global, la liste d’un autre utilisateur, ou toute valeur pour une adresse qui ne correspond à aucun compte local y est refusé, et rien ne change.

L’opérateur peut nommer n’importe quelle whitelist, y compris une whitelist partagée, dans le fichier INI, dans pepsi.config_override à n’importe quelle portée (globale, domain:, address:) et dans une ligne pepsi.settings écrite avec pepsi-settings(1). L’étape utilise la valeur effective telle qu’elle la trouve.

CONTROL_LOCAL_PART

La partie locale par laquelle commencent les adresses de réponse. Par défaut secretary. Au plus 30 lettres ASCII, chiffres, ., _ ou -.

HOLD_TIME

Durée pendant laquelle un message retenu attend son expéditeur. Par défaut 120 h. Utilisez les unités h/m/s.

REQUIRE_AUTHENTICATED

N’envoyer de défi qu’à un expéditeur dont le domaine a réussi SPF ou DMARC. Par défaut yes : c’est la barrière contre le backscatter.

MAX_CHALLENGES_PER_SENDER

Nombre de défis qu’une adresse peut recevoir par 24 heures, toutes listes blanches confondues. Par défaut 5 ; 0 signifie sans limite. Un défi qui rebondit compte quand même.

DKIM_REQUIRED

auto (par défaut), yes ou no : le dkim_required de la ligne de liste blanche écrite pour un expéditeur confirmé. auto reflète le courrier retenu — exigé si l’un des messages retenus pour le défi a réussi DKIM (ou ARC conjointement avec DMARC).

CONFIRM_NOTICE

Informer un expéditeur confirmé que son courrier a été remis. Par défaut no.

PENALTY

Texte libre nommant ce que l’expéditeur accepte de payer si son message n’était pas sollicité (par exemple EUR:50). Non défini par défaut.

SUBJECT_HINT_LENGTH

Nombre de caractères du sujet retenu cités dans le défi. Par défaut 8 ; 0 omet l’aperçu ; au plus 64.

TEMPLATE

Nom de base des fichiers de modèle du défi. Par défaut secretary-challenge.

SUBJECT

Sujet du défi lorsqu’aucun SUBJECT_<LANG> ne convient. Par défaut Please confirm your message to {{RECIPIENT}}.

DEFAULT_LANGUAGE

Langue utilisée lorsqu’aucune des langues détectées de l’expéditeur n’a de texte. Par défaut en ; pepsi-setup(1) exige un texte pour elle.

MESSAGE_<LANG>, SUBJECT_<LANG>, CONFIRM_MESSAGE_<LANG>

Redéfinitions par langue du texte du défi, de son sujet et du texte de confirmation.

LOCAL_DOMAINS, TARGETS, RECIPIENT_DELIMITER

Les options de localité partagées : à quels domaines une adresse de réponse peut se trouver, et comment {login}/{localpart} se développent.

85.1.32.1.9. Placement

Sur le chemin entrant :

  • après pepsi-stage-detect-language(1), afin que le défi soit dans la langue de l’expéditeur ;

  • après pepsi-stage-check-whitelist(1), dont le state.spam = false est ce qui laisse passer un expéditeur connu — cette étape n’interroge jamais elle-même la liste blanche ;

  • avant pepsi-stage-aliases(1), le routeur de listes et la remise locale, dont aucun ne doit voir une adresse de réponse en premier (un attrape-tout @domain ou un RECIPIENT_DELIMITER - la réécrirait).

Le chemin sortant devrait exécuter pepsi-stage-auto-whitelist(1) vers le même WHITELIST_NAME, afin que les personnes auxquelles vos utilisateurs écrivent ne reçoivent jamais de défi lorsqu’elles répondent. Les milters anti-spam restent par défaut en amont de check-whitelist ; la section « Filtres anti-spam et secrétaire » du manuel décrit comment les placer derrière cette étape.

85.1.32.1.10. Base de données

Deux tables. pepsi.secretary_challenge contient un défi ouvert par paire (liste blanche, expéditeur) : le cookie, l’expiration et le fait qu’un message retenu ait ou non réussi DKIM. pepsi.secretary_sent note qu’un défi a été envoyé, sans son cookie, pour le plafond sur 24 heures — tenue à part afin qu’un défi qui reçoit une réponse, rebondit ou expire ne rende pas son quota à l’expéditeur. Quatre fonctions font le travail, en une instruction chacune : secretary_challenge_claim() (sûre face aux concurrences : deux workers revendiquant une même paire ouvrent un seul défi), secretary_confirm(), secretary_abandon() et secretary_expire(). Les lignes expirées sont balayées à mesure que de nouveaux défis sont revendiqués ; il n’y a aucune tâche cron à exécuter.

85.1.32.1.11. Sous-commandes

worker

Exécuté comme un worker persistant de pepsi-dispatch(1), lisant les identifiants de message sur l’entrée standard.

85.1.32.1.12. Options globales

-c FILE, –config FILE

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

-L LOGLEVEL, –log LOGLEVEL

Règle la verbosité de journalisation (par défaut info).

-v, –verbose

Affiche les messages de journal de toutes les sources.

-h, –help; -V, –version

Affiche un résumé d’utilisation / la version et quitte.

85.1.32.1.13. Code de sortie

0

Le message a été traité (transmis, retenu, réacheminé ou consommé).

1

Une erreur s’est produite (message introuvable ou non running, ou un réglage par adresse qui rend la section de l’étape invalide). La raison est écrite dans le journal.

Une défaillance de l’hôte (la base de données, un modèle ou un helper inutilisable) n’est pas signalée comme un échec : le message est mis en pause et réessayé, comme le décrit Erreurs d’étape dans pepsi-dispatch(1). Une section qui ne s’analyse pas fait refuser au worker de démarrer (statut 78) au lieu de mettre chaque message en échec à tour de rôle.

Un défi qui ne peut être mis en file est une telle défaillance. Il est retiré — le défi supprimé, à moins qu’un autre message retenu ne l’ait rejoint entre-temps, et l’emplacement MAX_CHALLENGES_PER_SENDER de l’expéditeur rendu — et le message est réessayé, plutôt que retenu dans l’attente d’une réponse que personne n’a été invité à donner.

85.1.32.1.14. Exemples

Une section de pipeline sur le chemin entrant

[stage-check-whitelist]
PROGRAM = pepsi-stage-check-whitelist
NEXT_STAGE = secretary
WHITELIST_NAME = correspondents

[stage-secretary]
PROGRAM = pepsi-stage-secretary
NEXT_STAGE = local
RESPONSE_STAGE = dkim-sign
WHITELIST_NAME = correspondents
PENALTY = EUR:50

Le texte de défi propre à un utilisateur, saisi par l’opérateur

pepsi-settings -c /etc/pepsi/pepsi.conf set alice@example.org secretary \
    MESSAGE_EN "Hi {{SENDER_NAME}},  please reply once to reach me."

85.1.32.1.15. Voir aussi

pepsi-stage-check-whitelist(1), pepsi-stage-auto-whitelist(1), pepsi-stage-anti-spam(1), pepsi-stage-vacation(1), pepsi-whitelist(1), pepsi-settings(1), pepsi-dispatch(1), pepsi.conf(5), pepsi.state(7), pepsi-setup(1)

RFC 3834 (réponses automatiques).

85.1.32.1.16. Bogues

Signalez les bogues au gestionnaire de tickets de Pepsi.