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ème | Cause | Solution |
|---|---|---|
| SpamAssassin continuait à intervenir | Appels résiduels à spamc/Procmail | Suppression des appels à SpamAssassin |
| Arrêt de SpamAssassin perturbant la livraison | Anciennes dépendances encore présentes | Migration progressive |
| Bayes/statistiques Rspamd revenaient à zéro | Données Redis supprimées | Identification de l’utilisation de Redis |
| Redis partagé avec WordPress | Même DB Redis utilisée par deux applications | Isolation de Rspamd dans Redis DB 1 |
| Purges WordPress supprimant les données Rspamd | Cache WordPress partageant la DB | Séparation DB 0 / DB 1 |
| Score faible sur certains spams | Bayes encore en apprentissage + authentification valide | Apprentissage et observation |
Erreur avec HTML_ONLY | Symbole déjà natif dans Rspamd | Utilisation du symbole natif |
REDIRECTOR_URL trop pénalisant | Score initial trop important pour certains cas | Réduction à 0,25 |
| Risque de faux positifs | Réglages trop agressifs | Seuils 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.