Hardening con nftables en Linux: Firewall de Producción con Sets, Rate Limiting y Fail2ban

Guía práctica para configurar un firewall nftables endurecido en Linux. Incluye sets dinámicos, verdict maps, rate limiting, geo-bloqueo, integración con Fail2ban, flowtables y migración desde iptables. Configuración lista para producción.

Introducción: Por qué nftables es el futuro (y el presente) del firewall en Linux

Voy a ser directo: si a estas alturas de 2026 todavía estás gestionando servidores con iptables, estás viviendo de prestado. No es una opinión — es la realidad técnica. Desde que Debian 10 adoptó nftables como framework de firewall por defecto allá por 2019, cada distribución relevante ha seguido el mismo camino. RHEL 8+, Ubuntu 20.04+, Fedora, openSUSE — todas dieron el salto. Y en febrero de 2025, openSUSE Tumbleweed incluso migró su sistema de control de acceso mandatorio de AppArmor a SELinux, dejando bastante claro que el ecosistema Linux se mueve decididamente hacia herramientas más modernas.

Pero la realidad en producción es otra cosa.

Miles de servidores siguen ejecutando reglas iptables heredadas, muchas veces sin que nadie entienda del todo qué hacen. Y lo que es peor: muchos administradores que sí conocen nftables lo usan como si fuera iptables con otra sintaxis, sin aprovechar las capacidades que lo hacen realmente superior. He visto esto más veces de las que me gustaría admitir.

Los números del panorama de amenazas tampoco ayudan a la complacencia. El kernel de Linux acumuló 5.530 CVE en 2025 — un 28% más que el año anterior. Los ataques de fuerza bruta contra SSH representan el 89% de los vectores de ataque en servidores Linux, y los webshells suponen casi el 50% del malware detectado. En este contexto, un firewall mal configurado no es solo un riesgo teórico — es una invitación abierta.

Esta guía va más allá de lo básico. Vamos a construir una configuración de firewall completa y lista para producción usando nftables, aprovechando sus características más potentes: sets dinámicos, verdict maps, rate limiting inteligente, geo-bloqueo, integración con Fail2ban, y optimización de rendimiento con flowtables. Si ya conoces los fundamentos, genial — aquí vas a encontrar lo que necesitas para llevar tu firewall al siguiente nivel.

Arquitectura de nftables: Entendiendo el framework desde la base

Antes de escribir una sola regla, vale la pena entender la arquitectura que hace a nftables fundamentalmente superior a iptables. No es simplemente «otra sintaxis» — es un rediseño completo del framework de filtrado de paquetes del kernel.

La diferencia fundamental: bytecode y máquina virtual

En iptables, cada regla se evalúa secuencialmente en el kernel. Con 500 reglas, el kernel recorre potencialmente las 500 para cada paquete. Eso es procesamiento O(n) — y escala horrible.

nftables funciona de forma radicalmente diferente. Las expresiones que forman las reglas de clasificación de paquetes se compilan en espacio de usuario a bytecode y se ejecutan en el kernel mediante una máquina virtual dedicada. Esto permite una flexibilidad enorme y, lo más importante, rendimiento O(1) mediante el uso de sets, maps y concatenaciones. Es como la diferencia entre buscar un nombre en una lista de 10.000 entradas línea por línea, o encontrarlo al instante en un diccionario. No hay comparación.

Tablas, cadenas y reglas: nada es predefinido

Una de las diferencias más importantes con iptables es que nftables no tiene tablas ni cadenas predefinidas. En iptables existían tablas obligatorias (filter, nat, mangle) y cadenas predefinidas (INPUT, OUTPUT, FORWARD) que se registraban incluso si no las usabas — con el impacto en rendimiento que eso conlleva.

En nftables, tú defines exactamente lo que necesitas. Y eso tiene dos ventajas clave:

  • Rendimiento: Solo se procesa lo que explícitamente configuras. Si no necesitas una tabla NAT, simplemente no la creas y no consume recursos.
  • Organización: Puedes estructurar tus reglas de forma lógica, separando por ejemplo el filtrado de tráfico web, SSH y la gestión de bots en cadenas independientes.

Familias de direcciones

