Aller au contenu

abuse-guard : Bannissement automatique des clients abusifs selon le taux de réponses d'erreur

Installation

Vous pouvez installer ce module sur toute distribution basée sur RHEL, y compris, mais sans s'y limiter :

  • RedHat Enterprise Linux 7, 8, 9 et 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 et Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-abuse-guard
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-abuse-guard

Activez le module en ajoutant ce qui suit en haut de /etc/nginx/nginx.conf :

load_module modules/ngx_http_abuse_guard_module.so;

Ce document décrit nginx-module-abuse-guard v2.1.0 publié le 03 septembre 2026.


Votre journal d'erreurs est un aveu. Abuse Guard le lit en temps réel et exclut les auteurs d'abus.

Chaque scanner, fuzzer et bot de bourrage d'identifiants laisse la même empreinte : une rafale de 404 à la recherche de chemins cachés, des 403 qui secouent des portes verrouillées, des requêtes qui échouent les unes après les autres. Abuse Guard surveille les codes de statut que votre serveur renvoie réellement, identifie les clients dont le trafic est majoritairement constitué d'échecs, et les verrouille — la décision est prise à l'intérieur du worker NGINX, sur la requête elle-même, en quelques microsecondes. Pas de sidecar. Pas d'expéditeur de journaux. Pas de couche de script. Juste du C compilé qui fait un travail exceptionnellement bien.

Fiche technique

Déclencheur Taux par client de réponses d'erreur que vous choisissez (403/404 par défaut)
Action Verrouillage temporisé — un bannissement ferme pour une fenêtre fixe, pas une limitation
Point de décision Phase de préaccès NGINX, avant tout handler ou upstream
Modèle mémoire Octets fixes par client, indépendants du seuil → échelle botnet
Mode flotte Réplication facultative des bannissements entre nœuds via Redis / Valkey
Durabilité Instantanés de sortie facultatifs préservant les bannissements actifs entre les redémarrages
Empreinte Un module autonome ; zéro dépendance d'exécution par défaut
Plateformes RHEL / AlmaLinux / Rocky / CentOS Stream / Oracle / Amazon Linux
##

Le problème qu'il élimine

Les visiteurs légitimes ne génèrent presque jamais une rafale d'erreurs. Les auteurs d'abus ne génèrent guère que cela — cette asymétrie est tout l'enjeu. Un scanner de vulnérabilités qui parcourt votre arborescence est un mur de 404. Un bot qui sonde des points d'administration est un mur de 403. Une attaque par force brute est un mur d'échecs.

Les limiteurs de débit traitent ce trafic comme n'importe quel autre : ils ralentissent tout le monde par volume de requêtes et laissent l'auteur de l'infraction revenir immédiatement dès qu'il se calme. Abuse Guard fait l'inverse. Il ignore entièrement le trafic bien comporté et réserve sa seule réponse — un bannissement réel et limité dans le temps — aux clients définis par leurs erreurs.

Utilisez un limiteur de débit pour façonner la charge. Utilisez Abuse Guard pour expulser les abus.

Comment un bannissement est décidé

Trois éléments mobiles, tous à l'intérieur du worker :

1 · Un score qui fuit, par client. Chaque identité de client porte un seul petit nombre en mémoire partagée. Chaque erreur correspondante ajoute un point par défaut, ou le poids que vous attribuez à ce statut ; le score s'écoule en continu à seuil ÷ intervalle par seconde. Une rafale courte et brutale le fait passer au-dessus de la ligne ; un filet lent ne le fait jamais. Point crucial, ce score est un enregistrement de taille fixe quel que soit le seuil que vous définissez, de sorte qu'une seule zone suit confortablement les dizaines de milliers d'adresses sources distinctes qu'un botnet vous lance.

