Skip to main content
Le courrier entrant sur le port 25 traverse un pipeline scoré avant d’atteindre la boîte de réception de quiconque. Cette page couvre le pipeline tel que la version communautaire (AGPL) le livre, les réglages qui l’ajustent, et la boucle de retour qui l’entraîne. Les conséquences d’un verdict sur la remise (quel dossier, quel tag, quelle réponse SMTP) sont décrites sur la couche SMTP ; cette page explique comment le verdict se forme.
La version communautaire score avec les signaux d’authentification, de réseau, d’heuristique et bayésiens. Des étages d’analyse de contenu plus avancés existent en extension commerciale et sont volontairement absents ici : les champs qu’ils rempliraient restent vides plutôt que d’être simulés.

Le pipeline, dans l’ordre

  1. Contrôle des pairs de confiance (ADR-031). La boucle locale et [security] trusted_ips sautent SPF, DNSBL et le rejet de politique DMARC, mais jamais l’analyse de contenu : une application locale compromise peut toujours spammer. Un MX de secours de vos propres domaines n’est pas un pair de confiance : le courrier qu’il relaie est jugé sur l’adresse du client qu’il a servi, lue dans la ligne Received: qu’il a écrite, avec les mêmes SPF, DMARC, DNSBL et score qu’un client direct (MX de secours). Ne listez pas un secours dans trusted_ips ; oximail doctor avertit quand c’est le cas.
  2. Verdicts d’authentification. Les résultats SPF, DKIM, DMARC et ARC (issus de la passe de vérification entrante) alimentent le score. Un échec DMARC p=reject est rejeté d’emblée avec 550 5.7.26, avant le scoring de contenu : c’est une décision d’authentification, pas un score de spam. Une chaîne ARC valide ne lève un verdict DMARC en échec que si son premier sceau figure dans [spam] arc_trusted_sealers, vide par défaut (ARC et l’exception DMARC) : n’importe quel domaine peut sceller une chaîne, et un faussaire d’un domaine en p=reject atteignait la boîte de réception en scellant la sienne.
  3. DNSBL. L’IP qui se connecte (ou, derrière un MX de secours reconnu, le client qu’il a servi) est vérifiée contre les listes de blocage que vous configurez ([spam] dnsbl_servers, vide par défaut). Un listage pèse dans le score.
  4. Signaux de contenu. Des heuristiques (chaînes de raccourcisseurs d’URL, falsification d’en-têtes, anomalies structurelles) et un classifieur bayésien entraîné par organisation. Deux garde-fous gardent le Bayes honnête : il s’entraîne exactement sur la représentation qu’il score (pas de dérive entraînement/scoring), et les votes dans sa zone morte statistique sont écartés plutôt que forcés en verdict.
  5. Disposition (ADR-080). Une fonction partagée unique traduit le verdict final en action : Reject vers 550 ; Defer vers 451 ; Junk vers le dossier Indésirables ; suspect et en masse vers la boîte de réception, avec un tag X-Spam-Status, jamais aux Indésirables. Seul un verdict confiant écarte du courrier de la vue.
Deux flux particuliers vivent à côté :
  • La liste blanche des réponses. Un correspondant à qui vous avez répondu est en liste blanche, mais elle n’est honorée que si l’enveloppe entrante est authentifiée (alignement SPF/DKIM) : un From: falsifié d’un contact connu n’y gagne rien.
  • La capture spamtrap (ADR-077). Le courrier vers un destinataire piège désigné est accepté et classé de force en $junk comme vérité terrain pour le corpus, plutôt que rejeté : le piège existe pour collecter exactement ce courrier.

Pas de greylisting