nftables soporta múltiples familias de direcciones. La más relevante para firewalls de servidor es inet, que unifica IPv4 e IPv6 en una sola tabla. Esto elimina la duplicación absurda de mantener reglas separadas en iptables e ip6tables:

  • ip — Solo tráfico IPv4
  • ip6 — Solo tráfico IPv6
  • inet — IPv4 e IPv6 unificados (recomendado para la mayoría de casos)
  • arp — Tráfico ARP
  • bridge — Tráfico L2 en bridges
  • netdev — Tráfico en interfaces específicas (útil para mitigación de DDoS)

Instalación y configuración inicial

Instalación por distribución

En la mayoría de distribuciones modernas, nftables ya viene preinstalado. Si por alguna razón no es tu caso:

# Debian/Ubuntu
sudo apt install nftables
sudo systemctl enable --now nftables

# RHEL/CentOS/Fedora/Rocky/Alma
sudo dnf install nftables
sudo systemctl enable --now nftables

# Arch Linux
sudo pacman -S nftables
sudo systemctl enable --now nftables

# Verificar la versión instalada
nft --version

Importante: Si estás migrando desde iptables, asegúrate de desactivar el servicio anterior antes de habilitar nftables. Ejecutar ambos frameworks simultáneamente es una receta para problemas que luego cuesta mucho diagnosticar:

# Desactivar iptables antes de activar nftables
sudo systemctl disable --now iptables
sudo systemctl disable --now ip6tables

# Si usas firewalld, configurarlo para usar nftables como backend
sudo sed -i 's/FirewallBackend=.*/FirewallBackend=nftables/' /etc/firewalld/firewalld.conf

Estructura del archivo de configuración

El archivo principal de configuración es /etc/nftables.conf. Se carga automáticamente al iniciar el servicio nftables.service. Para configuraciones complejas, es buena práctica dividir todo en archivos separados (y honestamente, cualquier configuración de producción debería considerarse compleja):

/etc/nftables.conf              # Archivo principal
/etc/nftables/                   # Directorio para configuraciones modulares
├── 01-base.nft                  # Tablas y cadenas base
├── 02-sets.nft                  # Sets dinámicos (whitelists, blacklists)
├── 03-rate-limiting.nft         # Reglas de rate limiting
├── 04-services.nft              # Reglas por servicio (SSH, HTTP, etc.)
└── 05-fail2ban.nft              # Tabla dedicada para Fail2ban

El archivo principal quedaría así de simple:

#!/usr/sbin/nft -f

# Limpiar el conjunto de reglas existente
flush ruleset

# Incluir configuraciones modulares
include "/etc/nftables/*.nft"

Configuración de firewall endurecido para producción

Ahora sí, vamos al grano. Vamos a construir una configuración de firewall completa para un servidor de producción. El principio fundamental es deny-all por defecto: todo se bloquea salvo lo explícitamente permitido.

Configuración base con política deny-all

#!/usr/sbin/nft -f
# /etc/nftables/01-base.nft
# Configuración base del firewall - Política deny-all

table inet firewall {

    # ─── Cadena de entrada ───
    chain input {
        type filter hook input priority filter; policy drop;

        # Permitir tráfico de loopback (esencial para servicios locales)
        iif "lo" accept

        # Permitir conexiones ya establecidas y relacionadas
        ct state established,related accept

        # Descartar paquetes con estado inválido
        ct state invalid counter drop

        # ICMP/ICMPv6 — permitir con rate limiting
        icmp type { echo-request, destination-unreachable, time-exceeded } \
            limit rate 10/second burst 20 packets accept
        icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, \
            nd-router-solicit, nd-router-advert } \
            limit rate 10/second burst 20 packets accept

        # Saltar a cadenas específicas por protocolo
        ip protocol tcp jump tcp_chain
        ip protocol udp jump udp_chain

        # Registrar y descartar todo lo demás
        counter log prefix "[nftables-drop-input] " drop
    }

    # ─── Cadena de reenvío ───
    chain forward {
        type filter hook forward priority filter; policy drop;

        # Si no es un router, todo el tráfico de forward se descarta
        counter log prefix "[nftables-drop-forward] " drop
    }

    # ─── Cadena de salida ───
    chain output {
        type filter hook output priority filter; policy accept;

        # Permitir tráfico de loopback
        oif "lo" accept

        # Permitir conexiones establecidas
        ct state established,related accept
    }

    # ─── Cadena TCP ───
    chain tcp_chain {
        # SSH (puerto 22) - con rate limiting
        tcp dport 22 ct state new limit rate 4/minute burst 8 packets accept

        # HTTP/HTTPS
        tcp dport { 80, 443 } ct state new accept

        # Rechazar explícitamente conexiones TCP no permitidas
        # (RST es más limpio que drop para TCP — evita timeouts en el cliente)
        reject with tcp reset
    }

    # ─── Cadena UDP ───
    chain udp_chain {
        # DNS (solo si el servidor lo necesita)
        # udp dport 53 accept

        # NTP
        udp dport 123 accept

        # WireGuard VPN (descomentar si aplica)
        # udp dport 51820 accept

        # Rechazar explícitamente tráfico UDP no permitido
        reject with icmpx type port-unreachable
    }
}