2 · Une échéance ferme. Au moment où le score franchit votre seuil, le client obtient un horodatage blocked_until. Jusqu'à cette date, il est simplement absent — chaque requête est refusée à la phase de préaccès, avant que NGINX ne dépense un cycle en routage, fichiers ou upstreams. Le rejet est le résultat le moins coûteux possible.

3 · Un refus respectueux de la vie privée. Les clients bannis reçoivent 429 Too Many Requests (le code de votre choix) étiqueté de sorte qu'aucun cache partagé ne puisse jamais le stocker et servir la punition d'un client à un autre, avec un Retry-After indiquant aux clients honnêtes quand revenir.

Les identités sont réduites à un condensé de taille fixe, donc le ciblage sur quelque chose de volumineux comme $request_uri ou un en-tête coûte exactement autant de mémoire que le ciblage sur une adresse IP.

Opérationnel en moins d'une minute

Abuse Guard est fourni comme module précompilé et signé depuis le dépôt GetPageSpeed — installez-le, aucune chaîne d'outils de compilation requise.

sudo yum -y install https://extras.getpagespeed.com/release-latest.rpm
sudo yum -y install nginx-module-abuse-guard

Branchez-le :

load_module modules/ngx_http_abuse_guard_module.so;

http {
    abuse_guard_zone zone=clients:10m;     # une zone de mémoire partagée

    server {
        location / {
            abuse_guard zone=clients;      # appliquez ici
        }
    }
}
sudo nginx -t && sudo systemctl reload nginx

Ces valeurs par défaut bannissent toute adresse IP qui renvoie 100 réponses 403/404 dans une fenêtre de 5 minutes, pendant une heure. Resserrez ou assouplissez chaque nombre ci-dessous.

Configuration

Abuse Guard se compose de quatre directives. La première déclare une politique ; les autres l'appliquent, exemptent des personnes, et (facultativement) la partagent entre machines.

Déclarer une politique — abuse_guard_zone

Une directive de niveau http. Elle découpe une zone de mémoire partagée et définit la politique qui la régit. Définissez autant ou aussi peu de paramètres que vous le souhaitez — le nom et la taille de la zone sont les seules choses obligatoires ; des valeurs par défaut judicieuses comblent le reste (les valeurs ci-dessous sont exactement ces valeurs par défaut).

abuse_guard_zone  zone=clients:10m             nom + taille (le seul obligatoire)
                  key=$binary_remote_addr      qui est « un client »
                  statuses=403,404             quelles réponses ajoutent au score
                  interval=300s                la fenêtre de notation
                  threshold=100                score dans cette fenêtre  bannissement
                  block=60m;                   combien de temps le bannissement tient

zone=clients:10m est l'identité et le budget de la politique : un nom que vous référencez depuis abuse_guard, et la taille de la mémoire partagée. Environ 10 Mo suivent de l'ordre d'une centaine de milliers de clients actifs.

Tout le reste est un réglage facultatif :

  • key — l'expression qui définit un seul client. Toute variable NGINX ; la valeur par défaut $binary_remote_addr cible l'adresse IP source. Une requête dont la clé sort vide est entièrement ignorée (pratique avec un map, ci-dessous).
  • statuses — les réponses qui ajoutent au score. Un code nu ou une plage a un poids de 1, préservant la syntaxe d'origine : statuses=403,404,500-599. Ajoutez :weight pour qu'une réponse consomme davantage du même budget, par exemple statuses=404,401:5,500-599:2. Les poids sont des entiers de 1 à 1024 et une plage applique son poids à chaque statut qu'elle couvre. Répéter ou chevaucher un statut avec le même poids est sans danger ; des poids conflictuels font échouer nginx -t. La valeur par défaut reste 403,404.
  • interval — la fenêtre sur laquelle le score décroît (par défaut 300s). Une rafale à l'intérieur déclenche un bannissement ; un filet lent étalé plus largement ne s'accumule jamais.
  • threshold — combien de points de score dans cette fenêtre franchissent la ligne, jusqu'à 1024 (par défaut 100). Une réponse lourde peut consommer le budget restant et déclencher un bannissement immédiatement.
  • block — combien de temps un client déclenché reste verrouillé (par défaut 60m).
  • inactive — combien de temps un client dormant reste en mémoire avant d'être récupéré (par défaut max(1h, interval, block) ; toute valeur explicite doit être au moins aussi grande que interval et block).
  • redison pour répliquer les bannissements de cette zone à travers une flotte (voir ci-dessous) ; off par défaut.
  • persist — un chemin de fichier où les bannissements actifs sont déchargés à la sortie propre du worker et chargés au démarrage.
  • persist_secret — sur les builds avec prise en charge des instantanés signés, une clé hexadécimale qui ajoute l'authentification HMAC-SHA256 afin qu'un fichier falsifié soit rejeté.