Il n’y a aucune étape de greylisting, et aucun réglage n’en active une. La barrière au moment du RCPT, qui refusait temporairement un triplet (IP, expéditeur, destinataire) inconnu par un 451 en attendant un réessai, a été retirée (ADR-128), parce que l’hypothèse sur laquelle elle reposait a cessé de tenir : les botnets modernes réessaient, les gros expéditeurs légitimes répondent depuis des pools d’adresses qui ne re-présentent jamais le même triplet, et le coût retombait précisément sur les correspondants que vous voulez le plus voir arriver, ceux qui vous écrivent pour la première fois. Cela impliquait aussi d’accepter une écriture en base avant toute authentification. L’exigence d’authentification et le pipeline scoré portent cette charge à la place. Si vous migrez depuis un serveur où le greylisting faisait un travail visible, les équivalents ici sont la pondération DNSBL, les verdicts d’authentification et le corpus bayésien, pas un interrupteur à réactiver.
Une variante conditionnée au score existe (le patron de rspamd) : après la notation du contenu, un message dont le score tombe dans la bande grise (au moins 2,0 et sous le seuil de Junk, sans signal de confiance, ni liste blanche, ni ARC valide, ni conversation que vous avez engagée) est différé 451 une fois, sur une clé faite d’un préfixe réseau masqué et du message lui-même, si bien qu’un expéditeur en pool d’adresses qui réessaie depuis une voisine passe. Un réessai dans cette bande est une preuve plutôt qu’un péage. Elle est inactive par défaut et se règle : [spam] greylist_enabled = true avec greylist_duration_secs (300) ; l’activation est un geste délibéré par box, un canari d’abord, jamais dans la même fenêtre qu’un échange de binaire. Rien d’autre sur cette page n’en dépend.

Deux verdicts, et les axes derrière

Un message qui tente de voler vos identifiants et une promotion pour des couteaux de cuisine arrivaient avec le même verdict, « indésirable ». Ils en portent deux désormais, parce qu’ils répondent à deux questions, « voulez-vous ceci ? » et « quelqu’un se fait-il passer pour un autre ? », et que la seconde est une chose qu’un client montre plutôt qu’il ne range.
  • L’axe hameçonnage est une liste fermée de codes de contrôle dont le déclenchement est une affirmation d’usurpation, jamais de simple courrier non désiré : une institution revendiquée dans le message sans rien qui lui appartienne et sans un lien qui rentre chez elle ; un nom d’affichage d’expéditeur qui se lit comme une institution sur une identité faible ; et les quatre tromperies de lien qui portent sur la destination du clic (un texte de lien qui nomme un domaine et mène à un autre, une adresse cachée derrière un @, un hôte en IP nue, un hôte punycode qui imite le latin). Il lit aussi les liens du HTML encodé (un expéditeur sur deux encode la version illustrée, et la moitié du courrier ne montrait aucune URL aux contrôles), parle allemand autant que français (un faux avis de livraison en allemand passait), et ne prend plus un logo hotlinké pour un lien vers la maison : les hôtes qui comptent sont ceux qu’un lecteur peut cliquer. L’axe est servi sur le message en phishing, normalisé contre le seuil d’indésirable, 1.0 voulant dire « ces preuves suffisaient seules à condamner », avec ses codes ; un filtre isPhishing sélectionne dessus.
  • L’axe authentification (ADR-146) est le même patron pour les contrôles qui échouent par construction quand un réémetteur sans ARC retransmet un message légitime : SPF depuis la mauvaise IP, DMARC qui suit, alignement DKIM perdu par la réécriture. Il existe pour un besoin d’exploitation : pendant une migration, l’ancien fournisseur renvoie le courrier vers la nouvelle boîte, et ce courrier était condamné d’office. oximail account set-forward-tolerance --email X --source mx.ancien.tld --until AAAA-MM-JJ (simulation par défaut, --apply écrit, --clear retire) déclare une tolérance par compte destinataire sur une source de renvoi reconnue par son domaine EHLO, avec une échéance obligatoire d’un an au plus. On ne ment jamais au score (SPF a échoué, le journal le dit) ; à la livraison, si la tolérance couvre la source et que la condamnation vient du seul axe authentification, le message est re-disposé en boîte de réception avec une étiquette au lieu d’Indésirables, pour ce destinataire seulement. Un p=reject publié reste hors tolérance : ARC est la réponse sanctionnée de l’écosystème, et contourner la politique d’un expéditeur n’est pas notre affaire. Les lignes échues sont purgées, et un ARC valide avec un DMARC d’origine passant reste le chemin préféré, venant d’un sceau listé dans [spam] arc_trusted_sealers.

