Aller au contenu

nftset-access: Blocage IP à latence nulle utilisant les ensembles nftables du noyau Linux

Nécessite le plan Pro (ou supérieur) de l'abonnement GetPageSpeed NGINX Extras.

Installation

Vous pouvez installer ce module dans n'importe quelle 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-nftset-access
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-nftset-access

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

load_module modules/ngx_http_nftset_access.so;

Ce document décrit nginx-module-nftset-access v3.0.0 publié le 14 février 2026.


Contrôle d'accès basé sur l'IP de niveau entreprise pour NGINX utilisant les ensembles nftables de Linux. Bloquez les menaces, limitez le taux des abus, défiez les bots et protégez votre infrastructure.

Version GetPageSpeed

⚠️ Logiciel Commercial Il s'agit d'un module premium à code source fermé disponible exclusivement via le GetPageSpeed Repository.

Exigence de plan : Nécessite le plan Pro de l'abonnement GetPageSpeed NGINX Extras.

✨ Fonctionnalités

Fonctionnalités principales

Fonctionnalité Description
Liste blanche/Liste noire Autoriser ou interdire en fonction de l'appartenance à l'ensemble nftables
Ensembles multiples Vérifiez plusieurs ensembles nft dans une seule directive
Mises à jour en direct Modifiez les ensembles nft sans recharger NGINX
Codes d'état personnalisés Retournez tout code HTTP lors du blocage
Support CIDR Utilisez des ensembles d'intervalles pour les plages réseau (par exemple, 192.168.1.0/24)

Fonctionnalités de performance

Fonctionnalité Description
Sessions par thread Contextes libnftables locaux au thread éliminent la contention de verrouillage
Cache LRU Cache en mémoire partagée avec TTL configurable
Taux de réussite du cache Taux de réussite typiquement supérieur à 95 % réduit les appels au noyau

Fonctionnalités de sécurité

Fonctionnalité Description
Limitation de taux Limitez les requêtes par IP avec des fenêtres configurables
Auto-interdiction Mettez automatiquement sur liste noire les violateurs de la limite de taux
Défi JS Le défi de preuve de travail arrête les bots automatisés
Pièges honeypot Liste noire automatique des IP touchant les URL pièges
Délai d'entrée Expiration automatique des entrées de liste noire

Fonctionnalités opérationnelles

Fonctionnalité Description
Mode dry-run Testez la configuration sans bloquer
Fail-open/close Contrôlez le comportement en cas d'erreurs d'ensemble nft
Métriques Prometheus Point de terminaison /metrics natif pour Grafana
Statistiques JSON API de statistiques détaillées
Variables NGINX $nftset_result et $nftset_matched_set

🚀 Démarrage rapide

1. Créer des ensembles nftables

# Créer une table (si elle n'existe pas)
sudo nft add table ip filter

# Créer une liste noire avec support de délai d'expiration
sudo nft add set ip filter bad_guys '{ type ipv4_addr; flags timeout; timeout 1d; }'

# Créer une liste de bannissement par limite de taux
sudo nft add set ip filter ratelimited '{ type ipv4_addr; flags timeout; timeout 30m; }'

# Créer une liste de pièges honeypot
sudo nft add set ip filter honeypot '{ type ipv4_addr; flags timeout; timeout 1d; }'

# Créer une liste blanche avec support CIDR
sudo nft add set ip filter trusted '{ type ipv4_addr; flags interval; }'
sudo nft add element ip filter trusted '{ 10.0.0.0/8, 192.168.0.0/16 }'

2. Configurer NGINX

load_module modules/ngx_http_nftset_access_module.so;

http {
    server {
        listen 80;

        # Bloquer les IPs connues comme mauvaises (format : table:setname)
        nftset_blacklist filter:bad_guys;

        # Limitation de taux : 100 requêtes par minute
        nftset_ratelimit rate=100 window=60s autoban=filter:ratelimited;

        # Votre contenu
        location / {
            root /var/www/html;
        }

        # Piège honeypot - retourne 404 par défaut
        location /wp-admin.php {
            nftset_autoadd filter:honeypot timeout=86400;
        }

        # Point de terminaison des métriques
        location /metrics {
            nftset_metrics;
            allow 127.0.0.1;
            deny all;
        }
    }
}

3. Tester et recharger