Pourquoi 5xx est exclu par défaut : une erreur serveur est généralement le fait de votre côté, et la compter permettrait à un backend instable de faire bannir des visiteurs innocents. Ajoutez statuses=403,404,500-599 uniquement lorsque vous voulez délibérément agir sur les clients qui déclenchent des erreurs serveur.

L'appliquer — abuse_guard

Valide dans les blocs http, server et location, afin que vous puissiez protéger tout un site ou seulement les points d'accès qui attirent les abus. Nommez la zone pour l'activer ; écrivez abuse_guard off; dans une portée imbriquée pour la désactiver.

location /wp-login.php {
    abuse_guard zone=clients status=429 log_level=warn;
}
  • zone — la zone (déclarée ci-dessus) dont la politique s'applique ici.
  • status — le code qu'un client banni reçoit, n'importe où dans 400599 (par défaut 429).
  • dry_runon pour observer sans appliquer : le verdict est journalisé mais aucun bannissement n'est écrit. Désactivé par défaut.
  • log_level — avec quelle intensité journaliser chaque décision : info, notice (par défaut), warn ou error.

Déployez sans crainte avec dry_run=on. Il enregistre chaque bannissement qu'il aurait émis sans toucher à l'état, afin que vous puissiez calibrer les seuils contre le trafic réel — même à côté d'un emplacement d'application sur la même zone — puis passez-le en direct.

Exempter les bons — abuse_guard_allow

Contexte : http · server · location · répétable, hérité vers le bas.

abuse_guard_allow 127.0.0.0/8;
abuse_guard_allow 10.0.0.0/8 192.168.0.0/16;

Les clients listés ne sont jamais comptés ni bannis. La correspondance se fait sur la véritable adresse de connexion, donc elle coopère avec realip. C'est aussi ainsi que vous protégez les robots d'exploration vérifiés : autorisez les plages publiées Googlebot / Bingbot afin qu'un robot qui parcourt des URL obsolètes (et accumule des 404) ne soit jamais attrapé.

Partager les bannissements à travers la flotte — abuse_guard_redis

Contexte : http

abuse_guard_redis host=10.0.0.5 password=… ;   # tls://host pour TLS
abuse_guard_zone  zone=clients:10m redis=on;

Pointez chaque nœud vers un Redis ou Valkey, activez redis=on, et un bannissement gagné sur n'importe quelle machine se propage à toutes. Valeurs par défaut : port=6379, db=0, prefix=ag_, timeout=100ms. Comment cela reste rapide est expliqué dans la section suivante.

SELinux : sur les systèmes d'application (RHEL, Rocky, AlmaLinux), le noyau empêche NGINX d'ouvrir la connexion à Redis jusqu'à ce que vous l'autorisiez une fois — setsebool -P httpd_can_network_connect 1. Si vous sautez cette étape, la réplication ne fait silencieusement rien pendant que l'application locale continue normalement.

Un bannissement, chaque nœud — sans ralentir une seule requête

Derrière un équilibreur de charge, un bannissement par serveur est du théâtre : l'attaquant atterrit simplement sur un autre nœud. Abuse Guard comble cette lacune sans jamais mettre Redis dans le chemin des requêtes.