Mesuré sur la flotte, puis changé

  • Les correspondants établis. Un domaine a une posture établie dans un compte dès que ce compte garde et a lu un de ses messages depuis au moins 14 jours (contre 7, mesuré sur 119 000 messages : le courrier légitime qui avait besoin de la posture était identique à 3, 7 et 14 jours, et le spam venant de domaines qui en tenaient une a baissé), et la posture adoucit une condamnation isolée du modèle statistique ou d’une seule liste DNSBL ; un correspondant avec qui vous échangez ne part plus en Indésirables sur le seul verdict bayésien, puisque ce verdict pesait à lui seul exactement le seuil. La relation se lit dans les deux sens (le formulaire de contact de votre propre site et les avis de votre registrar ne sont plus des inconnus perpétuels), et elle ne se fabrique pas en l’ignorant.
  • Les signaux du courrier de masse. Une signature List-Unsubscribe en un clic n’est plus un passe-droit au-dessus du seuil (un envoi listé avec un faisceau de preuves au-dessus chevauchait cette immunité jusqu’à la boîte de réception) ; un envoi en Cci massif sans authentification atteint la bande suspecte quel que soit le TLD (l’escalade dépendait d’une liste de douze) ; un statut de transaction (Paiement accepté, Commande réussie) n’est pas un nom d’expéditeur ; un renvoi par règle ne blanchit plus un message déjà jugé (une campagne mise en Indésirables sur une boîte aliasée repartait propre vers trois humains), et la copie qu’une règle redirige vers un autre compte local garde le verdict d’hameçonnage, les résultats d’authentification et le sélecteur BIMI relevés à l’entrée du message dans le serveur, reportés et non réévalués (elle arrivait comme si rien n’avait été vérifié).
  • Les réponses DNSBL sont lues, pas crues. Une inscription est un code 127.0.0.x dans le vocabulaire de la zone ; la bande d’erreur 127.255.x (« requête refusée », une faute dans le nom de zone, un résolveur public que la liste bloque) et les réponses aberrantes d’un résolveur qui détourne les domaines inexistants ne comptent plus comme une inscription, là où toute réponse comptait. Dans l’autre sens, seul NXDOMAIN veut dire « non listé » : un refus, une réponse sans adresse (NODATA), un SERVFAIL ou un délai dépassé ne pèse rien, mais il est journalisé et compté, là où il se lisait comme propre. Une liste qui ne répondait plus à la box faisait ainsi vérifier chaque message contre rien. oximail_dnsbl_answers_total{source,list,outcome} compte chaque réponse (source vaut inbound pour le filtre, self pour l’IP d’envoi de la box ; outcome vaut listed, clean, refused ou inconclusive), et oximail doctor --reachability demande à chaque liste son entrée de test 127.0.0.2 (RFC 5782 §5) sous dnsbl_lists : listée donne ok, un refus du résolveur de la box un échec, toute autre réponse un avertissement.
  • Le domaine organisationnel suit la liste des suffixes à jour. La liste des suffixes publics embarquée dans le filtre anti-spam (les contrôles de liens de phishing) et dans le résolveur de logos de marque passe de sa version de mai 2026 à celle de septembre 2026 (28 versions de suffixes ajoutés et retirés) : un site sous un suffixe nouvellement listé, comme chatgpt.site, n’est plus pris pour une partie du domaine organisationnel de son voisin.
  • Un verdict ne se perd plus à l’entrée. Un message condamné par le filtre faisait surface en boîte de réception sans trace du pourquoi quand la recherche du dossier Indésirables échouait au moment du rangement ; cet échec est bruyant désormais, et l’alerte sur les rapports agrégés DMARC dit ce que le rapporteur a expliqué (la raison de dérogation qu’il a appliquée), pas seulement combien ont échoué.

