Migration de SpamAssassin vers Rspamd : retour d’expérience

1. Pourquoi remplacer SpamAssassin ?

Sur mon serveur mail, le filtrage antispam reposait depuis longtemps sur SpamAssassin.

Le système fonctionnait, mais j’ai souhaité passer à Rspamd, notamment pour bénéficier :

  • d’un moteur plus moderne ;
  • d’une analyse plus complète des messages ;
  • d’une meilleure intégration SPF, DKIM et DMARC ;
  • d’un système Bayes/statistique performant ;
  • d’une interface Web permettant de comprendre précisément les décisions du moteur ;
  • d’une configuration beaucoup plus facilement ajustable.

La migration s’est toutefois révélée plus complexe qu’un simple remplacement de SpamAssassin.

Le principal problème rencontré n’a d’ailleurs pas été Rspamd lui-même, mais l’interaction avec Redis et un site WordPress hébergé sur le même serveur.


2. Première étape : intégrer Rspamd à Postfix

Rspamd a été installé et intégré à Postfix comme Milter.

Le principe devient alors :

Internet
   │
   ▼
Postfix
   │
   ▼
Rspamd
   │
   ├── SPF
   ├── DKIM
   ├── DMARC
   ├── Bayes
   ├── réputation
   ├── analyse des URLs
   └── règles
   │
   ▼
Postfix / livraison

L’objectif était de supprimer progressivement la dépendance à SpamAssassin tout en conservant la distribution normale des mails.


3. Premier problème : SpamAssassin continuait à être appelé

Après l’installation de Rspamd, certains mails contenaient encore des informations provenant de SpamAssassin.

Le problème venait de l’ancienne chaîne de traitement.

Virtualmin/Procmail continuait notamment à appeler :

spamc

Rspamd fonctionnait donc bien, mais SpamAssassin continuait à être exécuté en parallèle.

Il a fallu identifier puis supprimer les appels résiduels à SpamAssassin.

Cette étape a également montré qu’il ne fallait pas simplement arrêter spamd : tant que des éléments de la configuration existante dépendaient encore de SpamAssassin, cela pouvait perturber la livraison des messages.

La migration devait donc être faite progressivement.


4. Le véritable problème : Rspamd perdait régulièrement sa mémoire

Une fois SpamAssassin réellement écarté, un problème beaucoup plus important est apparu.

Le système Bayes/statistique de Rspamd semblait régulièrement revenir à zéro.

On constatait notamment :

  • des statistiques qui disparaissaient ;
  • Bayes qui semblait perdre son apprentissage ;
  • une base qui redevenait vide ;
  • des performances antispam qui changeaient brutalement ;
  • des comportements laissant penser que Rspamd avait été réinitialisé.

Au départ, plusieurs hypothèses ont été envisagées :

  • problème de configuration ;
  • redémarrage de Rspamd ;
  • problème de persistance ;
  • rechargement de configuration ;
  • problème avec Redis ;
  • plusieurs instances ;
  • fichiers ou configurations modifiés.

Un point particulièrement trompeur était que :

rspamadm configtest

retournait bien :

syntax OK

La configuration était donc valide.

Le problème était ailleurs.


5. Découverte : Rspamd utilisait Redis

L’analyse a montré que Rspamd utilisait Redis pour stocker ses données, notamment les informations nécessaires au fonctionnement statistique.

Redis était également utilisé par un site WordPress hébergé sur le serveur.

C’était là que se trouvait le problème.

Le même Redis servait donc à deux applications complètement différentes :

                 Redis
                   │
          ┌────────┴────────┐
          │                 │
          ▼                 ▼
      WordPress           Rspamd
          │                 │
       données          Bayes/stats

En soi, partager un serveur Redis n’est pas nécessairement un problème.

Le problème venait du fait que WordPress effectuait des opérations de nettoyage/purge dans Redis.


6. Le problème venait d’un site WordPress

L’analyse de Redis a permis d’identifier des connexions provenant de PHP et notamment de Predis.

On retrouvait dans Redis des clés appartenant à WordPress, par exemple :

wp_*
mainwp_*
yqhcg:*

mais également des clés utilisées par Rspamd, notamment :

RS_*

Le point déterminant a été l’observation des commandes exécutées par le client PHP.

WordPress utilisait Redis comme cache et pouvait effectuer des opérations de purge sur sa base.

Comme WordPress et Rspamd utilisaient la même DB Redis, une purge destinée à WordPress pouvait également supprimer les données utilisées par Rspamd.

Le résultat était particulièrement problématique :

Rspamd apprend
      │
      ▼
Bayes / statistiques progressent
      │
      ▼
WordPress effectue une purge Redis
      │
      ▼
données Rspamd supprimées
      │
      ▼
Rspamd repart avec une base vide

Cela expliquait parfaitement le comportement observé.