Chaque nœud décide localement et compte localement. Dès qu'il émet un bannissement, il diffuse ce seul fait au cluster et enregistre une copie durable. Chaque autre nœud l'importe en quelques millisecondes, et tout nœud qui était hors ligne se réconcilie au moment de sa reconnexion. Parce que l'application est toujours servie depuis l'état mémoire de chaque nœud, la requête d'un visiteur n'attend jamais un aller-retour réseau — le seul coût du regroupement est qu'un attaquant fraîchement banni est exclu à l'échelle de la flotte un battement de cœur plus tard au lieu d'instantanément.

Redis ici est une cloche d'alarme unidirectionnelle, pas un registre partagé consulté par requête — donc un Redis lent ou manquant ne peut jamais ajouter de latence à votre trafic. Exécutez-le sur un réseau privé et traitez-le comme privilégié : tout ce qui peut y écrire peut émettre des bannissements.

Des bannissements qui survivent à un redémarrage

Pointez une zone vers un fichier et les bannissements actifs sont déchargés lorsque le worker se termine proprement, puis restaurés au démarrage. Les rechargements et les redémarrages ordonnés conservent les bannissements actuels sans exécuter un rédacteur périodique de zone complète. Une panne brutale de processus ou de machine peut perdre les bannissements émis depuis la dernière sortie propre ; l'application échoue toujours ouvertement.

abuse_guard_zone zone=clients:10m
                 persist=/var/lib/nginx/abuse_guard/clients.state
                 persist_secret=00112233445566778899aabbccddeeff;

L'instantané compact ne contient que des condensés d'identité et des échéances de bannissement. CRC32 détecte la corruption, et un renommage atomique maintient les écritures partielles hors du chemin actif. Les builds avec prise en charge des instantanés signés peuvent en outre l'authentifier avec persist_secret. Gardez le répertoire lisible uniquement par l'utilisateur du worker.

Voir tout ce qu'il décide

Trois variables exposent le verdict d'Abuse Guard à vos journaux et à votre configuration :

Variable Valeur
$abuse_guard_status BYPASSED · PASSED · COUNTED · BLOCKED · DRY_RUN
$abuse_guard_count Score pondéré actuel, arrondi à un point entier.
$abuse_guard_blocked_until Heure Unix à laquelle le bannissement est levé, ou 0.
log_format guard '$remote_addr "$request" $status '
                 'guard=$abuse_guard_status count=$abuse_guard_count';

Ciblage derrière un CDN ou un proxy ? Ne faites jamais confiance à un X-Forwarded-For brut. Laissez realip résoudre le vrai client d'abord, puis ciblez sur $binary_remote_addr :

set_real_ip_from 10.0.0.0/8;
real_ip_header   X-Forwarded-For;
real_ip_recursive on;

Besoin d'une logique d'exemption par requête ? Toute requête dont la key se résout en une chaîne vide est ignorée — donc un map vous permet, par exemple, de suivre les visiteurs anonymes par IP tout en laissant les utilisateurs authentifiés intacts.

Conçu pour être digne de confiance en production

Abuse Guard est tenu à une norme bien supérieure à « ça compile ». Chaque modification passe par le parcours du combattant d'AddressSanitizer, UndefinedBehaviorSanitizer, Valgrind, l'analyse statique et le fuzzing continu de ses analyseurs et de son format sur disque. Ses dépendances facultatives — regroupement et instantanés signés — sont au mieux par conception : si Redis ou le disque se comporte mal, l'application continue tranquillement depuis la mémoire locale. Votre trafic n'est jamais pris en otage par une dépendance.

Obtenir Abuse Guard

Abuse Guard est un module NGINX commercial de GetPageSpeed LLC, livré avec des mises à jour continues et un support via un abonnement GetPageSpeed.

© GetPageSpeed LLC. Tous droits réservés.