sudo nginx -t && sudo nginx -s reload

📦 Installation

Ce module est disponible exclusivement via le GetPageSpeed Premium Repository.

Étape 1 : S'abonner au GetPageSpeed Repository

Visitez GetPageSpeed Repository Subscription pour obtenir l'accès.

Étape 2 : Installer le Repository

# RHEL/CentOS/Rocky/Alma Linux 8+
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm

Étape 3 : Installer le Module

sudo dnf install nginx-module-nftset-access

Étape 4 : Activer le Module

Ajoutez à /etc/nginx/nginx.conf avant tout bloc http {} :

load_module modules/ngx_http_nftset_access_module.so;

Étape 5 : Recharger NGINX

sudo nginx -t && sudo systemctl reload nginx

📖 Référence de configuration

Format de spécification d'ensemble

Toutes les directives qui font référence aux ensembles nftables utilisent le format : table:setname

  • table — Le nom de la table nftables (par exemple, filter, firewalld)
  • setname — Le nom de l'ensemble dans cette table

La famille IP (ip pour IPv4, ip6 pour IPv6) est auto-détectée à partir de l'adresse IP du client.

Exemples :

nftset_blacklist filter:blocklist;           # Client IPv4 → ip filter blocklist
                                             # Client IPv6 → ip6 filter blocklist
nftset_whitelist filter:trusted;
nftset_autoadd filter:honeypot timeout=3600;

Contrôle d'accès

nftset_blacklist table:set1 [table:set2 ...]

Contexte : http, server Par défaut :

Bloque les requêtes si l'IP du client apparaît dans n'importe lequel des ensembles nft listés. Plusieurs ensembles sont vérifiés dans l'ordre jusqu'à ce qu'une correspondance soit trouvée.

# Ensemble unique
nftset_blacklist filter:bad_guys;

# Ensembles multiples (logique OU - bloqué si dans UN ensemble)
nftset_blacklist filter:spammers filter:hackers filter:tor_exits;

# Désactiver
nftset_blacklist off;

nftset_whitelist table:set1 [table:set2 ...]

Contexte : http, server Par défaut :

Autorise les requêtes uniquement si l'IP du client apparaît dans au moins un des ensembles nft listés. Toutes les autres IP sont rejetées.

# Autoriser uniquement les IPs de confiance
nftset_whitelist filter:trusted_partners filter:office_ips;

Important : Les IPs sur liste blanche contournent toutes les restrictions du module, y compris : - Limitation de taux (nftset_ratelimit) - Défis JavaScript (nftset_challenge)

Ceci est utile pour les IPs administratives qui ne devraient pas être soumises à des limites de taux ou à des défis :

# Les IPs administratives contournent les limitations de taux et les défis
nftset_whitelist filter:admin_ips;
nftset_ratelimit rate=100 window=1m autoban=filter:ratelimited ban_time=1800;
nftset_challenge on;

nftset_status code

Contexte : http, server Par défaut : 403

Code d'état HTTP retourné lorsqu'une requête est bloquée.

nftset_status 403;   # Interdit (par défaut)
nftset_status 444;   # Fermer la connexion sans réponse (spécial NGINX)
nftset_status 429;   # Trop de requêtes
nftset_status 503;   # Service indisponible

Mise en cache & Performance

nftset_cache_ttl time

Contexte : http, server Par défaut : 60s

Combien de temps mettre en cache les résultats de recherche d'ensemble nft. Les résultats mis en cache évitent les appels répétés au noyau pour la même IP.

nftset_cache_ttl 30s;    # 30 secondes
nftset_cache_ttl 5m;     # 5 minutes
nftset_cache_ttl 1h;     # 1 heure

Remarque de débogage : Si vous retirez une IP d'un ensemble nft mais que le module continue de la signaler comme "correspondante", cela est dû à la mise en cache. Le résultat mis en cache expirera après le TTL configuré. Pour un effet immédiat lors des tests, vous pouvez temporairement définir nftset_cache_ttl 0; pour désactiver la mise en cache (non recommandé pour la production en raison de l'impact sur la performance).

Impact sur la performance : - TTL plus élevé = Meilleure performance, mais plus lent à refléter les changements d'ensemble nft - TTL plus bas = Plus réactif aux changements d'ensemble nft, mais plus d'appels au noyau - Recommandé : 30s à 5m pour la plupart des cas d'utilisation

