Saltar a contenido

nftset-access: Bloqueo de IPs con cero latencia usando conjuntos nftables del kernel de Linux

Requiere el plan Pro (o superior) de la suscripción de GetPageSpeed NGINX Extras.

Instalación

Puedes instalar este módulo en cualquier distribución basada en RHEL, incluyendo, pero no limitado a:

  • RedHat Enterprise Linux 7, 8, 9 y 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 y 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

Habilita el módulo añadiendo lo siguiente en la parte superior de /etc/nginx/nginx.conf:

load_module modules/ngx_http_nftset_access.so;

Este documento describe nginx-module-nftset-access v3.0.0 lanzado el 14 de febrero de 2026.


Control de acceso basado en IP de nivel empresarial para NGINX usando conjuntos nftables de Linux. Bloquea amenazas, limita la tasa de abusadores, desafía bots y protege tu infraestructura.

Version GetPageSpeed

⚠️ Software Comercial Este es un módulo premium de código cerrado disponible exclusivamente a través del GetPageSpeed Repository.

Requisito de plan: Requiere el plan Pro de la suscripción de GetPageSpeed NGINX Extras.

✨ Características

Características Principales

Característica Descripción
Lista Blanca/Lista Negra Permitir o denegar según la pertenencia al conjunto nftables
Múltiples conjuntos Comprobar contra múltiples conjuntos nft en una directiva
Actualizaciones en vivo Modificar conjuntos nft sin recargar NGINX
Códigos de estado personalizados Devolver cualquier estado HTTP al bloquear
Soporte CIDR Usar conjuntos de intervalos para rangos de red (por ejemplo, 192.168.1.0/24)

Características de Rendimiento

Característica Descripción
Sesiones por hilo Contextos libnftables locales por hilo eliminan la contención de bloqueos
Cache LRU Caché en memoria compartida con TTL configurable
Tasas de aciertos de caché Típicamente más del 95% de tasa de aciertos reduce llamadas al kernel

Características de Seguridad

Característica Descripción
Limitación de tasa Limitar solicitudes por IP con ventanas configurables
Auto-baneo Añadir automáticamente a la lista negra a los violadores de la limitación de tasa
Desafío JS El desafío de prueba de trabajo detiene bots automatizados
Trampas de Honeypot Auto-bloquear IPs que acceden a URLs trampa
Tiempo de entrada Expirar automáticamente entradas de la lista negra

Características Operativas

Característica Descripción
Modo de prueba Probar configuración sin bloquear
Fail-open/close Controlar el comportamiento en errores de conjuntos nft
Métricas de Prometheus Endpoint nativo /metrics para Grafana
Estadísticas JSON API de estadísticas detalladas
Variables de NGINX $nftset_result y $nftset_matched_set

🚀 Inicio Rápido

1. Crear conjuntos nftables

# Crear una tabla (si no existe)
sudo nft add table ip filter

# Crear una lista negra con soporte de tiempo de espera
sudo nft add set ip filter bad_guys '{ type ipv4_addr; flags timeout; timeout 1d; }'

# Crear una lista de prohibición por limitación de tasa
sudo nft add set ip filter ratelimited '{ type ipv4_addr; flags timeout; timeout 30m; }'

# Crear una lista de trampas de honeypot
sudo nft add set ip filter honeypot '{ type ipv4_addr; flags timeout; timeout 1d; }'

# Crear una lista blanca con soporte 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. Configurar NGINX

load_module modules/ngx_http_nftset_access_module.so;

http {
    server {
        listen 80;

        # Bloquear IPs malas conocidas (formato: tabla:nombre_conjunto)
        nftset_blacklist filter:bad_guys;

        # Limitar tasa: 100 solicitudes por minuto
        nftset_ratelimit rate=100 window=60s autoban=filter:ratelimited;

        # Tu contenido
        location / {
            root /var/www/html;
        }

        # Trampa de honeypot - devuelve 404 por defecto
        location /wp-admin.php {
            nftset_autoadd filter:honeypot timeout=86400;
        }

        # Endpoint de métricas
        location /metrics {
            nftset_metrics;
            allow 127.0.0.1;
            deny all;
        }
    }
}

