En 2026, el ecosistema de filtrado de paquetes en Linux vive una transición que lleva años en marcha pero que muchos equipos siguen aplazando: el paso de iptables a nftables. Distribuciones como Debian 10+, RHEL 8+, Ubuntu 20.04+ y Arch Linux ya usan nftables como backend por defecto. Sin embargo, iptables sigue presente en miles de servidores en producción, scripts de despliegue y documentación que nadie se atreve a tocar.
Esta guía no elige bando: aprenderás ambas herramientas, entenderás cuándo usar cada una y sabrás cómo migrar cuando llegue el momento. Si administras servidores Linux o trabajas en seguridad de red, aquí tienes la referencia práctica que necesitas tener a mano.
Conceptos fundamentales
Tanto iptables como nftables son interfaces de usuario para Netfilter, el framework de filtrado de paquetes integrado en el kernel de Linux. Netfilter opera en varios puntos del recorrido de un paquete (hooks): PREROUTING, INPUT, FORWARD, OUTPUT y POSTROUTING.
Tablas y cadenas
Las reglas se organizan en tablas según su función:
- filter — La tabla por defecto. Contiene las cadenas INPUT, FORWARD y OUTPUT para decidir si un paquete se acepta o se descarta.
- nat — Para traducción de direcciones (PREROUTING, OUTPUT, POSTROUTING).
- mangle — Para modificar cabeceras de paquetes.
- raw — Para control de seguimiento de conexiones (conntrack).
Cada tabla contiene cadenas (chains), que son listas ordenadas de reglas. Cuando un paquete atraviesa una cadena, se evalúan las reglas en orden hasta que una coincide. Si ninguna coincide, se aplica la política por defecto de la cadena.
Una regla tiene dos partes: los criterios de coincidencia (match) y el objetivo (target). Los targets más comunes son ACCEPT, DROP, REJECT, LOG y RETURN.
iptables: Guía de referencia rápida
Limpiar y resetear reglas
El primer paso antes de configurar un firewall limpio es eliminar todas las reglas existentes:
# Eliminar todas las reglas de la tabla filter
iptables --flush
iptables --delete-chain
# Eliminar todas las reglas de NAT y mangle
iptables --table nat --flush
iptables --table mangle --flush
# Resetear contadores de paquetes y bytes
iptables --zeroPolíticas por defecto
La política por defecto determina qué ocurre cuando ninguna regla coincide. La estrategia más segura es denegar todo por defecto y luego permitir solo lo necesario:
# Política restrictiva (recomendada para servidores)
iptables --policy INPUT DROP
iptables --policy FORWARD DROP
iptables --policy OUTPUT ACCEPT
# Política permisiva (útil durante desarrollo)
iptables --policy INPUT ACCEPT
iptables --policy FORWARD ACCEPT
iptables --policy OUTPUT ACCEPTVer las reglas activas
# Listar reglas con números de línea
iptables --list --line-numbers
# Verbose: muestra interfaz, contadores de paquetes/bytes y opciones
iptables --list --verbose --numeric --line-numbers
# Ver solo la tabla nat
iptables --table nat --list --verbose --numeric --line-numbersEliminar reglas
# Eliminar por número de línea (ver con --line-numbers primero)
iptables --delete INPUT 3
# Eliminar por coincidencia exacta con la regla
iptables --delete INPUT --protocol tcp --dport 8080 --jump ACCEPTGuardar y restaurar reglas
Las reglas de iptables no persisten tras un reinicio. Para hacerlas permanentes:
# Guardar reglas actuales
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
# Restaurar reglas desde archivo
iptables-restore < /etc/iptables/rules.v4En sistemas con systemd, instala el paquete iptables-persistent (Debian/Ubuntu) o habilita el servicio iptables (RHEL/CentOS) para que las reglas se carguen automáticamente al arranque.
Reglas habituales
# Permitir loopback (imprescindible)
iptables --append INPUT --in-interface lo --jump ACCEPT
iptables --append OUTPUT --out-interface lo --jump ACCEPT
# Permitir conexiones ya establecidas y relacionadas
iptables --append INPUT --match conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT
# Descartar paquetes con estado INVALID
iptables --append INPUT --match conntrack --ctstate INVALID --jump DROP
# SSH (puerto 22)
iptables --append INPUT --protocol tcp --dport 22 --match conntrack --ctstate NEW,ESTABLISHED --jump ACCEPT
# HTTP y HTTPS
iptables --append INPUT --protocol tcp --match multiport --dports 80,443 --jump ACCEPT
# DNS (si el servidor actúa como resolver)
iptables --append INPUT --protocol udp --dport 53 --jump ACCEPT
iptables --append INPUT --protocol tcp --dport 53 --jump ACCEPT
# ICMP (ping) -- permite diagnóstico de red
iptables --append INPUT --protocol icmp --icmp-type echo-request --jump ACCEPTBloquear tráfico por IP, rango y puerto
# Bloquear una IP concreta
iptables --append INPUT --source 203.0.113.42 --jump DROP
# Bloquear un rango CIDR
iptables --append INPUT --source 198.51.100.0/24 --jump DROP
# Bloquear rango de puertos
iptables --append INPUT --protocol tcp --dport 8000:8100 --jump DROP
# Bloquear una IP de origen en un puerto específico
iptables --append INPUT --source 203.0.113.42 --protocol tcp --dport 80 --jump DROPLimitación de tasa (prevención de DoS)
# Limitar nuevas conexiones SSH a 3 por minuto por IP
iptables --append INPUT --protocol tcp --dport 22 \
--match conntrack --ctstate NEW \
--match recent --set --name SSH --rsource
iptables --append INPUT --protocol tcp --dport 22 \
--match conntrack --ctstate NEW \
--match recent --update --seconds 60 --hitcount 4 --name SSH --rsource \
--jump DROP
# Limitar SYN floods usando hashlimit
iptables --append INPUT --protocol tcp --syn \
--match hashlimit \
--hashlimit-name syn_flood \
--hashlimit-above 200/second \
--hashlimit-burst 1000 \
--hashlimit-mode srcip \
--jump DROPNAT y reenvío de puertos
# Habilitar IP forwarding (obligatorio para NAT)
echo 1 > /proc/sys/net/ipv4/ip_forward
# O de forma permanente en /etc/sysctl.conf:
# net.ipv4.ip_forward = 1
# MASQUERADE -- NAT de salida para una interfaz (p.ej. router con IP dinámica)
iptables --table nat --append POSTROUTING --out-interface eth0 --jump MASQUERADE
# SNAT -- NAT de salida con IP estática (más eficiente que MASQUERADE)
iptables --table nat --append POSTROUTING --source 192.168.1.0/24 \
--out-interface eth0 --jump SNAT --to-source 203.0.113.10
# DNAT -- Reenvío de puerto 80 externo a servidor interno en 8080
iptables --table nat --append PREROUTING \
--in-interface eth0 --protocol tcp --dport 80 \
--jump DNAT --to-destination 192.168.1.100:8080
# Permitir el tráfico redirigido en FORWARD
iptables --append FORWARD --destination 192.168.1.100 --protocol tcp \
--dport 8080 --jump ACCEPTLogging
# Registrar paquetes descartados (insertar ANTES de la regla DROP)
iptables --append INPUT --match limit --limit 5/min --jump LOG \
--log-prefix "iptables-DROP: " --log-level 4
# Registrar intentos de conexión a puertos no permitidos
iptables --append INPUT --protocol tcp --dport 23 --jump LOG \
--log-prefix "TELNET-BLOCKED: " --log-level warning
# Los logs aparecen en /var/log/kern.log o con: journalctl -k | grep iptablesnftables: La evolución del filtrado en Linux
Por qué nftables en 2026
nftables no es simplemente "iptables con mejor sintaxis". Aporta mejoras estructurales importantes:
- Framework unificado: reemplaza iptables, ip6tables, arptables y ebtables con una sola herramienta.
- Operaciones atómicas: puedes cargar un conjunto completo de reglas en una sola operación, sin ventanas de tiempo sin protección.
- Sets y mapas: estructuras de datos nativas para gestionar listas de IPs, puertos o rangos de forma eficiente.
- Mejor rendimiento: el bytecode generado es más eficiente que el modelo de evaluación lineal de iptables.
- Sintaxis legible: las reglas son más compactas y fáciles de auditar.
Sintaxis básica
La jerarquía en nftables es: familia → tabla → cadena → regla. Las familias más comunes son ip (IPv4), ip6 (IPv6), inet (ambos), arp y bridge.
# Crear una tabla
nft add table inet filter
# Crear una cadena con política por defecto DROP
nft add chain inet filter input \
'{ type filter hook input priority 0 ; policy drop ; }'
# Crear la cadena OUTPUT con política ACCEPT
nft add chain inet filter output \
'{ type filter hook output priority 0 ; policy accept ; }'
# Añadir una regla (permite SSH)
nft add rule inet filter input tcp dport 22 ct state new,established accept
# Listar todo el ruleset
nft list ruleset
# Listar una tabla concreta
nft list table inet filterEquivalencias con iptables
Las reglas más comunes tienen su equivalente directo en nftables, con una sintaxis más natural:
# Loopback
nft add rule inet filter input iif lo accept
nft add rule inet filter output oif lo accept
# Conexiones establecidas y relacionadas
nft add rule inet filter input ct state established,related accept
# Descartar paquetes INVALID
nft add rule inet filter input ct state invalid drop
# SSH, HTTP y HTTPS en una sola regla
nft add rule inet filter input tcp dport { 22, 80, 443 } accept
# DNS
nft add rule inet filter input udp dport 53 accept
nft add rule inet filter input tcp dport 53 accept
# ICMP (ping)
nft add rule inet filter input icmp type echo-request accept
nft add rule inet filter input icmpv6 type echo-request accept
# Bloquear una IP
nft add rule inet filter input ip saddr 203.0.113.42 drop
# Bloquear un rango CIDR
nft add rule inet filter input ip saddr 198.51.100.0/24 drop
# Rango de puertos
nft add rule inet filter input tcp dport 8000-8100 dropNAT con nftables
# Crear tabla NAT
nft add table ip nat
# Cadenas de pre y postrouting
nft add chain ip nat prerouting \
'{ type nat hook prerouting priority -100 ; }'
nft add chain ip nat postrouting \
'{ type nat hook postrouting priority 100 ; }'
# MASQUERADE
nft add rule ip nat postrouting oif eth0 masquerade
# SNAT con IP estática
nft add rule ip nat postrouting ip saddr 192.168.1.0/24 oif eth0 \
snat to 203.0.113.10
# DNAT -- reenvío de puerto 80 a servidor interno
nft add rule ip nat prerouting iif eth0 tcp dport 80 \
dnat to 192.168.1.100:8080Limitación de tasa en nftables
# Limitar nuevas conexiones SSH a 3 por minuto por IP de origen
nft add rule inet filter input tcp dport 22 ct state new \
meter ssh_limit { ip saddr limit rate 3/minute } accept
# Descartar lo que supere el límite
nft add rule inet filter input tcp dport 22 ct state new drop
# Limitar ICMP para prevenir ping flood
nft add rule inet filter input icmp type echo-request \
limit rate 10/second burst 20 packets acceptLogging en nftables
# Registrar y luego descartar (dos reglas consecutivas)
nft add rule inet filter input limit rate 5/minute \
log prefix "nft-DROP: " level warn
nft add rule inet filter input drop
# Logging solo de TCP sin establecer en puertos críticos
nft add rule inet filter input tcp dport 23 \
log prefix "TELNET-BLOCKED: " level warn dropSets y mapas: el superpoder de nftables
Los sets son listas de elementos (IPs, puertos, rangos) que se pueden referenciar en múltiples reglas. Son mucho más eficientes que añadir una regla por cada elemento:
# Crear un set de IPs bloqueadas
nft add set inet filter blocklist \
'{ type ipv4_addr ; flags interval ; }'
# Añadir elementos al set
nft add element inet filter blocklist \
{ 203.0.113.0/24, 198.51.100.42, 192.0.2.0/28 }
# Usar el set en una regla
nft add rule inet filter input ip saddr @blocklist drop
# Añadir o eliminar elementos en caliente (sin recargar el firewall)
nft add element inet filter blocklist { 10.0.0.99 }
nft delete element inet filter blocklist { 10.0.0.99 }
# Set con timeout automático (ideal para bloqueos temporales)
nft add set inet filter temp_block \
'{ type ipv4_addr ; flags dynamic,timeout ; timeout 1h ; }'Los mapas (maps) permiten hacer asociaciones clave-valor, útiles para DNAT dinámico o asignación de marcas por IP:
# Mapa de DNAT: puerto de destino -> servidor interno
nft add map ip nat portmap \
'{ type inet_service : ipv4_addr . inet_service ; }'
nft add element ip nat portmap \
{ 80 : 192.168.1.10 . 8080, 443 : 192.168.1.10 . 8443 }
nft add rule ip nat prerouting iif eth0 \
dnat to tcp dport map @portmapEliminar reglas y gestionar el ruleset
# Ver handles (identificadores) de las reglas
nft --handle list chain inet filter input
# Eliminar una regla por su handle
nft delete rule inet filter input handle 7
# Vaciar una cadena
nft flush chain inet filter input
# Eliminar una tabla completa
nft delete table inet filter
# Limpiar todo el ruleset
nft flush rulesetPersistencia en nftables
# Volcar el ruleset completo a un archivo
nft list ruleset > /etc/nftables.conf
# Cargar el ruleset desde archivo (operación atómica)
nft --file /etc/nftables.conf
# Validar sintaxis sin aplicar los cambios
nft --check --file /etc/nftables.conf
# Habilitar el servicio de nftables en systemd
systemctl enable nftables
systemctl start nftablesMigración de iptables a nftables
iptables-translate
La herramienta iptables-translate convierte reglas de iptables al equivalente nftables. Es la forma más rápida de migrar reglas existentes:
# Traducir una regla individual
iptables-translate --append INPUT --protocol tcp --dport 22 --jump ACCEPT
# Salida: nft add rule ip filter INPUT tcp dport 22 counter accept
# Traducir todas las reglas guardadas
iptables-save | iptables-restore-translate > /etc/nftables.conf
# Revisar el archivo generado antes de aplicarlo
nft --check --file /etc/nftables.conf
# Aplicar si todo es correcto
nft --file /etc/nftables.confCapa de compatibilidad iptables-nft
En RHEL 8+ y distribuciones modernas, los comandos iptables, ip6tables, arptables y ebtables son en realidad wrappers que escriben reglas en el backend nftables. Puedes verificarlo:
# Comprobar qué iptables está en uso
update-alternatives --list iptables
# Cambiar entre el legado y la versión nft
update-alternatives --set iptables /usr/sbin/iptables-nft
update-alternatives --set iptables /usr/sbin/iptables-legacy
# Las reglas escritas con iptables-nft son visibles desde nft
iptables-nft --append INPUT --protocol tcp --dport 80 --jump ACCEPT
nft list ruleset # aparecerán en la tabla ip iptablesEjemplo práctico de migración
# Paso 1: Exportar reglas iptables actuales
iptables-save > /tmp/iptables-backup.rules
# Paso 2: Traducir a sintaxis nftables
iptables-restore-translate --file /tmp/iptables-backup.rules \
> /tmp/nftables-migrated.conf
# Paso 3: Revisar y limpiar el archivo generado
# (eliminar reglas redundantes, mejorar con sets, etc.)
# Paso 4: Validar sin aplicar
nft --check --file /tmp/nftables-migrated.conf
# Paso 5: Hacer backup del ruleset nftables actual
nft list ruleset > /etc/nftables.conf.bak
# Paso 6: Aplicar las nuevas reglas
nft flush ruleset
nft --file /tmp/nftables-migrated.conf
# Paso 7: Verificar conectividad y guardar permanentemente
nft list ruleset > /etc/nftables.confEjemplo completo: Firewall de servidor web
El siguiente archivo /etc/nftables.conf implementa un firewall completo para un servidor web con SSH, HTTP/HTTPS, rate limiting y logging. Se carga de forma atómica con nft -f:
#!/usr/sbin/nft -f
# /etc/nftables.conf -- Firewall para servidor web
# Cargar: nft -f /etc/nftables.conf
# Validar: nft --check -f /etc/nftables.conf
flush ruleset
# -------------------------------------------------------
# Tabla principal (inet = IPv4 + IPv6)
# -------------------------------------------------------
table inet filter {
# Set de IPs bloqueadas -- añadir elementos en caliente
set blocklist {
type ipv4_addr
flags interval
# elementos: { 203.0.113.0/24, 198.51.100.42 }
}
# Set de IPs de administración (solo estas pueden entrar por SSH)
set admin_ips {
type ipv4_addr
flags interval
elements = { 192.0.2.10, 10.0.0.0/8 }
}
chain input {
type filter hook input priority 0
policy drop
# Loopback -- siempre permitir
iif lo accept comment "Permitir loopback"
# Conexiones ya establecidas
ct state established,related accept comment "Permitir conexiones establecidas"
# Paquetes inválidos -- descartar antes de procesar
ct state invalid drop comment "Descartar paquetes inválidos"
# Bloquear IPs de la lista negra
ip saddr @blocklist drop comment "Bloquear IPs en blocklist"
# ICMP (ping) con límite de tasa
icmp type echo-request limit rate 10/second burst 20 packets accept
icmpv6 type echo-request limit rate 10/second burst 20 packets accept
# SSH -- solo desde IPs de administración, con rate limiting
ip saddr @admin_ips tcp dport 22 ct state new \
meter ssh_rate { ip saddr limit rate 5/minute } \
accept comment "SSH para administradores"
ip saddr @admin_ips tcp dport 22 ct state new \
log prefix "SSH-RATE-LIMIT: " level warn drop
# HTTP y HTTPS -- acceso público
tcp dport { 80, 443 } ct state new \
meter http_rate { ip saddr limit rate 100/second burst 200 } \
accept comment "HTTP/HTTPS público"
tcp dport { 80, 443 } ct state new \
log prefix "HTTP-RATE-LIMIT: " level warn drop
# Registrar y descartar todo lo demás
limit rate 5/minute log prefix "nft-INPUT-DROP: " level warn
drop
}
chain forward {
type filter hook forward priority 0
policy drop
comment "Servidor independiente: no reenviar paquetes"
}
chain output {
type filter hook output priority 0
policy accept
comment "Tráfico de salida: permitir todo"
}
}Para aplicar este firewall y habilitarlo en el arranque:
# Validar la sintaxis sin aplicar
nft --check --file /etc/nftables.conf
# Aplicar (operación atómica -- si falla, no se aplica nada)
nft --file /etc/nftables.conf
# Verificar que las reglas están activas
nft list ruleset
# Habilitar para el arranque
systemctl enable --now nftablesBuenas prácticas en 2026
Usa nftables para nuevos despliegues
Si estás configurando un servidor desde cero hoy, usa nftables directamente. iptables sigue funcionando, pero su desarrollo está congelado. El mantenimiento activo, las nuevas características y la integración con herramientas modernas (como firewalld o systemd-networkd) están en nftables.
Carga atómica de reglas
Nunca apliques reglas una por una con nft add rule en un entorno de producción. Siempre usa nft --file con un archivo que incluya flush ruleset al inicio. Esto garantiza que la transición entre el estado anterior y el nuevo sea instantánea, sin ventanas de tiempo vulnerables ni reglas inconsistentes.
Usa sets para listas de IPs y puertos
Una regla que referencia un set de 1000 IPs es mucho más eficiente que 1000 reglas individuales. El kernel evalúa los sets con estructuras de datos optimizadas (árboles de intervalos, tablas hash), no con búsqueda lineal. Además, puedes modificar el contenido de un set en caliente sin recargar el firewall:
# Bloquear una IP temporalmente sin recargar el firewall
nft add element inet filter blocklist { 203.0.113.99 }
# Desbloquear
nft delete element inet filter blocklist { 203.0.113.99 }Registra antes de descartar (log before drop)
Añade siempre una regla de logging justo antes de los drops finales, aunque sea con rate limiting para no saturar el log. Sin logging, diagnosticar por qué un servicio no es accesible se convierte en un ejercicio de adivinación. El prefijo del log (log prefix "...") es clave para filtrar eventos en journalctl:
# Filtrar logs del firewall
journalctl -k --grep "nft-INPUT-DROP"
# En tiempo real
journalctl -kf | grep "nft-"Documenta tus reglas con comentarios
nftables soporta comentarios inline con comment "...". Úsalos. Una regla sin comentario que bloquea tráfico sin explicación es un problema de mantenimiento esperando su momento:
tcp dport 8443 accept comment "API interna -- ver ticket INFRA-2847"
ip saddr 192.0.2.0/24 drop comment "Bloqueo ISP abusivo -- revisar en Q3 2026"Valida siempre con --check antes de aplicar
El flag --check (o -c) parsea y valida el archivo de configuración sin aplicar ningún cambio al kernel. Es gratuito y puede salvarte de un lockout por una errata:
nft --check --file /etc/nftables.conf && echo "OK" || echo "ERROR en la configuracion"Gestiona el firewall bajo control de versiones
El archivo /etc/nftables.conf debe estar en un repositorio Git. Cada cambio en el firewall es un commit con mensaje descriptivo. Si trabajas con Ansible, Puppet o Salt, gestiona las reglas como código (Infrastructure as Code). Un cambio de firewall sin auditoría es un riesgo de seguridad y un problema operacional.
Haz un rollback automático al cambiar reglas en remoto
Al modificar reglas en un servidor remoto, programa siempre un rollback automático por si te quedas sin acceso:
# Programar rollback en 5 minutos si no se cancela
nft list ruleset > /tmp/nft-backup.conf
(sleep 300 && nft --file /tmp/nft-backup.conf && \
echo "ROLLBACK ejecutado automaticamente") &
ROLLBACK_PID=$!
# Aplicar nuevas reglas
nft --file /etc/nftables-new.conf
# Si todo funciona, cancelar el rollback
kill $ROLLBACK_PID && echo "Rollback cancelado -- reglas aplicadas correctamente"La distancia entre un firewall bien configurado y un servidor inaccesible es una sola regla mal escrita. La diferencia entre un sysadmin experimentado y uno que aprende a las malas es tener el rollback preparado antes de tocar nada.
:wq!
Comentarios