nftset_fail_open on|off

Contexte : http, server Par défaut : off

Contrôle le comportement lorsqu'une recherche d'ensemble nft échoue (par exemple, l'ensemble n'existe pas).

nftset_fail_open off;   # Refuser en cas d'erreur (sécurisé, par défaut)
nftset_fail_open on;    # Autoriser en cas d'erreur (disponible mais risqué)

nftset_dryrun on|off

Contexte : http, server Par défaut : off

Lorsqu'il est activé, journalise ce qui serait bloqué mais ne bloque pas réellement. Parfait pour tester de nouvelles règles en production.

nftset_dryrun on;   # Journaliser mais ne pas bloquer

Vérifiez les journaux pour des messages comme :

nftset: DRYRUN would block 1.2.3.4 (matched: filter:bad_guys)

Important : Lorsque vous utilisez les variables $nftset_result et $nftset_matched_set avec le mode dryrun, ces valeurs reflètent l'état à un instant donné lorsque la requête a été traitée - pas l'état actuel de l'ensemble nft. Si vous vérifiez l'ensemble nft manuellement plus tard et ne trouvez pas l'IP, les raisons possibles incluent :

  1. Expiration du délai : L'IP a été ajoutée avec un délai (par exemple, timeout 1d) et a depuis expiré
  2. Retard de cache : Le module met en cache les résultats de recherche (par défaut 60s). Une entrée retirée de l'ensemble nft peut encore apparaître comme "correspondante" jusqu'à ce que le cache expire
  3. Retrait manuel : Quelqu'un ou quelque chose (fail2ban, scripts) a retiré l'entrée

C'est un comportement attendu - le dryrun vous montre exactement ce que la production verrait au moment de la requête.

Limitation de taux

nftset_ratelimit parameters

Contexte : http, server Par défaut :

Limite les requêtes par IP dans une fenêtre temporelle. Peut ajouter automatiquement les violateurs à un ensemble nft.

Paramètres :

Paramètre Requis Description
rate=N Oui Maximum de requêtes par fenêtre
window=TIME Non Fenêtre temporelle (par défaut : 60s)
autoban=TABLE:SET Non ensemble nft à ajouter aux violateurs
ban_time=N Non Secondes jusqu'à expiration automatique (par défaut : 3600)

Exemples :

# Basique : 100 requêtes par minute
nftset_ratelimit rate=100;

# Avec fenêtre personnalisée : 1000 requêtes par heure
nftset_ratelimit rate=1000 window=1h;

# Avec auto-interdiction : Ajouter les violateurs à l'ensemble nft pendant 30 minutes
nftset_ratelimit rate=60 window=1m autoban=filter:ratelimited ban_time=1800;

# Protection API stricte
nftset_ratelimit rate=10 window=1s autoban=filter:api_abusers ban_time=3600;

Comment ça fonctionne : 1. Chaque IP obtient un compteur de requêtes et un temps de début de fenêtre 2. Le compteur s'incrémente à chaque requête 3. Lorsque la fenêtre expire, le compteur se réinitialise 4. Si le compteur dépasse rate, retourne 429 Trop de requêtes 5. Si autoban est défini, l'IP est ajoutée à l'ensemble nft spécifié

Remarque : L'état de la limite de taux est stocké en mémoire partagée et survit aux redémarrages des travailleurs.

Défi JavaScript

nftset_challenge on|off

Contexte : http, server Par défaut : off

Active le mode défi JavaScript. Les navigateurs doivent résoudre une énigme de preuve de travail pour accéder au site. Efficace contre les bots automatisés et les scrapers.

nftset_challenge on;

Comment ça fonctionne : 1. La première requête reçoit une page de défi (HTTP 503) 2. Le navigateur exécute JavaScript qui résout une énigme de hachage 3. La solution est stockée dans un cookie (_nftset_verified) 4. Les requêtes suivantes avec un cookie valide passent 5. Le cookie expire après 24 heures

nftset_challenge_difficulty level

Contexte : http, server Par défaut : 2

Contrôle la difficulté du défi (1-8). Plus élevé = temps de résolution plus long.