3. Probar y recargar

sudo nginx -t && sudo nginx -s reload

📦 Instalación

Este módulo está disponible exclusivamente a través del GetPageSpeed Premium Repository.

Paso 1: Suscribirse al GetPageSpeed Repository

Visita GetPageSpeed Repository Subscription para obtener acceso.

Paso 2: Instalar el Repositorio

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

Paso 3: Instalar el Módulo

sudo dnf install nginx-module-nftset-access

Paso 4: Habilitar el Módulo

Añadir a /etc/nginx/nginx.conf antes de cualquier bloque http {}:

load_module modules/ngx_http_nftset_access_module.so;

Paso 5: Recargar NGINX

sudo nginx -t && sudo systemctl reload nginx

📖 Referencia de Configuración

Formato de Especificación de Conjunto

Todas las directivas que hacen referencia a conjuntos nftables utilizan el formato: tabla:nombre_conjunto

  • tabla — El nombre de la tabla nftables (por ejemplo, filter, firewalld)
  • nombre_conjunto — El nombre del conjunto dentro de esa tabla

La familia IP (ip para IPv4, ip6 para IPv6) es detectada automáticamente desde la dirección IP del cliente.

Ejemplos:

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

Control de Acceso

nftset_blacklist tabla:set1 [tabla:set2 ...]

Contexto: http, server Predeterminado:

Bloquea solicitudes si la IP del cliente aparece en cualquiera de los conjuntos nft listados. Se comprueban múltiples conjuntos en orden hasta que se encuentra una coincidencia.

# Conjunto único
nftset_blacklist filter:bad_guys;

# Múltiples conjuntos (lógica OR - bloqueado si está en CUALQUIER conjunto)
nftset_blacklist filter:spammers filter:hackers filter:tor_exits;

# Desactivar
nftset_blacklist off;

nftset_whitelist tabla:set1 [tabla:set2 ...]

Contexto: http, server Predeterminado:

Permite solicitudes solo si la IP del cliente aparece en al menos uno de los conjuntos nft listados. Todas las demás IPs son rechazadas.

# Solo permitir IPs de confianza
nftset_whitelist filter:trusted_partners filter:office_ips;

Importante: Las IPs en la lista blanca eluden todas las restricciones del módulo, incluyendo: - Limitación de tasa (nftset_ratelimit) - Desafíos de JavaScript (nftset_challenge)

Esto es útil para IPs de administradores que no deberían estar sujetas a límites de tasa o desafíos:

# Las IPs de administradores eluden la limitación de tasa y desafíos
nftset_whitelist filter:admin_ips;
nftset_ratelimit rate=100 window=1m autoban=filter:ratelimited ban_time=1800;
nftset_challenge on;

nftset_status código

Contexto: http, server Predeterminado: 403

Código de estado HTTP devuelto cuando se bloquea una solicitud.

nftset_status 403;   # Prohibido (predeterminado)
nftset_status 444;   # Cerrar conexión sin respuesta (especial de NGINX)
nftset_status 429;   # Demasiadas Solicitudes
nftset_status 503;   # Servicio No Disponible

Caché y Rendimiento

nftset_cache_ttl tiempo

Contexto: http, server Predeterminado: 60s

Cuánto tiempo almacenar en caché los resultados de búsqueda de conjuntos nft. Los resultados almacenados en caché evitan llamadas repetidas al kernel para la misma IP.

nftset_cache_ttl 30s;    # 30 segundos
nftset_cache_ttl 5m;     # 5 minutos
nftset_cache_ttl 1h;     # 1 hora

Nota de depuración: Si eliminas una IP de un conjunto nft pero el módulo aún la informa como "coincidente", esto se debe a la caché. El resultado almacenado en caché expirará después del TTL configurado. Para un efecto inmediato durante las pruebas, puedes configurar temporalmente nftset_cache_ttl 0; para deshabilitar la caché (no recomendado para producción debido al impacto en el rendimiento).