Entraînement et retour

Le corpus Bayes apprend des actions des utilisateurs : déplacer un message vers les Indésirables l’entraîne comme spam, en repêcher un l’entraîne comme légitime, depuis le bouton du webmail comme depuis IMAP (le geste passait par une propriété de dossier que le détecteur ne voyait jamais) ; un geste qui contredit un verdict que le filtre avait ancré comme légitime (la notification de votre propre outil de support, un collègue) est enregistré comme une opinion et n’apprend pas au corpus partagé à voir du spam dans son propre vocabulaire de travail (avec un plafond sur les votes légitimes répétés pour une même classe de message, pour qu’un utilisateur enthousiaste ne fausse pas le modèle). Le corpus est versionné par époque : quand la représentation des jetons change, le modèle est reconstruit depuis le corpus stocké au lieu de mélanger des générations incompatibles :
Pour demander au filtre ce qu’il pense maintenant, et non ce qu’il pensait à l’ingestion :
La commande score les messages les plus récents du compte (boîte de réception et Indésirables) contre le corpus courant, et affiche le verdict vivant à côté du score enregistré à l’ingestion. Elle n’écrit rien. La distinction est tout l’intérêt : les scores stockés sont figés à l’arrivée du message, donc après tout entraînement ou reconstruction ils décrivent un modèle qui n’existe plus, ce qui est précisément le moment où quelqu’un demande « ce message serait-il encore classé indésirable aujourd’hui ? ». Le réentraîneur horaire échoue bruyamment une fois plutôt que de journaliser une erreur toutes les heures pour toujours. Par l’API d’administration, /admin/v1/spam/stats, /spam/test, /spam/feedback/{tenant} et /spam/retrain/{tenant} exposent les mêmes opérations pour l’outillage. POST /spam/retrain/{tenant} lance un vrai réentraînement et renvoie son résultat (précision, échantillons utilisés, version du modèle) ; sans réentraîneur configuré (spam.retrain_enabled vaut false par défaut, aucune box n’embarque de script d’entraînement), la route répond 503 en nommant les deux réglages à changer, jamais un succès pour un travail qui n’a pas eu lieu.

Configuration

Les seuils ajustables à l’exécution se règlent en surcharges de configuration, sans redémarrage.

Anti-abus : limitation de débit et fail2ban

La protection contre la force brute et les inondations est dans le binaire ; n’installez pas le démon fail2ban à côté d’OxiMail :
  • Limiteur de débit : seau à jetons par IP ([rate_limit]), couvrant les surfaces HTTP et SMTP.
  • Fail2ban : les échecs d’authentification sur tous les protocoles (JMAP, IMAP, SMTP AUTH, DAV) sont suivis par IP ; franchir [security] fail2ban_max_attempts bannit l’IP avec escalade (60 min puis 6 h puis 24 h). Derrière un proxy, la vraie IP cliente est lue depuis X-Forwarded-For (uniquement depuis [server] trusted_proxies) ou le protocole PROXY : les bannissements frappent l’attaquant, pas votre proxy.
  • Surface opérateur : oximail unban <ip>, oximail ban list|add, et GET/DELETE /admin/v1/bans : voir Exploitation.
[security] trusted_ips contourne les deux mécanismes pour votre propre infrastructure.

Lire le verdict sur un message

Chaque message scoré porte un en-tête X-Spam-Status avec le verdict et les signaux qui y ont contribué : le premier endroit où regarder quand un utilisateur demande « pourquoi ce message est aux Indésirables » (ou « pourquoi celui-ci n’y est pas »). La décision de remise produite est dans les journaux du serveur avec son contexte complet.