Niveau Temps de résolution approximatif
1 ~100ms
2 ~500ms (par défaut)
3 ~1 seconde
4 ~2 secondes
5 ~5 secondes
6+ ~10+ secondes
nftset_challenge on;
nftset_challenge_difficulty 3;  # ~1 seconde de temps de résolution

Fonctionnalités de la page de défi : - Design moderne et réactif - Indicateur de chargement animé - Retour d'information sur la progression - Redirection automatique en cas de succès - Pas de dépendances externes

Auto-ajout Honeypot

nftset_autoadd table:setname [table:setname2 ...] [timeout=seconds] [status=code]

Contexte : server, location Par défaut :

Ajoute automatiquement l'IP du client aux ensembles nft spécifiés lorsque la localisation est accédée et retourne un code d'état HTTP. Parfait pour les pièges honeypot.

Paramètres :

Paramètre Requis Description
table:setname Oui Ensemble nft cible (peut spécifier plusieurs)
timeout=N Non Délai d'expiration de l'entrée en secondes
status=N Non Code d'état HTTP à retourner (par défaut : 404)

Exemples :

# Basique : Ajouter à l'ensemble honeypot et retourner 404 (par défaut)
location /config.php {
    nftset_autoadd filter:honeypot;
}

# Avec délai : Expiration automatique après 24 heures
location /wp-admin.php {
    nftset_autoadd filter:scanners timeout=86400;
}

# Retourner 403 Interdit au lieu de 404
location /admin.php {
    nftset_autoadd filter:honeypot timeout=86400 status=403;
}

# Ajouter à plusieurs ensembles (IPv4 et IPv6)
location /trap {
    nftset_autoadd filter:honeypot4 filter:honeypot6 timeout=86400;
}

Chemins de piège courants :

# Pièges WordPress - retourne 404 pour sembler être un fichier manquant
location ~ ^/(wp-admin\.php|wp-login\.php|xmlrpc\.php)$ {
    nftset_autoadd filter:honeypot timeout=86400;
}

# Pièges de fichiers de configuration - retourne 403 pour simuler un accès interdit
location ~ ^/(\.env|config\.php|phpinfo\.php)$ {
    nftset_autoadd filter:honeypot timeout=86400 status=403;
}

# Pièges de shell/exploit - sévère, bloquer pendant 1 semaine
location ~ ^/(shell|cmd|eval|exec)\.php$ {
    nftset_autoadd filter:malicious timeout=604800 status=403;
}

Remarque : Lorsqu'une IP est auto-ajoutée, le module retourne immédiatement le code d'état HTTP spécifié (404 par défaut), empêchant tout traitement de requête ultérieur. Le keep-alive de la connexion est également désactivé pour éviter d'autres requêtes sur la même connexion.

Observabilité

nftset_stats

Contexte : location Par défaut :

Active le point de terminaison des statistiques JSON.

location = /_stats {
    nftset_stats;
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;
}

Voir JSON Stats API pour le format de réponse.

nftset_metrics

Contexte : location Par défaut :

Active le point de terminaison des métriques Prometheus.

location = /metrics {
    nftset_metrics;
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;
}

Voir Prometheus Metrics pour les métriques disponibles.

📝 Variables NGINX

Le module expose deux variables à utiliser dans les journaux, les en-têtes ou les conditionnels.

$nftset_result

La décision d'accès prise pour cette requête.

Valeur Description
allow Requête autorisée
deny Requête bloquée
dryrun Serait bloquée (mode dry-run)
ratelimited Limite de taux dépassée
challenged Page de défi servie

$nftset_matched_set

Nom de l'ensemble nft qui a correspondu (le cas échéant), au format table:setname. Vide s'il n'y a pas de correspondance.