Impacto en el rendimiento: - TTL más alto = Mejor rendimiento, pero más lento para reflejar cambios en el conjunto nft - TTL más bajo = Más receptivo a cambios en el conjunto nft, pero más llamadas al kernel - Recomendado: 30s a 5m para la mayoría de los casos de uso

nftset_fail_open on|off

Contexto: http, server Predeterminado: off

Controla el comportamiento cuando falla una búsqueda de conjunto nft (por ejemplo, el conjunto no existe).

nftset_fail_open off;   # Denegar en error (seguro, predeterminado)
nftset_fail_open on;    # Permitir en error (disponible pero arriesgado)

nftset_dryrun on|off

Contexto: http, server Predeterminado: off

Cuando está habilitado, registra lo que se bloquearía pero no bloquea realmente. Perfecto para probar nuevas reglas en producción.

nftset_dryrun on;   # Registrar pero no bloquear

Revisa los registros para mensajes como:

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

Importante: Al usar las variables $nftset_result y $nftset_matched_set en modo dryrun, estos valores reflejan el estado en el momento en que se procesó la solicitud, no el estado actual del conjunto nft. Si revisas el conjunto nft manualmente más tarde y no encuentras la IP, las razones posibles incluyen:

  1. Expiración de tiempo de espera: La IP fue añadida con un tiempo de espera (por ejemplo, timeout 1d) y ha expirado desde entonces.
  2. Retraso de caché: El módulo almacena en caché los resultados de búsqueda (predeterminado 60s). Una entrada eliminada del conjunto nft puede seguir mostrando como "coincidente" hasta que la caché expire.
  3. Eliminación manual: Alguien o algo (fail2ban, scripts) eliminó la entrada.

Este es un comportamiento esperado: el modo dryrun te muestra exactamente lo que vería la producción en el momento de la solicitud.

Limitación de Tasa

nftset_ratelimit parámetros

Contexto: http, server Predeterminado:

Limita las solicitudes por IP dentro de una ventana de tiempo. Puede añadir automáticamente a los violadores a un conjunto nft.

Parámetros:

Parámetro Requerido Descripción
rate=N Máximo de solicitudes por ventana
window=TIME No Ventana de tiempo (predeterminado: 60s)
autoban=TABLE:SET No conjunto nft para añadir violadores
ban_time=N No Segundos hasta auto-expirar (predeterminado: 3600)

Ejemplos:

# Básico: 100 solicitudes por minuto
nftset_ratelimit rate=100;

# Con ventana personalizada: 1000 solicitudes por hora
nftset_ratelimit rate=1000 window=1h;

# Con auto-baneo: Añadir violadores al conjunto nft durante 30 minutos
nftset_ratelimit rate=60 window=1m autoban=filter:ratelimited ban_time=1800;

# Protección estricta de API
nftset_ratelimit rate=10 window=1s autoban=filter:api_abusers ban_time=3600;

Cómo funciona: 1. Cada IP recibe un contador de solicitudes y un tiempo de inicio de ventana. 2. El contador se incrementa en cada solicitud. 3. Cuando la ventana expira, el contador se restablece. 4. Si el contador excede rate, devuelve 429 Too Many Requests. 5. Si autoban está configurado, la IP se añade al conjunto nft especificado.

Nota: El estado de la limitación de tasa se almacena en memoria compartida y sobrevive a los reinicios de trabajadores.

Desafío de JavaScript

nftset_challenge on|off

Contexto: http, server Predeterminado: off

Habilita el modo de desafío de JavaScript. Los navegadores deben resolver un rompecabezas de prueba de trabajo para acceder al sitio. Efectivo contra bots automatizados y raspadores.

nftset_challenge on;

Cómo funciona: 1. La primera solicitud recibe una página de desafío (HTTP 503). 2. El navegador ejecuta JavaScript que resuelve un rompecabezas de hash. 3. La solución se almacena en una cookie (_nftset_verified). 4. Solicitudes subsiguientes con una cookie válida pasan. 5. La cookie expira después de 24 horas.

nftset_challenge_difficulty nivel

Contexto: http, server Predeterminado: 2

Controla la dificultad del desafío (1-8). Más alto = más tiempo para resolver.