Algunos puntos que vale la pena destacar sobre esta configuración:

  • Usamos la familia inet para manejar IPv4 e IPv6 con un solo conjunto de reglas. Menos duplicación, menos errores.
  • El rate limiting en SSH (4 conexiones nuevas por minuto con burst de 8) previene ataques de fuerza bruta a nivel de firewall, antes de que lleguen a OpenSSH o Fail2ban.
  • Para TCP, usamos reject with tcp reset en lugar de drop. ¿Por qué? Porque drop silencioso causa timeouts en el cliente y puede revelar que hay un firewall filtrando (un atacante experimentado nota la diferencia entre un puerto cerrado y uno filtrado). El reset simula un puerto cerrado normal.
  • Los paquetes con estado invalid se descartan explícitamente — esto bloquea intentos de evasión basados en paquetes malformados o fuera de secuencia.

Sets y maps: Rendimiento O(1) para listas de control de acceso

Los sets y maps son, en mi opinión, la característica más potente de nftables y la que más lo diferencia de iptables. Permiten agrupar direcciones IP, puertos o cualquier selector soportado en estructuras de datos optimizadas (hashtables o árboles rojo-negro) que el kernel consulta en tiempo constante O(1).

Sets con nombre: Whitelists y blacklists dinámicas

# /etc/nftables/02-sets.nft
# Sets dinámicos para control de acceso

table inet firewall {

    # ─── Whitelist de IPs de administración ───
    set admin_ips {
        type ipv4_addr
        flags interval
        comment "IPs autorizadas para administración"
        elements = {
            10.0.1.0/24,          # Red de gestión
            192.168.100.50,       # Estación de trabajo del admin
            203.0.113.10          # IP VPN corporativa
        }
    }

    # ─── Blacklist dinámica con expiración automática ───
    set blacklist {
        type ipv4_addr
        flags dynamic,timeout
        timeout 24h
        size 65536
        comment "IPs bloqueadas automáticamente"
    }

    # ─── Set de puertos de servicios internos ───
    set internal_services {
        type inet_service
        elements = { 5432, 6379, 9090 }
        comment "PostgreSQL, Redis, Prometheus — solo acceso interno"
    }

    # ─── Blacklist IPv6 ───
    set blacklist_v6 {
        type ipv6_addr
        flags dynamic,timeout
        timeout 24h
        size 65536
    }
}

Ahora usamos estos sets en las reglas:

table inet firewall {
    chain input {
        # Bloquear IPs en la blacklist (evaluar primero para máxima eficiencia)
        ip saddr @blacklist counter drop
        ip6 saddr @blacklist_v6 counter drop

        # Permitir acceso SSH solo desde IPs de administración
        tcp dport 22 ip saddr @admin_ips ct state new accept

        # Bloquear acceso a servicios internos desde fuera de la red local
        tcp dport @internal_services ip saddr != 10.0.0.0/8 counter drop
    }
}

Y la gestión dinámica de estos sets es trivial, que es lo bonito del asunto:

# Añadir una IP a la blacklist (se eliminará automáticamente en 24h)
sudo nft add element inet firewall blacklist { 198.51.100.42 }

# Añadir con timeout personalizado
sudo nft add element inet firewall blacklist { 198.51.100.43 timeout 1h }

