--- title: "Cómo configurar un firewall con iptables en Ubuntu 24.04" description: "Configura un firewall con iptables en Ubuntu 24.04: reglas para SSH y web, límite contra fuerza bruta, registro, IPv6 y reglas persistentes sin bloquearte." category: "Configuración de Red" language: es source: https://cubepath.com/es/docs/configuración-de-red/configuracion-de-iptables-guia-completa updated: 2026-09-25 --- # Cómo configurar un firewall con iptables en Ubuntu 24.04 iptables es la herramienta clásica para definir las reglas del firewall del kernel Linux (Netfilter). Aunque en Ubuntu 24.04 funciona internamente sobre nftables (backend `iptables-nft`), su sintaxis sigue siendo la más documentada y la que usan muchos scripts y herramientas. En este tutorial crearás un firewall de entrada para un servidor web con SSH, lo aplicarás de forma segura con reversión automática, añadirás IPv6 y harás las reglas persistentes tras reiniciar. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Acceso a la consola del servidor (VNC o consola web del proveedor) por si una regla te deja sin SSH. - Saber qué puertos debe ofrecer el servidor. En este ejemplo: SSH (22), HTTP (80) y HTTPS (443). > Importante: usa un único gestor de firewall. Si vas a gestionar iptables directamente, desactiva UFW; si prefieres algo más sencillo, UFW genera estas mismas reglas por ti. Docker también inserta sus propias reglas en iptables: si el servidor ejecuta Docker, no borres las cadenas `DOCKER*` ni hagas `iptables -F` a ciegas. ## Paso 1: Preparar el sistema Comprueba la versión y el backend de iptables: ```bash sudo iptables -V ``` ```output iptables v1.8.10 (nf_tables) ``` `(nf_tables)` confirma que las reglas se guardan en nftables, algo transparente para ti mientras uses solo comandos `iptables`. Si UFW está activo, desactívalo para que no compita con tus reglas: ```bash sudo ufw status sudo ufw disable ``` Instala `iptables-persistent`, que carga las reglas guardadas en cada arranque. Durante la instalación preguntará si quieres guardar las reglas actuales; responde **Sí** para IPv4 e IPv6, así se crean los archivos de reglas: ```bash sudo apt update sudo apt install iptables-persistent ``` Comprueba que existen los archivos: ```bash ls /etc/iptables/ ``` ```output rules.v4 rules.v6 ``` ## Paso 2: Conocer los comandos básicos Las reglas se organizan en **tablas** (`filter` para filtrar, `nat` para traducir direcciones) y **cadenas**. En la tabla `filter` las cadenas principales son `INPUT` (tráfico hacia el servidor), `OUTPUT` (tráfico que sale de él) y `FORWARD` (tráfico que atraviesa el servidor, solo si hace de router). Cada paquete recorre las reglas de su cadena en orden hasta que una coincide; si ninguna coincide, se aplica la **política** de la cadena. Lista las reglas actuales con contadores y número de línea: ```bash sudo iptables -L INPUT -n -v --line-numbers ``` ```output Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination ``` La forma más útil de ver las reglas es en el mismo formato con el que se escriben: ```bash sudo iptables -S ``` ```output -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT ``` Las operaciones que usarás a diario son: | Comando | Efecto | |---------|--------| | `iptables -A INPUT ...` | Añade una regla al final de la cadena | | `iptables -I INPUT 3 ...` | Inserta una regla en la posición 3 | | `iptables -D INPUT 3` | Borra la regla número 3 | | `iptables -D INPUT ...` | Borra la regla que coincida exactamente con la especificación | | `iptables -P INPUT DROP` | Cambia la política de la cadena | Los cambios hechos con estos comandos son inmediatos pero se pierden al reiniciar. En lugar de construir el firewall regla a regla en caliente, en este tutorial lo escribirás en un archivo y lo aplicarás de una vez, que es más seguro y fácil de revisar. ## Paso 3: Escribir las reglas IPv4 Abre el archivo de reglas IPv4: ```bash sudo nano /etc/iptables/rules.v4 ``` Sustituye su contenido por lo siguiente: ```text *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] # Tráfico local y respuestas a conexiones ya establecidas -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT -A INPUT -m conntrack --ctstate INVALID -j DROP # Ping, con límite para no convertirlo en vector de abuso -A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/second --limit-burst 10 -j ACCEPT # SSH: máximo 10 conexiones nuevas por minuto y por IP -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name ssh --set -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name ssh --update --seconds 60 --hitcount 10 -j DROP -A INPUT -p tcp --dport 22 -j ACCEPT # Web -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT # Registrar lo que se descarta, como máximo 5 líneas por minuto -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables-drop: " --log-level 4 COMMIT ``` Qué hace cada bloque: - Las políticas `DROP` en `INPUT` y `FORWARD` descartan todo lo que no se permita explícitamente. `OUTPUT` queda en `ACCEPT` para que el servidor pueda actualizarse y hacer consultas DNS sin reglas adicionales. - La regla `ESTABLISHED,RELATED` es la más importante: deja pasar las respuestas a conexiones que el propio servidor inició y los mensajes ICMP de error asociados. Sin ella, nada funcionaría. - El módulo `recent` guarda las IP que abren conexiones SSH y descarta a las que abren más de 10 en 60 segundos, lo que frena ataques de fuerza bruta sin afectar a un uso normal. - La regla `LOG` va al final, justo antes de que actúe la política `DROP`, así que registra exactamente lo que se descarta. Si tu servidor escucha SSH en otro puerto, cámbialo en las tres reglas de SSH. Si ofrece otros servicios, añade reglas `ACCEPT` antes de la línea `LOG`. Para permitir un servicio solo desde una IP concreta, por ejemplo una base de datos: ```text -A INPUT -p tcp -s 198.51.100.20 --dport 5432 -j ACCEPT ``` ## Paso 4: Aplicar las reglas sin quedarte fuera Comprueba primero que la sintaxis es válida. `-t` solo prueba el archivo, no lo aplica: ```bash sudo iptables-restore -t /etc/iptables/rules.v4 ``` Si no muestra nada, el archivo es correcto. Ahora aplícalo con `iptables-apply`, que carga las reglas y te pide confirmación; si no respondes en el tiempo indicado (porque has perdido la conexión), restaura automáticamente las reglas anteriores: ```bash sudo iptables-apply -t 60 /etc/iptables/rules.v4 ``` ```output Applying new ruleset... done. Can you establish NEW connections to the machine? (y/N) ``` Antes de contestar, **abre una segunda sesión SSH** al servidor desde otra terminal. Si entra sin problemas, responde `y` en la primera. Si no puedes conectar, no respondas: en 60 segundos las reglas se revierten solas. Verifica las reglas cargadas y observa cómo suben los contadores: ```bash sudo iptables -L INPUT -n -v --line-numbers ``` ```output Chain INPUT (policy DROP 12 packets, 624 bytes) num pkts bytes target prot opt in out source destination 1 40 3120 ACCEPT 0 -- lo * 0.0.0.0/0 0.0.0.0/0 2 1822 152K ACCEPT 0 -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED 3 0 0 DROP 0 -- * * 0.0.0.0/0 0.0.0.0/0 ctstate INVALID ... ``` ## Paso 5: Añadir las reglas IPv6 Si el servidor tiene IPv6, necesita su propio conjunto de reglas, que se gestiona con `ip6tables`. Dejarlo sin reglas equivale a dejar el servidor abierto por IPv6. Abre el archivo: ```bash sudo nano /etc/iptables/rules.v6 ``` ```text *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # ICMPv6 es imprescindible: sin él fallan la resolución de vecinos y el descubrimiento de rutas -A INPUT -p ipv6-icmp -j ACCEPT -A INPUT -m conntrack --ctstate INVALID -j DROP -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name ssh6 --set -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name ssh6 --update --seconds 60 --hitcount 10 -j DROP -A INPUT -p tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT -A INPUT -m limit --limit 5/min -j LOG --log-prefix "ip6tables-drop: " --log-level 4 COMMIT ``` A diferencia de IPv4, en IPv6 no puedes bloquear ICMP en general: el protocolo lo usa para encontrar vecinos en la red local, y sin él el servidor perdería la conectividad IPv6. Por eso la regla de ICMPv6 va antes del descarte de paquetes `INVALID`. Valida y aplica. Como la sesión SSH actual va normalmente por IPv4, basta con probar y cargar el archivo: ```bash sudo ip6tables-restore -t /etc/iptables/rules.v6 sudo ip6tables-restore < /etc/iptables/rules.v6 ``` Comprueba desde otra máquina con IPv6 que el servidor sigue respondiendo: ```bash ping -6 -c 3 your_server_ipv6 ``` ## Paso 6: Hacer las reglas persistentes Como has editado directamente `/etc/iptables/rules.v4` y `rules.v6`, esos archivos ya contienen tus reglas. El servicio `netfilter-persistent` los cargará en cada arranque. Asegúrate de que está habilitado: ```bash sudo systemctl enable netfilter-persistent ``` Si en el futuro añades reglas en caliente con `iptables -A` o `-I` y quieres conservarlas, guárdalas en los mismos archivos con: ```bash sudo netfilter-persistent save ``` Reinicia el servidor y verifica que las reglas siguen cargadas: ```bash sudo reboot ``` ```bash sudo iptables -S INPUT | head -n 5 ``` ```output -P INPUT DROP -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -m conntrack --ctstate INVALID -j DROP -A INPUT -p icmp -m icmp --icmp-type 8 -m limit --limit 5/sec --limit-burst 10 -j ACCEPT ``` ## Paso 7: Consultar el registro de paquetes descartados La regla `LOG` envía cada paquete descartado al registro del kernel con el prefijo que definiste. Consúltalo con: ```bash sudo journalctl -k -g 'iptables-drop' -n 20 --no-pager ``` ```output sep 24 10:31:02 server kernel: iptables-drop: IN=eth0 OUT= SRC=192.0.2.77 DST=203.0.113.10 PROTO=TCP SPT=51234 DPT=3306 ... ``` `SRC` es la IP de origen y `DPT` el puerto al que intentaba conectar. Si ves tráfico legítimo descartado, añade la regla `ACCEPT` correspondiente. ## Paso 8 (opcional): Reenviar un puerto con NAT Si el servidor hace de puerta de enlace para una red privada, por ejemplo un contenedor o una VM en `10.0.0.20`, puedes publicar uno de sus puertos. Primero habilita el reenvío de paquetes en el kernel de forma persistente: ```bash echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-ip-forward.conf sudo sysctl --system ``` Añade al principio de `/etc/iptables/rules.v4`, antes de `*filter`, una sección para la tabla `nat`. Sustituye `eth0` por tu interfaz pública: ```text *nat :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.0.0.20:80 -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE COMMIT ``` Como la política de `FORWARD` es `DROP`, permite ese tráfico en la sección `*filter`: ```text -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT -A FORWARD -i eth0 -p tcp -d 10.0.0.20 --dport 80 -m conntrack --ctstate NEW -j ACCEPT -A FORWARD -s 10.0.0.0/24 -o eth0 -j ACCEPT ``` Vuelve a aplicar con `sudo iptables-apply -t 60 /etc/iptables/rules.v4` y prueba `curl -I http://your_server_ip:8080` desde fuera. ## Solución de problemas **Te has quedado sin SSH**: entra por la consola del proveedor y ejecuta `sudo iptables -P INPUT ACCEPT` seguido de `sudo iptables -F INPUT` para abrir temporalmente la cadena. Corrige el archivo y vuelve a aplicarlo con `iptables-apply`. **`iptables-restore: line N failed`**: hay un error de sintaxis en esa línea del archivo. Los fallos más comunes son una opción mal escrita o que falte `COMMIT` al final de una tabla. **El servidor no resuelve nombres ni descarga actualizaciones**: falta la regla `ESTABLISHED,RELATED` en `INPUT` o está después de una regla `DROP`. El orden de las reglas importa. **Las reglas desaparecen tras reiniciar**: comprueba que `netfilter-persistent` está habilitado y que UFW sigue desactivado (`sudo systemctl is-enabled ufw`); si UFW arranca, reemplaza tus reglas. ## Conclusión Has construido un firewall con política de descarte por defecto, límite de conexiones SSH, registro de paquetes descartados y reglas equivalentes para IPv6, aplicado con reversión automática y persistente tras reiniciar. Mantener las reglas en `/etc/iptables/rules.v4` y `rules.v6` te da un único sitio que revisar y versionar. Como siguientes pasos puedes: - Añadir Fail2ban para bloquear durante un tiempo las IP que fallan repetidamente al autenticarse. - Migrar a nftables, el sucesor de iptables, que usa una sola sintaxis para IPv4 e IPv6. - Guardar `rules.v4` y `rules.v6` en un repositorio o desplegarlos con Ansible en todos tus servidores.