Nivel Tiempo Aproximado de Resolución
1 ~100ms
2 ~500ms (predeterminado)
3 ~1 segundo
4 ~2 segundos
5 ~5 segundos
6+ ~10+ segundos
nftset_challenge on;
nftset_challenge_difficulty 3;  # ~1 segundo de tiempo de resolución

Características de la Página de Desafío: - Diseño moderno y responsivo - Animación de carga - Retroalimentación de progreso - Redirección automática en caso de éxito - Sin dependencias externas

Auto-adición de Honeypot

nftset_autoadd tabla:nombre_conjunto [tabla:nombre_conjunto2 ...] [timeout=segundos] [status=código]

Contexto: server, location Predeterminado:

Añade automáticamente la IP del cliente a los conjuntos nft especificados cuando se accede a la ubicación y devuelve un código de estado HTTP. Perfecto para trampas de honeypot.

Parámetros:

Parámetro Requerido Descripción
tabla:nombre_conjunto Conjunto nft objetivo (puede especificar múltiples)
timeout=N No Tiempo de espera de entrada en segundos
status=N No Código de estado HTTP a devolver (predeterminado: 404)

Ejemplos:

# Básico: Añadir al conjunto de honeypot y devolver 404 (predeterminado)
location /config.php {
    nftset_autoadd filter:honeypot;
}

# Con tiempo de espera: Expirar automáticamente después de 24 horas
location /wp-admin.php {
    nftset_autoadd filter:scanners timeout=86400;
}

# Devolver 403 Prohibido en lugar de 404
location /admin.php {
    nftset_autoadd filter:honeypot timeout=86400 status=403;
}

# Añadir a múltiples conjuntos (IPv4 e IPv6)
location /trap {
    nftset_autoadd filter:honeypot4 filter:honeypot6 timeout=86400;
}

Rutas Comunes de Honeypot:

# Trampas de WordPress - devolver 404 para parecer un archivo faltante
location ~ ^/(wp-admin\.php|wp-login\.php|xmlrpc\.php)$ {
    nftset_autoadd filter:honeypot timeout=86400;
}

# Trampas de archivos de configuración - devolver 403 para simular acceso prohibido
location ~ ^/(\.env|config\.php|phpinfo\.php)$ {
    nftset_autoadd filter:honeypot timeout=86400 status=403;
}

# Trampas de shell/exploit - severo, bloquear durante 1 semana
location ~ ^/(shell|cmd|eval|exec)\.php$ {
    nftset_autoadd filter:malicious timeout=604800 status=403;
}

Nota: Cuando se añade automáticamente una IP, el módulo devuelve inmediatamente el código de estado HTTP especificado (predeterminado 404), evitando el procesamiento adicional de la solicitud. El keep-alive de la conexión también se desactiva para evitar más solicitudes en la misma conexión.

Observabilidad

nftset_stats

Contexto: location Predeterminado:

Habilita el endpoint de estadísticas JSON.

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

Consulta JSON Stats API para el formato de respuesta.

nftset_metrics

Contexto: location Predeterminado:

Habilita el endpoint de métricas de Prometheus.

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

Consulta Prometheus Metrics para las métricas disponibles.

📝 Variables de NGINX

El módulo expone dos variables para su uso en registros, encabezados o condicionales.

$nftset_result

La decisión de acceso tomada para esta solicitud.

Valor Descripción
allow Solicitud permitida
deny Solicitud bloqueada
dryrun Sería bloqueada (modo de prueba)
ratelimited Límite de tasa excedido
challenged Página de desafío servida

$nftset_matched_set

Nombre del conjunto nft que coincidió (si lo hay), en formato tabla:nombre_conjunto. Vacío si no hay coincidencia.

Nota: Esta variable refleja el estado de coincidencia en el momento de la solicitud, no el estado actual del conjunto nft. Si revisas el conjunto nft manualmente y no encuentras la IP: - La entrada puede haber expirado (los conjuntos nft admiten tiempos de espera por entrada). - La caché del módulo (predeterminado 60s) puede mostrar una entrada recientemente eliminada como aún coincidente. - Algo puede haber eliminado la entrada después de que se procesó la solicitud.