# Eliminar una IP de la blacklist
sudo nft delete element inet firewall blacklist { 198.51.100.42 }

# Añadir una IP a la whitelist de administración
sudo nft add element inet firewall admin_ips { 203.0.113.20 }

# Listar el contenido de un set
sudo nft list set inet firewall blacklist

Verdict maps: Decisiones inteligentes con O(1)

Los verdict maps (vmaps) llevan los sets un paso más allá — permiten mapear un valor (como una dirección IP o un puerto) directamente a una acción (accept, drop, jump). Esto es extraordinariamente eficiente para enrutar tráfico a cadenas especializadas:

table inet firewall {

    # Verdict map que enruta tráfico por puerto destino
    map port_dispatch {
        type inet_service : verdict
        elements = {
            22   : jump ssh_chain,
            80   : jump http_chain,
            443  : jump http_chain,
            53   : jump dns_chain
        }
    }

    chain input {
        # Despachar tráfico TCP según el puerto destino
        tcp dport vmap @port_dispatch

        # Todo lo que no matchea el vmap se maneja aquí
        counter reject with tcp reset
    }

    chain ssh_chain {
        # Reglas específicas de SSH
        ip saddr @admin_ips accept
        limit rate 2/minute burst 5 packets accept
        counter drop
    }

    chain http_chain {
        # Reglas específicas de HTTP/HTTPS
        ct state new accept
    }

    chain dns_chain {
        # Solo permitir DNS desde redes internas
        ip saddr 10.0.0.0/8 accept
        counter drop
    }
}

Con este enfoque, el kernel no necesita recorrer reglas secuencialmente para saber qué hacer con cada paquete TCP — simplemente consulta el verdict map y salta directamente a la cadena correspondiente. En un servidor con muchos servicios, la diferencia de rendimiento es notable.

Rate limiting avanzado: Protección contra fuerza bruta y DDoS

nftables ofrece mecanismos de rate limiting bastante más sofisticados que iptables. Podemos limitar tanto por tasa de paquetes como por tasa de bytes, y combinar esto con sets dinámicos para crear sistemas de auto-bloqueo. Aquí es donde las cosas se ponen interesantes.

Protección anti fuerza bruta con denylist automática

table inet firewall {

    # Set dinámico para IPs que exceden los límites de SSH
    set ssh_abusers {
        type ipv4_addr
        flags dynamic,timeout
        timeout 30m
        size 65536
    }

    chain ssh_chain {
        # Si la IP ya está en la lista de abusadores, descartar inmediatamente
        ip saddr @ssh_abusers counter drop

        # Limitar a 2 conexiones simultáneas por IP
        meter ssh_connlimit { ip saddr ct count over 2 } \
            counter reject with tcp reset

        # Limitar a 4 conexiones nuevas por minuto por IP
        # Si se excede, añadir a la denylist automáticamente
        tcp flags syn meter ssh_ratelimit { ip saddr limit rate over 4/minute } \
            add @ssh_abusers { ip saddr } \
            counter drop

        # Conexiones que pasan ambos filtros
        accept
    }
}

Este enfoque me parece muy elegante: las IPs que realizan más de 4 intentos de conexión SSH por minuto se añaden automáticamente al set ssh_abusers durante 30 minutos. Durante ese periodo, todo su tráfico SSH se descarta inmediatamente, sin siquiera evaluar más reglas.

Y lo mejor — no necesitas intervención manual ni servicios externos. El firewall se defiende solo.

Protección contra SYN flood

table inet firewall {
    chain input {
        # Limitar tasa global de paquetes SYN (prevención de SYN flood)
        tcp flags syn limit rate 500/second burst 1000 packets accept

        # Si se supera la tasa global, registrar y descartar
        tcp flags syn counter log prefix "[nftables-synflood] " drop
    }
}

Rate limiting por servicio HTTP/HTTPS

table inet firewall {

    set http_flood {
        type ipv4_addr
        flags dynamic,timeout
        timeout 5m
    }

    chain http_chain {
        # Bloquear IPs que excedieron el límite
        ip saddr @http_flood counter drop

        # Limitar conexiones nuevas HTTP/HTTPS a 60/minuto por IP
        ct state new meter http_limit { ip saddr limit rate over 60/minute } \
            add @http_flood { ip saddr } \
            counter drop

        accept
    }
}