Remarque : Cette variable reflète l'état de correspondance au moment de la requête, pas l'état actuel de l'ensemble nft. Si vous vérifiez l'ensemble nft manuellement et ne trouvez pas l'IP : - L'entrée peut avoir expiré (les ensembles nft prennent en charge les délais d'expiration par entrée) - Le cache du module (par défaut 60s) peut montrer une entrée récemment retirée comme toujours correspondante - Quelque chose peut avoir retiré l'entrée après que la requête a été traitée

Exemples d'utilisation

Journal d'accès personnalisé :

log_format security '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    'nftset_result="$nftset_result" '
                    'matched_set="$nftset_matched_set"';

access_log /var/log/nginx/security.log security;

Ajouter des en-têtes pour le débogage :

add_header X-NFTSet-Result $nftset_result always;
add_header X-NFTSet-Matched $nftset_matched_set always;

Journalisation conditionnelle :

# Journaliser uniquement les requêtes bloquées
map $nftset_result $loggable {
    "deny"  1;
    default 0;
}

access_log /var/log/nginx/blocked.log combined if=$loggable;

📊 Métriques Prometheus

Le point de terminaison /metrics retourne des métriques au format d'exposition Prometheus.

Métriques disponibles

# HELP nginx_nftset_requests_total Total des requêtes traitées
# TYPE nginx_nftset_requests_total counter
nginx_nftset_requests_total{result="checked"} 1234567
nginx_nftset_requests_total{result="allowed"} 1234000
nginx_nftset_requests_total{result="blocked"} 500
nginx_nftset_requests_total{result="error"} 67

# HELP nginx_nftset_cache_total Opérations de cache
# TYPE nginx_nftset_cache_total counter
nginx_nftset_cache_total{result="hit"} 1200000
nginx_nftset_cache_total{result="miss"} 34567

# HELP nginx_nftset_cache_entries Entrées de cache actuelles
# TYPE nginx_nftset_cache_entries gauge
nginx_nftset_cache_entries 5432

# HELP nginx_nftset_autoadd_total Opérations d'auto-ajout
# TYPE nginx_nftset_autoadd_total counter
nginx_nftset_autoadd_total{result="success"} 42
nginx_nftset_autoadd_total{result="failed"} 3

# HELP nginx_nftset_ratelimit_total Événements de limitation de taux
# TYPE nginx_nftset_ratelimit_total counter
nginx_nftset_ratelimit_total{action="triggered"} 156
nginx_nftset_ratelimit_total{action="autobanned"} 23

# HELP nginx_nftset_challenge_total Événements de défi
# TYPE nginx_nftset_challenge_total counter
nginx_nftset_challenge_total{result="issued"} 1000
nginx_nftset_challenge_total{result="passed"} 950
nginx_nftset_challenge_total{result="failed"} 50

# HELP nginx_nftset_uptime_seconds Temps de fonctionnement du module
# TYPE nginx_nftset_uptime_seconds gauge
nginx_nftset_uptime_seconds 86400

Requêtes du tableau de bord Grafana

Taux de requêtes par résultat :

rate(nginx_nftset_requests_total[5m])

Taux de blocage :

rate(nginx_nftset_requests_total{result="blocked"}[5m])

Taux de réussite du cache :

rate(nginx_nftset_cache_total{result="hit"}[5m]) /
(rate(nginx_nftset_cache_total{result="hit"}[5m]) + rate(nginx_nftset_cache_total{result="miss"}[5m]))

Déclenchements de limite de taux par minute :

rate(nginx_nftset_ratelimit_total{action="triggered"}[1m]) * 60

📈 JSON Stats API

Le point de terminaison /_stats retourne des statistiques détaillées au format JSON.

Format de réponse

{
  "version": "3.0.0",
  "uptime_seconds": 86400,
  "requests": {
    "checked": 1234567,
    "allowed": 1234000,
    "blocked": 500,
    "errors": 67
  },
  "cache": {
    "hits": 1200000,
    "misses": 34567,
    "entries": 5432,
    "hit_rate": 97.20
  },
  "autoadd": {
    "success": 42,
    "failed": 3
  },
  "ratelimit": {
    "triggered": 156,
    "autobanned": 23
  },
  "challenge": {
    "issued": 1000,
    "passed": 950,
    "failed": 50
  }
}

Descriptions des champs

Champ Description
version Version du module
uptime_seconds Secondes depuis le chargement du module
requests.checked Total des requêtes traitées
requests.allowed Requêtes qui ont passé
requests.blocked Requêtes qui ont été bloquées
requests.errors Erreurs de recherche d'ensemble nft
cache.hits Hits de cache (évité appel au noyau)
cache.misses Misses de cache (appel au noyau requis)
cache.entries Entrées mises en cache actuelles
cache.hit_rate Pourcentage de taux de réussite
autoadd.success Ajouts honeypot réussis
autoadd.failed Échecs d'ajouts honeypot
ratelimit.triggered Violations de limite de taux
ratelimit.autobanned IPs ajoutées automatiquement à la liste de bannissement
challenge.issued Pages de défi servies
challenge.passed Défis résolus avec succès
challenge.failed Échecs de défi

🏗️ Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                           FLUX DE REQUÊTE                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   Requête entrante                                                  │
│         │                                                            │
│         ▼                                                            │
│   ┌───────────────┐                                                  │
│   │  Vérification  │──── Dépassé ? ────▶ 429 + Auto-interdiction     │
│   │    de taux     │                                                  │
│   └───────┬───────┘                                                  │
│           │ OK                                                       │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │   Vérification │──── Pas de cookie ? ────▶ Servir JS Puzzle      │
│   │    de défi     │                                                  │
│   └───────┬───────┘                                                  │
│           │ Passé                                                   │
│           ▼                                                          │
│   ┌───────────────┐     ┌─────────────┐                             │
│   │  Vérification  │────▶│   HIT       │────▶ Utiliser le résultat mis en cache │
│   └───────┬───────┘     └─────────────┘                             │
│           │ MISS                                                     │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │  Requête nft   │──── Contexte libnftables local au thread        │
│   │  (noyau)      │                                                  │
│   └───────┬───────┘                                                  │
│           │                                                          │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │ Stocker dans le cache│                                              │
│   └───────┬───────┘                                                  │
│           │                                                          │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │    Décision    │──── Correspondance sur liste noire ? ────▶ Bloquer (403/444)  │
│   │                │──── Correspondance sur liste blanche ? ────▶ Bloquer (403/444)  │
│   └───────┬───────┘                                                  │
│           │ Autoriser                                                │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │   Vérification  │──── Correspondance de localisation ? ────▶ Ajouter à l'ensemble nft │
│   │    honeypot     │                                                  │
│   └───────┬───────┘                                                  │
│           │                                                          │
│           ▼                                                          │
│       Continuer vers                                               │
│       le gestionnaire de contenu                                     │
│                                                                      │
├─────────────────────────────────────────────────────────────────────┤
│                        MÉMOIRE PARTAGÉE                             │
│  ┌────────────────┬─────────────────┬─────────────────────────────┐ │
│  │     Statistiques │    Cache LRU    │    Seaux de Limitation de Taux │ │
│  │   (compteurs)   │  (IP → Résultat)  │   (IP → Compteur de Requêtes) │ │
│  └────────────────┴─────────────────┴─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

Disposition de la mémoire

Composant Emplacement But
Contexte libnftables Local au thread Contexte par travailleur pour éviter les verrous
Cache de recherche Mémoire partagée Cache LRU des mappages IP→résultat
Seaux de limitation de taux Mémoire partagée Compteurs de requêtes par IP
Statistiques Mémoire partagée Compteurs atomiques pour les métriques

📚 Exemples

Exemple 1 : Liste noire basique

# Créer une table nft et un ensemble
sudo nft add table ip filter
sudo nft add set ip filter blacklist '{ type ipv4_addr; }'
sudo nft add element ip filter blacklist '{ 1.2.3.4 }'
server {
    listen 80;

    nftset_blacklist filter:blacklist;

    location / {
        root /var/www/html;
    }
}

Exemple 2 : API avec limitation de taux

server {
    listen 80;

    # Limitation stricte de taux pour l'API
    nftset_ratelimit rate=100 window=1m autoban=filter:api_banned ban_time=3600;

    # Autoriser uniquement les partenaires connus
    nftset_whitelist filter:api_partners;
    nftset_status 401;

    location /api/ {
        proxy_pass http://backend;
    }
}

Exemple 3 : Pile de sécurité complète

server {
    listen 80 default_server;

    # Couche 1 : Menaces connues
    nftset_blacklist filter:malware_ips filter:tor_exits filter:datacenter_ranges;
    nftset_status 444;
    nftset_cache_ttl 5m;

    # Couche 2 : Limitation de taux
    nftset_ratelimit rate=60 window=1m autoban=filter:ratelimited ban_time=1800;

    # Couche 3 : Défi bot
    nftset_challenge on;
    nftset_challenge_difficulty 2;

    # Contenu réel
    location / {
        root /var/www/html;
    }

    # Pièges honeypot - retourne 404 (par défaut) pour sembler être des fichiers manquants
    location ~ ^/(wp-admin|phpmyadmin|admin)\.php$ {
        nftset_autoadd filter:honeypot timeout=86400;
    }

    # Surveillance
    location = /metrics {
        nftset_metrics;
        allow 10.0.0.0/8;
        deny all;
    }
}

Exemple 4 : Test en mode dry-run

server {
    listen 80;

    # Tester de nouvelles règles sans bloquer
    nftset_blacklist filter:new_threat_list;
    nftset_dryrun on;

    location / {
        root /var/www/html;
    }
}

Vérifiez les journaux :

tail -f /var/log/nginx/error.log | grep "DRYRUN"

Exemple 5 : Liste blanche CIDR (plages réseau)

# Créer un ensemble d'intervalles pour les plages CIDR
sudo nft add table ip filter
sudo nft add set ip filter trusted '{ type ipv4_addr; flags interval; }'
sudo nft add element ip filter trusted '{ 192.168.1.0/24, 10.0.0.0/8 }'

# Version IPv6
sudo nft add table ip6 filter
sudo nft add set ip6 filter trusted6 '{ type ipv6_addr; flags interval; }'
sudo nft add element ip6 filter trusted6 '{ 2001:db8::/32 }'
server {
    listen 80;

    # Liste blanche de réseaux entiers
    nftset_whitelist filter:trusted;

    location / {
        root /var/www/html;
    }
}

🔧 Dépannage

Module non chargé

nginx: [emerg] dlopen() failed

Solution : Assurez-vous que NGINX a été construit avec --with-compat et que le module a été construit contre la même version de NGINX.

Ensemble nft non trouvé

nftset: set 'filter:myset' does not exist

Solution : Créez la table nft et l'ensemble avant de démarrer NGINX :

sudo nft add table ip filter
sudo nft add set ip filter myset '{ type ipv4_addr; }'

ASTUCE : Listez les ensembles disponibles avec :

nft list sets

Permission refusée

nftset: kernel error

Solution : Le travailleur NGINX a besoin de la capacité CAP_NET_ADMIN :

sudo setcap cap_net_admin+ep /usr/sbin/nginx

Refus SELinux (RHEL/CentOS/AlmaLinux)

SELinux empêche /usr/sbin/nginx d'utiliser netlink_netfilter_socket

Solution : Installez le module de politique SELinux inclus :

cd selinux/
sudo ./install.sh

Ou manuellement :

# Vérifier
semodule -l | grep nginx_nftset

La politique permet à httpd_t (le domaine SELinux de NGINX) d'utiliser les sockets netlink_netfilter requis par libnftables.

Utilisation élevée de la mémoire

Solution : Réduisez le TTL du cache ou limitez la taille du cache dans la configuration de la mémoire partagée.

Limitation de taux non fonctionnelle

Solution : Assurez-vous que l'ensemble nft pour l'auto-interdiction existe et a un support de délai d'expiration :

sudo nft add table ip filter
sudo nft add set ip filter ratelimited '{ type ipv4_addr; flags timeout; timeout 1h; }'

Le journal montre "matched=table:setname" mais l'IP n'est pas dans l'ensemble nft

C'est un comportement attendu. Le module rapporte ce qu'il a vu au moment de la requête. Si vous vérifiez l'ensemble nft plus tard et ne trouvez pas l'IP :

  1. Expiration du délai : L'IP a été ajoutée avec un délai et a depuis expiré

    # Vérifiez les drapeaux de l'ensemble
    nft list set ip filter setname
    # Recherchez "flags timeout" dans la sortie
    

  2. Cache du module : Le module met en cache les recherches (par défaut 60s). Une IP récemment retirée peut encore apparaître comme "correspondante"

    # Désactivez temporairement le cache pour le débogage (pas pour la production !)
    nftset_cache_ttl 0;
    

  3. L'entrée a été retirée : fail2ban, des scripts ou des commandes manuelles peuvent l'avoir retirée

  4. Problème de configuration de piège : Si vous utilisez des pièges honeypot avec nftset_autoadd, des bots légitimes peuvent avoir déclenché des pièges. Vérifiez que vos emplacements de piège ne se chevauchent pas avec des chemins de bots légitimes (comme les sitemaps, robots.txt). Utilisez robots.txt pour exclure les chemins de piège du crawl.

autoadd échoue avec une erreur de délai

Cela signifie que vous utilisez timeout=N dans nftset_autoadd mais que l'ensemble nft a été créé sans support de délai d'expiration.

Solution : Recréez l'ensemble nft avec un support de délai d'expiration :

# Supprimer et recréer avec le drapeau de délai
sudo nft delete set ip filter honeypot
sudo nft add set ip filter honeypot '{ type ipv4_addr; flags timeout; timeout 1d; }'

🔄 Migration depuis ipset-access

Si vous migrez depuis l'ancien ngx_http_ipset_access_module, suivez ces étapes :

1. Convertir les ipsets en ensembles nft

# Ancien ipset
ipset create bad_guys hash:ip timeout 86400

# Nouvel ensemble nft équivalent
nft add table ip filter
nft add set ip filter bad_guys '{ type ipv4_addr; flags timeout; timeout 1d; }'

# Pour les plages CIDR (hash:net → drapeau d'intervalle)
# Ancien : ipset create networks hash:net
# Nouveau :
nft add set ip filter networks '{ type ipv4_addr; flags interval; }'

2. Mettre à jour la configuration NGINX

Ancien (ipset) Nouveau (nftset)
ipset_blacklist bad_guys; nftset_blacklist filter:bad_guys;
ipset_whitelist trusted; nftset_whitelist filter:trusted;
ipset_autoadd honeypot timeout=3600; nftset_autoadd filter:honeypot timeout=3600;
ipset_ratelimit rate=100 autoban=ratelimited; nftset_ratelimit rate=100 autoban=filter:ratelimited;
ipset_challenge on; nftset_challenge on;
ipset_challenge_difficulty 3; nftset_challenge_difficulty 3;
ipset_dryrun on; nftset_dryrun on;
ipset_fail_open on; nftset_fail_open on;
ipset_cache_ttl 60s; nftset_cache_ttl 60s;
ipset_status 403; nftset_status 403;
ipset_stats; nftset_stats;
ipset_metrics; nftset_metrics;
$ipset_result $nftset_result
$ipset_matched_set $nftset_matched_set

3. Mettre à jour les formats de journal

# Ancien
log_format security '... ipset_result="$ipset_result" matched_set="$ipset_matched_set"';

# Nouveau
log_format security '... nftset_result="$nftset_result" matched_set="$nftset_matched_set"';

4. Mettre à jour les requêtes Prometheus/Grafana

Ancienne métrique Nouvelle métrique
nginx_ipset_requests_total nginx_nftset_requests_total
nginx_ipset_cache_total nginx_nftset_cache_total
nginx_ipset_cache_entries nginx_nftset_cache_entries
nginx_ipset_autoadd_total nginx_nftset_autoadd_total
nginx_ipset_ratelimit_total nginx_nftset_ratelimit_total
nginx_ipset_challenge_total nginx_nftset_challenge_total
nginx_ipset_uptime_seconds nginx_nftset_uptime_seconds

5. Différences clés

Fonctionnalité ipset-access nftset-access
Backend libipset (ipsets du noyau) libnftables (nftables)
Format d'ensemble setname table:setname
Ensembles CIDR type hash:net drapeau flags interval
Famille Spécifiée dans le type d'ensemble Auto-détectée à partir de l'IP du client
firewalld Backend iptables uniquement Compatible avec le backend nftables

Pourquoi migrer ?

  • Compatibilité RHEL 9/Rocky 9 : firewalld par défaut utilise le backend nftables
  • Support moderne du noyau : nftables est l'avenir du pare-feu Linux
  • Gestion unifiée : Utilisez les commandes nft pour le pare-feu et le contrôle d'accès
  • Meilleur support CIDR : Les ensembles d'intervalles gèrent efficacement les plages réseau

📋 Exigences

  • NGINX ≥ 1.22 (construit avec --with-compat)
  • Noyau Linux avec support nftables
  • Bibliothèque et en-têtes de développement libnftables
  • Capacités : CAP_NET_ADMIN pour les opérations nftables

📜 Licence

Il s'agit d'un logiciel propriétaire. Tous droits réservés.

Disponible exclusivement via le GetPageSpeed Premium Repository.

👤 Auteur

Danila Vershinin GetPageSpeed LLC

🆘 Support

Module d'accès NGINX NFTSet
Un module NGINX premium par GetPageSpeed LLC
www.getpagespeed.com