Ejemplos de Uso

Registro de acceso personalizado:

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;

Añadir encabezados para depuración:

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

Registro condicional:

# Solo registrar solicitudes bloqueadas
map $nftset_result $loggable {
    "deny"  1;
    default 0;
}

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

📊 Métricas de Prometheus

El endpoint /metrics devuelve métricas en formato de exposición de Prometheus.

Métricas Disponibles

# HELP nginx_nftset_requests_total Total de solicitudes procesadas
# 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 Operaciones de caché
# 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 Entradas de caché actuales
# TYPE nginx_nftset_cache_entries gauge
nginx_nftset_cache_entries 5432

# HELP nginx_nftset_autoadd_total Operaciones de auto-adición
# 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 Eventos de limitación de tasa
# 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 Eventos de desafío
# 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 Tiempo de actividad del módulo
# TYPE nginx_nftset_uptime_seconds gauge
nginx_nftset_uptime_seconds 86400

Consultas del Dashboard de Grafana

Tasa de solicitudes por resultado:

rate(nginx_nftset_requests_total[5m])

Tasa de bloqueo:

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

Tasa de aciertos de caché:

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

Disparadores de limitación de tasa por minuto:

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

📈 JSON Stats API

El endpoint /_stats devuelve estadísticas detalladas en formato JSON.

Formato de Respuesta

{
  "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
  }
}

Descripciones de Campos

Campo Descripción
version Versión del módulo
uptime_seconds Segundos desde que se cargó el módulo
requests.checked Total de solicitudes procesadas
requests.allowed Solicitudes que pasaron
requests.blocked Solicitudes que fueron bloqueadas
requests.errors Errores de búsqueda de conjuntos nft
cache.hits Aciertos de caché (evitar llamada al kernel)
cache.misses Fallos de caché (requiere llamada al kernel)
cache.entries Entradas de caché actuales
cache.hit_rate Porcentaje de tasa de aciertos
autoadd.success Adiciones exitosas de honeypot
autoadd.failed Adiciones fallidas de honeypot
ratelimit.triggered Violaciones de limitación de tasa
ratelimit.autobanned IPs auto-agregadas a la lista de prohibición
challenge.issued Páginas de desafío servidas
challenge.passed Desafíos resueltos con éxito
challenge.failed Fallos de desafío

🏗️ Arquitectura

┌─────────────────────────────────────────────────────────────────────┐
│                           FLUJO DE SOLICITUD                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   Solicitud Entrante                                                │
│         │                                                            │
│         ▼                                                            │
│   ┌───────────────┐                                                  │
│   │  Verificar     │──── Excedido? ────▶ 429 + Auto-baneo          │
│   │   Limitación   │                                                  │
│   └───────┬───────┘                                                  │
│           │ OK                                                       │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │   Verificar    │──── ¿Sin cookie? ────▶ Servir rompecabezas JS │
│   │    Desafío     │                                                  │
│   └───────┬───────┘                                                  │
│           │ Pasado                                                  │
│           ▼                                                          │
│   ┌───────────────┐     ┌─────────────┐                             │
│   │  Verificar    │────▶│   ACIERTO   │────▶ Usar resultado en caché│
│   └───────┬───────┘     └─────────────┘                             │
│           │ FALLA                                                  │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │  Consulta nft  │──── Contexto libnftables local por hilo        │
│   │  (kernel)     │                                                  │
│   └───────┬───────┘                                                  │
│           │                                                          │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │ Almacenar en   │                                                  │
│   │    Caché       │                                                  │
│   └───────┬───────┘                                                  │
│           │                                                          │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │    Decisión    │──── ¿Coincidencia en lista negra? ────▶ Bloquear (403/444) │
│   │                │──── ¿Falta en lista blanca? ────▶ Bloquear (403/444) │
│   └───────┬───────┘                                                  │
│           │ Permitir                                               │
│           ▼                                                          │
│   ┌───────────────┐                                                  │
│   │   Verificar    │──── ¿Coincidencia en ubicación? ────▶ Añadir al conjunto nft │
│   │    Honeypot    │                                                  │
│   └───────┬───────┘                                                  │
│           │                                                          │
│           ▼                                                          │
│       Continuar hacia                                              │
│       Manejador de Contenido                                        │
│                                                                      │
├─────────────────────────────────────────────────────────────────────┤
│                        MEMORIA COMPARTIDA                           │
│  ┌────────────────┬─────────────────┬─────────────────────────────┐ │
│  │     Estadísticas │    Caché LRU   │    Cubos de Limitación      │ │
│  │   (contadores)   │  (IP → Resultado)  │   (IP → Contador de Solicitudes) │ │
│  └────────────────┴─────────────────┴─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