Geo-bloqueo: Restringir acceso por país

El geo-bloqueo permite restringir el acceso a tu servidor basándose en la ubicación geográfica de la IP de origen. Seamos honestos: no es una solución de seguridad definitiva (las VPN existen y cualquiera puede usarlas), pero reduce significativamente el ruido de bots y ataques automatizados que provienen de regiones donde no tienes usuarios legítimos. Y menos ruido significa logs más útiles y menos carga innecesaria.

Implementación con nft-geo-filter

La herramienta nft-geo-filter descarga los bloques de IPs asignados a países específicos y los configura como sets en nftables:

# Instalar nft-geo-filter desde el repositorio
git clone https://github.com/rpthms/nft-geo-filter.git
cd nft-geo-filter

# Bloquear tráfico proveniente de países específicos
# Reemplazar eth0 con tu interfaz de red pública
sudo ./nft-geo-filter -i eth0 -f 4 CN RU KP

# Para IPv6 también
sudo ./nft-geo-filter -i eth0 -f 6 CN RU KP

Un detalle que me gusta de esta herramienta: crea una tabla nftables separada, lo que significa que no interfiere con tu configuración de firewall existente. Limpio y seguro.

Geo-bloqueo selectivo por servicio

Un enfoque más refinado (y personalmente el que prefiero) es aplicar geo-bloqueo solo a servicios específicos. Por ejemplo, permitir tráfico HTTP/HTTPS global pero restringir SSH a un país o región:

table inet firewall {

    # Set de rangos IP de tu país (ejemplo: España)
    set country_allowed {
        type ipv4_addr
        flags interval
        # Este set se poblaría con un script que descarga los rangos CIDR
        # del país correspondiente desde bases de datos como db-ip.com
    }

    chain ssh_chain {
        # SSH solo permitido desde IPs del país autorizado
        ip saddr @country_allowed accept

        # O desde la whitelist de administración
        ip saddr @admin_ips accept

        counter drop
    }
}

Para mantener los datos actualizados, configura un cron job que actualice los sets periódicamente:

# /etc/cron.weekly/update-geoip
#!/bin/bash
# Actualizar bloques de IP geográficos semanalmente
cd /opt/nft-geo-filter
./nft-geo-filter -i eth0 -f 4 CN RU KP 2>&1 | logger -t nft-geoip
./nft-geo-filter -i eth0 -f 6 CN RU KP 2>&1 | logger -t nft-geoip

Integración con Fail2ban: Bloqueo automatizado de atacantes

Fail2ban es probablemente la herramienta más popular para bloquear atacantes automáticamente basándose en patrones de logs. Su integración con nftables es nativa y bien soportada, pero requiere configuración específica para que funcione correctamente. No es difícil, pero hay pasos que no te puedes saltar.

Configurar la tabla nftables dedicada para Fail2ban

La mejor práctica es crear una tabla nftables separada para Fail2ban, con una prioridad que asegure que sus reglas se evalúan de forma adecuada respecto al firewall principal:

# /etc/nftables/05-fail2ban.nft
#!/usr/sbin/nft -f

table ip fail2ban {
    chain input {
        # Prioridad 100 = se evalúa después del firewall principal
        type filter hook input priority 100;
    }
}

Incluir esta configuración en el archivo principal de nftables:

# Añadir en /etc/nftables.conf
include "/etc/nftables/05-fail2ban.nft"

Configurar Fail2ban para usar nftables

Editar el archivo /etc/fail2ban/jail.local para usar las acciones de nftables:

# /etc/fail2ban/jail.local

[DEFAULT]
# Usar nftables como acción de ban
banaction = nftables-multiport
chain = input

# Tiempo de ban por defecto: 1 hora
bantime = 3600

# Ventana de detección: 10 minutos
findtime = 600

# Intentos máximos antes del ban
maxretry = 5

[sshd]
enabled = true
port = ssh
backend = systemd
maxretry = 3
bantime = 3600

[recidive]
# Jail para reincidentes — ban más largo para IPs que repiten
enabled = true
logpath = /var/log/fail2ban.log
backend = auto
maxretry = 2
banaction = nftables-allports
bantime = 604800
findtime = 86400

Configurar la acción nftables-common