7. Pourquoi le problème était difficile à identifier

Le problème pouvait facilement être interprété comme un dysfonctionnement de Rspamd.

On observait par exemple :

Bayes : données présentes
        ↓
quelque temps plus tard
        ↓
Bayes : retour à zéro

sans nécessairement avoir redémarré Rspamd.

La configuration était correcte.

Le service fonctionnait.

Redis fonctionnait.

Et pourtant les données disparaissaient.

La cause était donc externe à Rspamd : une autre application modifiait la base de données qu’il utilisait.

C’est probablement le problème qui a demandé le plus de diagnostic pendant toute la migration.


8. Solution : isoler Rspamd dans une DB Redis différente

La solution retenue a été de ne pas créer obligatoirement une deuxième instance Redis, mais simplement d’utiliser une base logique Redis différente.

Redis permet en effet d’avoir plusieurs bases indépendantes dans une même instance.

La situation était alors :

Redis
│
├── DB 0
│     └── WordPress
│
└── DB 1
      └── Rspamd

Rspamd a donc été configuré pour utiliser :

db = 1

au lieu de la DB utilisée par WordPress.

Cette séparation permettait à WordPress de continuer à effectuer ses opérations de cache sans pouvoir supprimer les données de Rspamd.


9. Vérification de la nouvelle base

Après le changement, la DB 1 était initialement vide.

Ce n’était pas une anomalie.

C’était simplement une nouvelle base Redis dans laquelle Rspamd allait reconstruire progressivement ses données.

Après l’apprentissage de nouveaux messages, les données Rspamd apparaissaient dans cette base.

La séparation devenait alors :

DB 0
└── WordPress
    ├── wp_*
    ├── mainwp_*
    └── autres clés WordPress

DB 1
└── Rspamd
    ├── RS_*
    └── données statistiques / Bayes

Cette isolation a supprimé le risque que les opérations de nettoyage du cache WordPress effacent les données de Rspamd.


10. Un autre diagnostic trompeur : le Configuration ID

Pendant les investigations, le Configuration ID de Rspamd changeait en cours de fonctionnement, alors qu’il restait identique entre deux redémarrages.

Cela avait initialement orienté les recherches vers un éventuel rechargement dynamique de configuration.

Cette piste était pertinente à examiner, car un reload de configuration peut modifier le comportement du moteur et, selon le mécanisme concerné, donner l’impression d’une réinitialisation.

Cependant, l’analyse de Redis a finalement permis d’expliquer le problème principal : les données statistiques étaient effectivement supprimées par une autre application.

Le problème de persistance n’était donc pas simplement un problème de configuration Rspamd.


11. Une fois Redis isolé, Rspamd pouvait enfin apprendre normalement

Après avoir séparé les deux applications :

WordPress → Redis DB 0
Rspamd    → Redis DB 1

Rspamd pouvait conserver durablement ses données.

C’était une étape essentielle avant de pouvoir réellement évaluer l’efficacité de son apprentissage Bayes.

Avant cette correction, tout réglage du système statistique était en partie inutile : même si Rspamd apprenait correctement, son apprentissage pouvait être supprimé ultérieurement par une opération Redis effectuée pour WordPress.


12. Deuxième série de problèmes : réglage des scores

Une fois le problème de persistance réglé, les ajustements de détection ont pu être réalisés dans de meilleures conditions.

Certains messages indésirables obtenaient encore des scores relativement faibles.

Il fallait cependant éviter de compenser immédiatement chaque erreur par une nouvelle règle ou par une augmentation importante des scores.

Les premiers résultats ont notamment montré qu’un message pouvait avoir :

  • SPF valide ;
  • DKIM valide ;
  • DMARC valide ;

tout en étant indésirable.

Ces mécanismes d’authentification ne constituent donc pas, à eux seuls, une preuve qu’un message est légitime.

Rspamd doit combiner plusieurs signaux.


13. Le cas de REDIRECTOR_URL

Un des symboles particulièrement intéressant pendant les réglages a été :

REDIRECTOR_URL

Il permet de détecter certains comportements liés aux URLs de redirection présents dans des messages indésirables.

Plusieurs messages ont permis d’évaluer son comportement.

Le poids de cette règle a finalement été volontairement ramené à un niveau faible, autour de :

0,25

plutôt que de lui donner une pénalisation excessive.

L’objectif était d’éviter qu’une caractéristique isolée puisse faire basculer un message légitime dans le spam.


14. Le cas de HTML_ONLY

Une autre erreur de configuration a été rencontrée avec le symbole :

HTML_ONLY

Une règle personnalisée avait été envisagée alors que Rspamd possède déjà ce symbole nativement.

Cela a provoqué une erreur de configuration.

La solution a été de supprimer la définition personnalisée et d’utiliser le symbole natif.

C’est une règle importante pour les futures modifications :

