Los firewalls de host en servidores Linux se tratan cada vez más como código de aplicación: las reglas están en un repositorio, los cambios pasan por una revisión y una herramienta los despliega. Es correcto, y en la transición plantea una trampa fácil de pasar por alto. Al final queda un servidor que se comporta con total normalidad y, sin embargo, ya no filtra nada.
Este artículo explica el mecanismo con el ejemplo de nftables, el framework de filtrado de paquetes de las distribuciones Linux actuales, describe un orden seguro para la migración y añade otros dos escollos: las reglas que se derivan de sockets abiertos y los puertos de contenedores que eluden el firewall del host.
Cómo funciona el servicio de la distribución
Debian incluye con el paquete nftables un servicio de systemd, nftables.service. Su archivo de unidad es breve e inequívoco: al arrancar y al recargar carga el archivo /etc/nftables.conf con nft -f. Al detenerse ejecuta nft flush ruleset.
A ello se suma el archivo de configuración incluido, cuya primera instrucción es flush ruleset. El wiki de nftables describe lo que hace este comando: borra el conjunto completo de reglas, «flush (erase, delete, wipe) the complete ruleset». No se refiere solo a las tablas que ha creado este servicio, sino a todo lo que está cargado en el kernel.
Para un servidor en el que solo este servicio gestiona reglas, es un diseño razonable: el conjunto de reglas se sustituye por completo en cada carga y no quedan restos de estados anteriores.
La trampa al cambiar de mecanismo
El problema surge en cuanto entra en juego un segundo mecanismo. Un desarrollo típico: en adelante, las reglas deben venir de un repositorio y cargarse mediante una gestión de la configuración o un servicio propio, en una tabla propia. El nuevo conjunto de reglas se carga, se comprueba y se da por bueno. El servicio antiguo se queda donde está, porque al fin y al cabo no molesta.
En algún momento posterior ocurre una de tres cosas:
- Alguien detiene el servicio antiguo, por ejemplo al hacer limpieza. La detención vacía todo el conjunto de reglas, incluida la nueva tabla. El host queda después sin filtrado.
- Alguien lo reinicia o lo recarga. Se carga el archivo antiguo, y su
flush rulesetelimina todo lo que no figura en él. El host vuelve a funcionar con el estado que se había sustituido. - Alguien carga a mano el archivo antiguo para «probar algo rápidamente», con el mismo resultado.
En ninguno de los casos hay un mensaje de error. Los servicios siguen accesibles, las aplicaciones funcionan, la monitorización sigue en verde. Precisamente eso hace peligrosa la trampa: las pruebas habituales tras un cambio comprueban si algo funciona, no si algo se bloquea.
El orden seguro
La solución no es una técnica nueva, sino un orden:
- Desactivar o enmascarar el servicio antiguo, sin detenerlo. La página de manual de systemctl es inequívoca:
disableno detiene la unidad; solo lo hace la opción--now.maskva más allá e impide cualquier arranque, también el manual. Unstopsería exactamente el comando que vacía el conjunto de reglas. - Cargar el nuevo conjunto de reglas: en una tabla propia, desde el repositorio, mediante el mecanismo que lo gestionará en adelante.
- Leer lo que está realmente cargado.
nft list tablesmuestra qué tablas existen;nft list rulesetmuestra el estado completo. En esta fase, la tabla antigua y la nueva deberían estar una junto a otra. Ambas actúan simultáneamente: mantenga esta fase breve. - Demostrar el filtrado desde fuera; enseguida, más sobre ello.
- Solo entonces eliminar de forma selectiva la tabla antigua, con
nft delete table, no deteniendo el servicio antiguo. El archivo de configuración antiguo se archiva o se modifica de modo que una carga accidental ya no vacíe nada. - Tras el siguiente reinicio, volver a leer si el nuevo conjunto de reglas está cargado y el antiguo no vuelve.
Un detalle merece atención: desactivar y enmascarar impiden un nuevo arranque, pero no terminan el estado en curso. Como la unidad está definida como servicio de ejecución única con RemainAfterExit=yes, para systemd sigue figurando como activa hasta el siguiente reinicio, y su comando de detención sigue registrado. Documéntelo como riesgo conocido.
La prueba negativa desde fuera
Tras cada cambio en el firewall se comprueba si los servicios permitidos son accesibles. Eso demuestra que no se ha roto nada. Que se filtra, no lo demuestra: un host sin ninguna regla supera esa prueba igualmente.
Por eso, todo cambio requiere una prueba negativa: un intento de conexión desde un origen fuera del host que debe bloquearse, hacia un puerto que debe estar cerrado. Solo cuando ese intento fracasa queda demostrado el efecto. «Fuera» debe tomarse al pie de la letra: las conexiones que un host establece consigo mismo pasan por la interfaz de loopback, que los conjuntos de reglas suelen permitir de forma general.
Repita la prueba periódicamente, no solo cuando haya cambios: la trampa descrita puede saltar semanas después.
Reglas desde el repositorio, no desde la shell
Los comandos ad hoc en la consola son el antagonista natural de toda gestión de reglas como código. Una regla que solo existe en el kernel en ejecución desaparece en la siguiente carga desde el repositorio, o sobrevive donde nadie la espera, porque alguien exporta el estado en ejecución a un archivo. Ambas cosas hacen imprevisible el conjunto de reglas. La regla es sencilla: lo que no figura en el repositorio revisado no pertenece al host. Los cambios de emergencia se incorporan después, y una comparación periódica entre el repositorio y nft list ruleset hace visibles las desviaciones.
No todo socket abierto necesita una regla
Al construir un nuevo conjunto de reglas, resulta tentador recorrer la lista de sockets abiertos de un host y crear una autorización para cada uno. Eso conduce a aperturas superfluas. Un ejemplo es un socket UDP que un servicio de registros usa solo para enviar, por ejemplo al reenviar mensajes de syslog a un servidor central. Aparece en la lista de sockets, pero no necesita ninguna regla de entrada.
Derive las autorizaciones de la finalidad: ¿qué conexiones debe aceptar el host y de quién? La lista de sockets es una herramienta de control, no la plantilla.
Los puertos de contenedores eluden INPUT
El tercer escollo afecta a los hosts con Docker. Según la documentación de Docker, los puertos publicados se vinculan por defecto a todas las direcciones del host; publicarlos sería «insecure by default» (inseguro por defecto). Los paquetes pasan por una redirección de Destination NAT y en la cadena FORWARD se entregan a cadenas propias de Docker. Para UFW, la documentación formula la consecuencia de forma expresa: Docker redirige el tráfico de los contenedores en la tabla nat antes de que llegue a las cadenas INPUT y OUTPUT que utiliza UFW. Una regla INPUT mantenida con esmero no ve, por tanto, un puerto de contenedor publicado.
Si un servicio en un contenedor solo debe ser accesible localmente, vincúlelo expresamente a 127.0.0.1, por ejemplo con -p 127.0.0.1:8080:80. Según la documentación, entonces solo puede acceder el propio host, con una salvedad: antes de Docker 28.0.0, los hosts del mismo segmento de capa 2 también podían alcanzar puertos publicados en localhost. Para reglas de filtrado que deban aplicarse también ante los contenedores, rige lo siguiente: con el backend iptables estándar, Docker prevé para ello la cadena DOCKER-USER; con el backend experimental de nftables, esta desaparece. Los puertos de contenedores publicados forman parte, además, de toda prueba negativa.
Cómo apoya sector7
Endurecemos sistemas operativos y servicios estándar y construimos con usted conjuntos de reglas limpios y trazables; más información en Ciberseguridad. Los escaneos de vulnerabilidades periódicos y automatizados revisan externamente su superficie de ataque e internamente sistemas y servicios; muestran cuándo hay un puerto abierto que no debería estarlo. En el marco de nuestros Managed Network Services asumimos también la gestión de cambios.
Fuentes
- nftables.service (fuente del paquete Debian) – Debian pkg-netfilter-team, consultado el 30.09.2026
- nftables.conf (fuente del paquete Debian) – Debian pkg-netfilter-team, consultado el 30.09.2026
- Operations at ruleset level – wiki de nftables, consultado el 30.09.2026
- Configuring tables – wiki de nftables, consultado el 30.09.2026
- systemctl(1), Debian Bookworm – Debian, consultado el 30.09.2026
- Packet filtering and firewalls – Docker, consultado el 30.09.2026
- Port publishing and mapping – Docker, consultado el 30.09.2026
- Docker with iptables – Docker, consultado el 30.09.2026
- Docker with nftables – Docker, consultado el 01.10.2026