# /etc/fail2ban/action.d/nftables-common.local

[Init]
# Familia de nftables a usar
nftables_family = ip

# Tabla donde Fail2ban insertará las reglas de ban
nftables_table = fail2ban

# Acción para IPs baneadas (drop es más seguro que reject)
blocktype = drop

# Eliminar prefijo de nombre de sets (limitados a 15 caracteres)
nftables_set_prefix =

Vincular Fail2ban al servicio nftables con systemd

Este paso es crucial y se omite con una frecuencia alarmante. Si nftables se reinicia (por ejemplo, al actualizar reglas), la tabla de Fail2ban se limpia y los bans se pierden. Así, sin más. La solución es vincular ambos servicios mediante systemd:

# Crear override de systemd para fail2ban
sudo systemctl edit fail2ban

Añadir el siguiente contenido:

[Unit]
Requires=nftables.service
PartOf=nftables.service

[Install]
WantedBy=multi-user.target nftables.service
# Recargar configuración de systemd
sudo systemctl daemon-reload

# Reiniciar ambos servicios
sudo systemctl restart nftables
sudo systemctl restart fail2ban

# Verificar que Fail2ban está operativo
sudo fail2ban-client status
sudo fail2ban-client status sshd

Con esta configuración, si nftables se reinicia, Fail2ban también se reiniciará automáticamente, re-creando su tabla y re-aplicando los bans activos. Un detalle pequeño que te ahorra dolores de cabeza enormes.

Flowtables: Optimización de rendimiento para alto tráfico

Si tu servidor maneja tráfico de reenvío (actúa como router, balanceador de carga o gateway VPN), los flowtables pueden mejorar drásticamente el rendimiento. Son una de las características más infrautilizadas de nftables, y sinceramente creo que se debe a que poca gente sabe que existen.

¿Cómo funcionan?

Los flowtables crean un fast path para paquetes de conexiones establecidas, permitiendo que estos eviten completamente los hooks de Netfilter (Prerouting, Forward, Postrouting). En lugar de recorrer todas las reglas del firewall para cada paquete, los paquetes en el fast path se procesan directamente, con solo actualización de TTL y dirección MAC de destino.

Las pruebas de rendimiento muestran mejoras significativas: hasta un 59% de incremento en throughput con firewalld y flowtables habilitados, y una reducción de latencia del 7.5% en escenarios típicos de forwarding. No está nada mal.

Configuración de flowtables

table inet firewall {

    # Definir el flowtable con las interfaces de alto tráfico
    flowtable ft {
        hook ingress priority filter;
        devices = { eth0, eth1 };
    }

    chain forward {
        type filter hook forward priority filter; policy drop;

        # Conexiones establecidas van al fast path
        ct state established flow add @ft counter accept

        # Nuevas conexiones se evalúan normalmente
        ct state new accept

        ct state related accept

        counter drop
    }
}

Advertencia importante: Los paquetes en el fast path del flowtable no son procesados por las reglas de las cadenas prerouting, forward y postrouting normales. Esto significa que si tienes reglas de logging, rate limiting o manipulación de paquetes en esas cadenas, no se aplicarán al tráfico del fast path. Tenlo muy en cuenta al planificar tu configuración.

Migración desde iptables: Guía práctica

Si administras servidores con reglas iptables heredadas, la migración a nftables es inevitable — mejor hacerla planificada que forzada. Afortunadamente, existen herramientas que hacen el proceso bastante manejable.

Paso 1: Auditar las reglas actuales

# Exportar todas las reglas iptables actuales
sudo iptables-save > /root/iptables-backup-$(date +%Y%m%d).rules
sudo ip6tables-save > /root/ip6tables-backup-$(date +%Y%m%d).rules

# Contar las reglas para dimensionar la migración
sudo iptables -L -n --line-numbers | wc -l

Paso 2: Traducir reglas con iptables-translate

# Traducir reglas individuales
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# Resultado: nft add rule ip filter INPUT tcp dport 22 counter accept

# Traducir un archivo completo de reglas
iptables-restore-translate -f /root/iptables-backup.rules > /root/nftables-migrated.nft

Paso 3: Revisar y optimizar