Toujours vérifier les symboles déjà disponibles dans Rspamd avant de créer une règle personnalisée.


15. Les seuils d’action

Pendant la phase de mise au point, les seuils ont été configurés de manière conservatrice.

Le principe est de distinguer :

greylist
     ↓
add_header
     ↓
rewrite_subject
     ↓
reject

Le rejet automatique a notamment été maintenu très haut pendant la phase de stabilisation.

L’objectif était clair : ne pas risquer de perdre un mail légitime pendant que le système était encore en phase d’apprentissage et de réglage.


16. Pourquoi Bayes devait être laissé apprendre

Une fois le problème Redis résolu, le fonctionnement de Bayes pouvait enfin devenir réellement intéressant.

Rspamd devait recevoir suffisamment d’exemples :

HAM  → messages légitimes
SPAM → messages indésirables

pour construire progressivement son modèle statistique.

Il était donc préférable de corriger les erreurs importantes et de laisser le système accumuler des données plutôt que de créer une règle spécifique pour chaque nouveau spam.


17. Architecture finale

La partie importante de l’architecture finale est donc :

                         INTERNET
                            │
                            ▼
                       ┌─────────┐
                       │ Postfix │
                       └────┬────┘
                            │
                            ▼
                       ┌─────────┐
                       │ Rspamd  │
                       └────┬────┘
                            │
                            │
                       Redis DB 1
                            │
                       Bayes / stats
                            │
                            ▼
                    Analyse / décision
                            │
                            ▼
                    Livraison du mail


             Redis DB 0
                  │
                  ▼
             WordPress
                  │
           cache / données

La séparation des bases Redis est fondamentale :

WordPress → DB 0
Rspamd    → DB 1

Les opérations de purge du cache WordPress ne peuvent ainsi plus supprimer les données statistiques de Rspamd.


18. Les principaux problèmes rencontrés

ProblèmeCauseSolution
SpamAssassin continuait à intervenirAppels résiduels à spamc/ProcmailSuppression des appels à SpamAssassin
Arrêt de SpamAssassin perturbant la livraisonAnciennes dépendances encore présentesMigration progressive
Bayes/statistiques Rspamd revenaient à zéroDonnées Redis suppriméesIdentification de l’utilisation de Redis
Redis partagé avec WordPressMême DB Redis utilisée par deux applicationsIsolation de Rspamd dans Redis DB 1
Purges WordPress supprimant les données RspamdCache WordPress partageant la DBSéparation DB 0 / DB 1
Score faible sur certains spamsBayes encore en apprentissage + authentification valideApprentissage et observation
Erreur avec HTML_ONLYSymbole déjà natif dans RspamdUtilisation du symbole natif
REDIRECTOR_URL trop pénalisantScore initial trop important pour certains casRéduction à 0,25
Risque de faux positifsRéglages trop agressifsSeuils conservateurs

19. Ce que cette migration m’a surtout appris

Le problème le plus important n’était finalement ni SpamAssassin, ni Rspamd.

C’était une question d’architecture.

Deux applications sans rapport apparent utilisaient le même service Redis :

WordPress ──┐
            ├── Redis DB 0
Rspamd ─────┘

et l’une d’elles effectuait des opérations de purge qui affectaient les données de l’autre.

La séparation :

WordPress → DB 0
Rspamd    → DB 1

a résolu le problème.

C’est un point à surveiller particulièrement sur les serveurs qui hébergent plusieurs applications : partager un service comme Redis ne signifie pas nécessairement partager les mêmes données. Les bases logiques ou les préfixes permettent d’isoler les applications, mais cette isolation doit être configurée explicitement.


20. Conclusion

La migration de SpamAssassin vers Rspamd a donc comporté deux problèmes très différents.

Le premier était lié à la migration elle-même : il fallait éliminer les appels historiques à SpamAssassin dans la chaîne Virtualmin/Procmail et faire fonctionner correctement Rspamd comme Milter Postfix.

Le second, beaucoup plus subtil, concernait la persistance des données.

Rspamd utilisait Redis pour ses statistiques et son apprentissage, tandis qu’un site WordPress utilisait également Redis. Les opérations de purge effectuées par WordPress pouvaient supprimer les données de Rspamd, provoquant des retours à zéro du système Bayes et des statistiques.

Le diagnostic de ce problème a été déterminant.

La solution finale a été d’isoler les deux applications :

Redis DB 0 → WordPress
Redis DB 1 → Rspamd

Une fois cette séparation réalisée, Rspamd a pu conserver son apprentissage et les réglages de détection ont pu être affinés progressivement.

La migration n’a donc pas seulement consisté à remplacer un logiciel antispam par un autre. Elle a nécessité de comprendre l’ensemble de la chaîne de traitement et les dépendances entre les différents services présents sur le serveur.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. Découvrez comment les données de vos commentaires sont traitées.