Diseño de Memoria

Componente Ubicación Propósito
Contexto libnftables Local por hilo Contexto por trabajador para evitar bloqueos
Caché de búsqueda Memoria compartida Caché LRU de asignaciones IP→resultado
Cubos de limitación de tasa Memoria compartida Contadores de solicitudes por IP
Estadísticas Memoria compartida Contadores atómicos para métricas

📚 Ejemplos

Ejemplo 1: Lista Negra Básica

# Crear tabla nft y conjunto
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;
    }
}

Ejemplo 2: API con Limitación de Tasa

server {
    listen 80;

    # Limitación de tasa estricta para API
    nftset_ratelimit rate=100 window=1m autoban=filter:api_banned ban_time=3600;

    # Solo permitir socios conocidos
    nftset_whitelist filter:api_partners;
    nftset_status 401;

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

Ejemplo 3: Stack de Seguridad Completo

server {
    listen 80 default_server;

    # Capa 1: Amenazas conocidas
    nftset_blacklist filter:malware_ips filter:tor_exits filter:datacenter_ranges;
    nftset_status 444;
    nftset_cache_ttl 5m;

    # Capa 2: Limitación de tasa
    nftset_ratelimit rate=60 window=1m autoban=filter:ratelimited ban_time=1800;

    # Capa 3: Desafío de bot
    nftset_challenge on;
    nftset_challenge_difficulty 2;

    # Contenido real
    location / {
        root /var/www/html;
    }

    # Trampas de honeypot - devolver 404 (predeterminado) para parecer archivos faltantes
    location ~ ^/(wp-admin|phpmyadmin|admin)\.php$ {
        nftset_autoadd filter:honeypot timeout=86400;
    }

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

Ejemplo 4: Pruebas en Modo de Prueba

server {
    listen 80;

    # Probar nuevas reglas sin bloquear
    nftset_blacklist filter:new_threat_list;
    nftset_dryrun on;

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

Revisa los registros:

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

Ejemplo 5: Lista Blanca CIDR (Rangos de Red)

# Crear conjunto de intervalos para rangos 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 }'

# Versión 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;

    # Lista blanca de redes enteras
    nftset_whitelist filter:trusted;

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

🔧 Solución de Problemas

Módulo no cargando

nginx: [emerg] dlopen() failed

Solución: Asegúrate de que NGINX fue construido con --with-compat y que el módulo fue construido contra la misma versión de NGINX.

Conjunto nft no encontrado

nftset: set 'filter:myset' does not exist

Solución: Crea la tabla nft y el conjunto antes de iniciar NGINX:

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

TIP: Lista los conjuntos disponibles con:

nft list sets

Permiso denegado

nftset: kernel error

Solución: El trabajador de NGINX necesita la capacidad CAP_NET_ADMIN:

sudo setcap cap_net_admin+ep /usr/sbin/nginx

Denegaciones de SELinux (RHEL/CentOS/AlmaLinux)

SELinux is preventing /usr/sbin/nginx from using netlink_netfilter_socket

Solución: Instala el módulo de política SELinux incluido:

cd selinux/
sudo ./install.sh

O manualmente:

# Verificar
semodule -l | grep nginx_nftset

La política permite que httpd_t (el dominio SELinux de NGINX) use sockets netlink_netfilter requeridos por libnftables.

Alto uso de memoria

Solución: Reduce el TTL de caché o limita el tamaño de la caché en la configuración de memoria compartida.

La limitación de tasa no funciona

Solución: Asegúrate de que el conjunto nft para auto-baneo exista y tenga soporte de tiempo de espera:

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

El registro muestra "matched=table:setname" pero la IP no está en el conjunto nft

Este es un comportamiento esperado. El módulo informa lo que vio en el momento de la solicitud. Si revisas el conjunto nft más tarde y no encuentras la IP:

  1. Expiración de tiempo de espera: La IP fue añadida con un tiempo de espera y ha expirado desde entonces.

    # Verificar banderas del conjunto
    nft list set ip filter setname
    # Buscar "flags timeout" en la salida
    

  2. Caché del módulo: El módulo almacena en caché las búsquedas (predeterminado 60s). Una IP recientemente eliminada puede seguir apareciendo como "coincidente".

    # Deshabilitar temporalmente la caché para depuración (¡no para producción!)
    nftset_cache_ttl 0;
    

  3. La entrada fue eliminada: fail2ban, scripts o comandos manuales pueden haberla eliminado.

  4. Problema de configuración de trampa: Si usas trampas de honeypot con nftset_autoadd, bots legítimos pueden haber activado trampas. Verifica que tus ubicaciones de trampa no se superpongan con rutas legítimas (como sitemaps, robots.txt). Usa robots.txt para excluir rutas de trampa de la indexación.

autoadd falla con error de tiempo de espera

Esto significa que estás usando timeout=N en nftset_autoadd pero el conjunto nft fue creado sin soporte de tiempo de espera.

Solución: Recrea el conjunto nft con soporte de tiempo de espera:

# Eliminar y recrear con la bandera de tiempo de espera
sudo nft delete set ip filter honeypot
sudo nft add set ip filter honeypot '{ type ipv4_addr; flags timeout; timeout 1d; }'

🔄 Migración desde ipset-access

Si estás migrando desde el antiguo ngx_http_ipset_access_module, sigue estos pasos:

1. Convertir ipsets a conjuntos nft

# Antiguo ipset
ipset create bad_guys hash:ip timeout 86400

# Nuevo conjunto nft equivalente
nft add table ip filter
nft add set ip filter bad_guys '{ type ipv4_addr; flags timeout; timeout 1d; }'

# Para rangos CIDR (hash:net → bandera de intervalo)
# Antiguo: ipset create networks hash:net
# Nuevo:
nft add set ip filter networks '{ type ipv4_addr; flags interval; }'

2. Actualizar la configuración de NGINX

Antiguo (ipset) Nuevo (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. Actualizar formatos de registro

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

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

4. Actualizar consultas de Prometheus/Grafana

Métrica Antigua Nueva Métrica
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. Diferencias clave

Característica ipset-access nftset-access
Backend libipset (ipsets del kernel) libnftables (nftables)
Formato de conjunto setname table:setname
Conjuntos CIDR tipo hash:net bandera flags interval
Familia Especificada en el tipo de conjunto Detectada automáticamente desde la IP del cliente
firewalld solo backend de iptables compatible con backend de nftables

¿Por qué migrar?

  • Compatibilidad con RHEL 9/Rocky 9: firewalld utiliza por defecto el backend de nftables.
  • Soporte para kernels modernos: nftables es el futuro del firewalling en Linux.
  • Gestión unificada: Usa comandos nft tanto para firewall como para control de acceso.
  • Mejor soporte CIDR: Los conjuntos de intervalos manejan rangos de red de manera eficiente.

📋 Requisitos

  • NGINX ≥ 1.22 (construido con --with-compat)
  • Kernel de Linux con soporte para nftables
  • Biblioteca y encabezados de desarrollo de libnftables
  • Capacidades: CAP_NET_ADMIN para operaciones de nftables

📜 Licencia

Este es un software propietario. Todos los derechos reservados.

Disponible exclusivamente a través del GetPageSpeed Premium Repository.

👤 Autor

Danila Vershinin GetPageSpeed LLC

🆘 Soporte

Módulo de Acceso NFTSet de NGINX
Un módulo premium de NGINX de GetPageSpeed LLC
www.getpagespeed.com