La traducción automática funciona para la mayoría de reglas, pero — y esto es importante — no aprovecharás las ventajas de nftables si simplemente traduces una por una. Este es el momento de:

  • Reemplazar listas de IPs repetidas con sets con nombre
  • Consolidar reglas duplicadas para IPv4/IPv6 usando la familia inet
  • Sustituir cadenas lineales largas con verdict maps
  • Eliminar reglas redundantes que solo existían por limitaciones de iptables

Paso 4: Prueba paralela

# Cargar las nuevas reglas nftables sin desactivar iptables
sudo nft -f /root/nftables-migrated.nft

# Listar el conjunto completo de reglas cargadas
sudo nft list ruleset

# Verificar que los servicios críticos siguen accesibles
# (hacer estas pruebas desde OTRA máquina)
ssh usuario@servidor
curl -I https://servidor

# Si todo funciona, desactivar iptables y habilitar nftables
sudo systemctl disable --now iptables
sudo systemctl enable --now nftables

Monitorización y depuración del firewall

Un firewall que no se monitoriza es un firewall que no se mantiene. Suena a frase de póster motivacional, pero es verdad. nftables ofrece varias herramientas para entender qué está pasando con tu tráfico.

Contadores y logging

# Ver contadores de todas las reglas
sudo nft list ruleset -a

# Ver contadores de una tabla específica
sudo nft list table inet firewall

# Resetear contadores
sudo nft reset counters table inet firewall

# Ver el contenido de un set dinámico (ej: IPs bloqueadas)
sudo nft list set inet firewall blacklist
sudo nft list set inet firewall ssh_abusers

Tracing de paquetes en tiempo real

nftables incluye un mecanismo de tracing que te permite seguir el camino exacto de un paquete a través de todas las reglas. Es increíblemente útil para depuración:

# Habilitar tracing para paquetes desde una IP específica
sudo nft add rule inet firewall input ip saddr 198.51.100.1 meta nftrace set 1

# Monitorizar el trace en tiempo real
sudo nft monitor trace

# Recuerda eliminar la regla de tracing cuando termines
sudo nft -a list chain inet firewall input
# Anotar el handle de la regla de tracing
sudo nft delete rule inet firewall input handle <numero>

Verificar la configuración antes de aplicar

# Validar la sintaxis sin aplicar cambios
sudo nft -c -f /etc/nftables.conf

# Si la validación pasa sin errores, aplicar
sudo nft -f /etc/nftables.conf

# Hacer la configuración persistente
sudo nft list ruleset > /etc/nftables.conf

Ejemplo completo: Configuración de producción integrada

Para cerrar, aquí va una configuración completa que integra todos los conceptos que hemos cubierto. Esta configuración es adecuada para un servidor web con SSH, HTTP/HTTPS y protección activa contra ataques:

#!/usr/sbin/nft -f
# Configuración completa de firewall nftables para producción
# Servidor: Web + SSH | Última actualización: 2026

flush ruleset

