Plugin Bounce Guard pour Discourse

Dépôt : GitHub - overgrow/discourse-bounce-guard · GitHub
Licence : MIT

&tldr; Ce que vous obtenez : un journal d’erreurs plus propre (les tentatives de renvoi horaires vers des adresses mortes cessent). Discourse cesse d’envoyer des e-mails qui ne peuvent pas être livrés. Et des utilisateurs plus satisfaits : ils peuvent retrouver l’accès à leur compte, car une adresse morte est remplacée par une adresse fonctionnelle à temps.

Si votre messagerie sortante passe par votre propre relais (Postfix, Exim, la plupart des installations auto-hébergées), vous avez probablement vu /logs se remplir de paires comme celle-ci :

SMTP Error Net::SMTPServerBusy with message: 450 4.1.2 <someone@gone-domain.com>: Recipient address rejected: Domain not found
Job exception: Net::SMTPServerBusy

Le domaine a disparu et ne reviendra pas. Mais le relais répond avec un code d’erreur temporaire (450), car en théorie, une recherche DNS peut échouer momentanément. Discourse interprète toute erreur temporaire comme signifiant « réessayer dans une heure », et Sidekiq continue d’essayer pendant des semaines. La détection de rebond du noyau ne se déclenche jamais. Elle ne réagit que lorsqu’un message de rebond revient, soit sous forme de e-mail (VERP), soit sous forme de webhook de votre fournisseur de messagerie. Ici, le message est refusé sur le champ, donc aucun message de rebond n’existe jamais. Le score de rebond reste à zéro, les tentatives de renvoi continuent, et l’utilisateur conserve une adresse qui ne peut rien recevoir, y compris une demande de réinitialisation de mot de passe.

Cela a été soulevé ici à plusieurs reprises sans réponse intégrée, par exemple Gestion des e-mails vers un domaine inexistant, Désactiver un utilisateur en cas de rebond dur et Comment désactiver les comptes des utilisateurs qui ne reçoivent pas d’e-mails. Nous avions le même problème sur notre forum, nous avons donc créé un plugin.

Ce qu’il fait

Bounce Guard s’insère directement dans le chemin d’envoi et classe chaque rejet SMTP :

  • Les réponses 5xx avec un statut renforcé de mauvais destinataire (5.1.1, 5.1.2, 5.2.1 et similaires) sont comptées comme des échecs durs.
  • Toute réponse correspondant à une liste de phrases configurables (« domain not found », « user unknown », …) est comptée comme un échec dur, quel que soit le code. Cela capture les relais qui échouent de manière temporaire (soft-fail) sur des conditions permanentes avec un 450.
  • Le greylisting, les boîtes aux lettres pleines et les limites de débit sont laissés de côté. Le comportement de renvoi du noyau n’est pas modifié.

Les échecs durs enregistrés montent ensuite une échelle :

  1. Chacun alimente le score de rebond du noyau comme un rebond dur (activé par défaut). Après deux occurrences, le seuil propre au noyau est atteint et Discourse cesse d’envoyer des e-mails à l’utilisateur. Le bruit dans les journaux s’arrête ici, même si vous n’activez rien d’autre.
  2. Après un nombre configurable (3 par défaut) réparti sur une période configurable (48 heures par défaut, pour qu’une panne brève ne désactive personne), le plugin agit. L’action par défaut est log_only : une entrée dans le journal des actions du personnel enregistrant que l’utilisateur aurait été désactivé. Passez à deactivate lorsque vous lui faites confiance.
  3. La désactivation achemine l’utilisateur à travers le flux d’activation standard lors de sa prochaine connexion, où le changement d’adresse est intégré. Ils vérifient un e-mail fonctionnel et continuent avec leur compte intact. L’objectif est la récupérabilité : un compte actif dont le seul canal de récupération est une boîte aux lettres morte est un verrouillage en puissance.

Si votre site reçoit des rebonds (VERP ou webhooks), vous pouvez également laisser le plugin agir lorsque le score de rebond du noyau dépasse un niveau de votre choix, supérieur au score auquel le noyau cesse d’envoyer. Quelques règles de sécurité s’appliquent partout. Le personnel et les bots ne sont jamais touchés. Un e-mail bloqué qui est renvoyé chaque heure compte comme un seul échec, grâce à une fenêtre de refroidissement. Un échec n’est compté contre un utilisateur que lorsque l’adresse rejetée par le serveur est l’adresse actuelle de cet utilisateur. Et chaque échec enregistré est conservé pendant 90 jours pour que vous puissiez vérifier ce qui s’est passé (requête Data Explorer dans le README).

Installation

Installation standard du plugin :

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/overgrow/discourse-bounce-guard.git

Activez bounce_guard_enabled, laissez bounce_guard_action sur log_only pendant une ou deux semaines, examinez ce qu’il signale, puis décidez de la désactivation. La référence des paramètres se trouve dans le README.

Côté serveur uniquement, aucun composant de thème ou JS. Construit et testé contre le noyau actuel (2026.8), 31 spécifications, CI exécute le workflow standard discourse-plugin. Les retours sont les bienvenus, en particulier les formulations de rejet d’autres relais que la liste de phrases par défaut ne capture pas.