table inet firewall {

    # ═══ SETS ═══

    set admin_ips {
        type ipv4_addr
        flags interval
        elements = { 10.0.1.0/24, 192.168.100.50 }
    }

    set blacklist {
        type ipv4_addr
        flags dynamic,timeout
        timeout 24h
        size 65536
    }

    set ssh_abusers {
        type ipv4_addr
        flags dynamic,timeout
        timeout 30m
        size 65536
    }

    set http_flood {
        type ipv4_addr
        flags dynamic,timeout
        timeout 5m
        size 65536
    }

    # ═══ VERDICT MAP ═══

    map port_dispatch {
        type inet_service : verdict
        elements = {
            22  : jump ssh_chain,
            80  : jump http_chain,
            443 : jump http_chain
        }
    }

    # ═══ CADENAS PRINCIPALES ═══

    chain input {
        type filter hook input priority filter; policy drop;

        # Loopback
        iif "lo" accept

        # Conexiones establecidas y relacionadas
        ct state established,related accept
        ct state invalid counter drop

        # Blacklist global (evaluar primero)
        ip saddr @blacklist counter drop

        # ICMP con rate limiting
        icmp type { echo-request, destination-unreachable, time-exceeded } \
            limit rate 10/second burst 20 packets accept
        icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, \
            nd-router-solicit, nd-router-advert } \
            limit rate 10/second burst 20 packets accept

        # Protección global contra SYN flood
        tcp flags syn limit rate 500/second burst 1000 packets accept
        tcp flags syn counter log prefix "[synflood] " drop

        # Despachar tráfico TCP por puerto
        tcp dport vmap @port_dispatch

        # Log y descarte de todo lo demás
        counter log prefix "[nft-drop] " drop
    }

    chain forward {
        type filter hook forward priority filter; policy drop;
    }

    chain output {
        type filter hook output priority filter; policy accept;
    }

    # ═══ CADENAS DE SERVICIO ═══

    chain ssh_chain {
        ip saddr @ssh_abusers counter drop

        # Acceso desde IPs de administración sin restricciones
        ip saddr @admin_ips accept

        # Rate limit para el resto: 4/minuto, bloqueo automático si excede
        meter ssh_ratelimit { ip saddr limit rate over 4/minute } \
            add @ssh_abusers { ip saddr } counter drop

        # Máximo 2 conexiones simultáneas por IP
        meter ssh_connlimit { ip saddr ct count over 2 } \
            counter reject with tcp reset

        ct state new accept
    }

    chain http_chain {
        ip saddr @http_flood counter drop

        # Limitar conexiones nuevas HTTP a 100/minuto por IP
        ct state new meter http_limit { ip saddr limit rate over 100/minute } \
            add @http_flood { ip saddr } counter drop

        ct state new accept
    }
}

# ═══ TABLA PARA FAIL2BAN ═══

table ip fail2ban {
    chain input {
        type filter hook input priority 100;
    }
}

Preguntas frecuentes

¿Puedo usar nftables y iptables al mismo tiempo?

Técnicamente es posible, pero no se recomienda en absoluto. Las interacciones entre ambos frameworks son impredecibles y pueden crear agujeros de seguridad o bloquear tráfico legítimo sin que te des cuenta. La recomendación oficial es ejecutar un solo framework a la vez. Si necesitas compatibilidad durante la migración, usa las herramientas iptables-translate e iptables-restore-translate para convertir tus reglas.

¿nftables reemplaza a firewalld?

No — son complementarios. firewalld es un frontend de gestión de firewall que puede usar nftables como backend (y de hecho, es el backend por defecto en las versiones modernas). Si prefieres gestionar reglas directamente con el comando nft, no necesitas firewalld. Pero si tu equipo está acostumbrado a firewalld o usas herramientas que dependen de él (como Cockpit), puedes seguir usándolo con nftables por debajo. Lo importante es no mezclar gestión manual con nft y gestión mediante firewalld al mismo tiempo — eso sí es pedir problemas.

¿Cómo hago que las reglas de nftables sobrevivan a un reinicio?

Las reglas cargadas con el comando nft son temporales — se pierden al reiniciar. Para hacerlas persistentes, guárdalas en /etc/nftables.conf y asegúrate de que el servicio nftables.service esté habilitado. El servicio carga automáticamente este archivo al arrancar. Puedes exportar tu configuración actual ejecutando nft list ruleset > /etc/nftables.conf.

¿Es nftables un firewall de capa 7 (aplicación)?

No. nftables opera en las capas 3 y 4 del modelo OSI (red y transporte). No puede inspeccionar contenido HTTP, filtrar por URL ni analizar tráfico cifrado SSL/TLS. Para filtrado a nivel de aplicación, necesitas complementarlo con un proxy inverso (como Nginx o HAProxy), un WAF (ModSecurity, Coraza) o un sistema IDS/IPS (Suricata, Snort). nftables se encarga de la primera línea de defensa; esas otras herramientas protegen la capa de aplicación.

¿Cuál es el impacto en rendimiento de nftables frente a iptables?

En pruebas de rendimiento de Red Hat, nftables escala significativamente mejor conforme aumenta el número de reglas. Con pocos cientos de reglas, ambos frameworks tienen rendimiento comparable. Pero con miles de reglas y uso de sets, nftables logra tiempo constante O(1) de búsqueda frente al O(n) lineal de iptables. Además, los flowtables pueden mejorar el throughput de tráfico reenviado hasta un 59%. Para la mayoría de servidores en producción, la diferencia es medible y significativa.

Sobre el Autor Editorial Team

Our team of expert writers and editors.