Firewalld en Rocky y AlmaLinux: guía básica para VPS
Aprende a abrir SSH y puertos web, cerrar servicios y conservar las reglas tras reiniciar. Incluye zonas de firewalld y el error común de --permanent.
Qué es firewalld y por qué Rocky y AlmaLinux lo incluyen
firewalld es el gestor de firewall instalado de forma predeterminada en Rocky Linux, AlmaLinux y las demás reconstrucciones de Red Hat Enterprise Linux (RHEL). Ambas distribuciones heredaron ese valor predeterminado en lugar de elegirlo, algo que se entiende mejor si conoce cómo Rocky y AlmaLinux reconstruyeron el trabajo de Red Hat después de que CentOS cambiara de dirección. firewalld no inspecciona los paquetes directamente. Mantiene una configuración guardada y convierte esa configuración en reglas de nftables. El comando firewall-cmd la modifica mientras el servidor sigue en línea. Nada de lo descrito en esta guía cambia entre ambas distribuciones, porque lo que realmente diferencia Rocky de AlmaLinux es el compromiso de compatibilidad y el rango de CPU que todavía admiten, no el firewall.
Si ya sabe cómo funciona ufw en un VPS de Ubuntu, conoce el objetivo. firewalld añade dos conceptos que ufw no tiene. El primero son las zonas: una política con nombre en la que se clasifican los paquetes. El segundo es la separación entre las reglas activas y las reglas guardadas. Esta separación se controla con el indicador --permanent y es la principal fuente de confusión de esta herramienta.
Todo lo que sigue son comandos que ejecutará en su propio servidor. Pruebe cada cambio desde una segunda máquina, porque una regla que parece correcta en el servidor aún puede ser incorrecta desde Internet.
Abrir SSH antes de hacer cualquier otra cosa
La mayoría de las instalaciones de Rocky y AlmaLinux ya tienen firewalld instalado y en ejecución, y la configuración incluida permite SSH. Algunas imágenes cloud mínimas lo eliminan. Compruébelo en lugar de darlo por hecho.
sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --statefirewall-cmd --state muestra running. Si el servicio está detenido, cualquier otra llamada a firewall-cmd responde FirewallD is not running y termina con un código distinto de cero. Es lo primero que debe comprobar cuando un comando parece no hacer nada.
Ahora revise lo que está permitido actualmente.
sudo firewall-cmd --list-allLa salida real contiene algunas líneas más. Estas son las importantes:
public (active)
target: default
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports:
rich rules:ssh en la línea services: es el motivo por el que su sesión sigue funcionando. Si falta, añádalo antes de modificar cualquier otra cosa, porque iniciar un firewall sin una regla para SSH termina la sesión y no permite volver a conectarse.
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reloadtarget: default significa que un paquete que no coincide con ninguna regla se rechaza con una respuesta ICMP (protocolo de mensajes de control de Internet) host-prohibited, por lo que un cliente que accede a un puerto cerrado ve No route to host de inmediato. Establecer el destino en DROP hace que el servidor permanezca silencioso; los escáneres esperan entonces hasta que se agota el tiempo de espera.
sudo firewall-cmd --permanent --zone=public --set-target=DROP
sudo firewall-cmd --reloadConozca el coste antes de ejecutar esto: DROP también impide que el servidor responda a ping, por lo que su propia supervisión también deja de recibir respuestas.
¿Por qué desapareció mi regla? El indicador --permanent
firewalld mantiene dos configuraciones a la vez. La configuración en tiempo de ejecución es la que aplica el kernel en este momento. La configuración permanente es la que se guarda en /etc/firewalld/zones/public.xml y la que vuelve a cargarse después de una recarga o un reinicio.
Un comando sin --permanent sólo modifica la configuración en tiempo de ejecución. Funciona de inmediato y desaparece con la siguiente recarga o el siguiente arranque. Un comando con --permanent escribe el archivo y no modifica nada de lo que está en ejecución, por lo que el puerto permanece cerrado hasta que se recarga la configuración. Ninguno de estos comportamientos es un error. Ambos confunden porque el comando muestra success en los dos casos.
Escriba siempre el par.
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reloadPuede leer las dos configuraciones. Es la forma más rápida de averiguar cuál de los dos errores cometió.
sudo firewall-cmd --list-services
sudo firewall-cmd --permanent --list-servicesEl primero muestra el conjunto activo. El segundo muestra el conjunto guardado. Si el conjunto activo contiene un servicio que no está en el conjunto guardado, esa regla desaparece con la siguiente recarga. Si el conjunto guardado contiene una regla que no está en el conjunto activo, olvidó recargar la configuración. sudo firewall-cmd --runtime-to-permanent copia todo lo que está activo en el archivo guardado. Esto resulta útil después de una sesión de pruebas.
--reload conserva el estado del seguimiento de conexiones, por lo que la sesión SSH sobrevive. --complete-reload también recarga los módulos del kernel y pierde ese estado, lo que normalmente termina todas las conexiones abiertas, incluida la suya. Use la recarga normal.
Hay una medida de seguridad integrada. Una regla en tiempo de ejecución puede caducar por sí sola.
sudo firewall-cmd --add-service=http --timeout=5mEsa regla se elimina después de cinco minutos. No se puede combinar con --permanent, y ese es el objetivo: sirve para probar un cambio sobre el que no tiene certeza. La medida de seguridad antigua es mejor. Mantenga abierta una segunda sesión SSH mientras edita las reglas y no la cierre hasta que un inicio de sesión nuevo confirme que las reglas funcionan.
Zonas y por qué sólo importa la zona predeterminada en un VPS
Una zona es un conjunto de permisos con un nivel de confianza asociado. firewalld asigna cada paquete entrante exactamente a una zona. Primero compara la dirección de origen del paquete con la lista sources: de cada zona. Si no encuentra ninguna coincidencia, usa la zona a la que está asociada la interfaz de entrada. Si la interfaz no está asociada a ninguna zona, el paquete pasa a la zona predeterminada.
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zonesEn un VPS con una sola interfaz de red, la primera respuesta casi siempre es public, y esa es la única zona que usará. firewall-cmd sin el argumento --zone= actúa sobre la zona predeterminada. Por eso todos los comandos breves de esta guía funcionan sin indicar una zona.
Este es el fallo que puede hacerle perder toda una tarde. Si la interfaz está asociada a otra zona, sus reglas se añaden a public, mientras el tráfico se gestiona en otra zona. Por tanto, nada de lo que añada tiene efecto y no aparece ningún aviso. --get-active-zones muestra la asociación:
public
interfaces: eth0Si la interfaz aparece bajo otro nombre de zona, escriba las reglas en esa zona con --zone= o mueva la interfaz.
sudo firewall-cmd --permanent --zone=public --change-interface=eth0
sudo firewall-cmd --reloadNetworkManager gestiona las interfaces en Rocky y AlmaLinux, y vuelve a aplicar la zona cuando se activa la conexión. Configúrela también allí para que un reinicio no deshaga los cambios. Obtenga el nombre de la conexión del primer comando, porque rara vez coincide con el nombre del dispositivo.
sudo nmcli connection show
sudo nmcli connection modify "System eth0" connection.zone publicLa coincidencia por origen tiene prioridad sobre la coincidencia por interfaz. Así, una dirección puede recibir una política diferente. La zona integrada trusted acepta todo.
sudo firewall-cmd --permanent --zone=trusted --add-source=203.0.113.10/32
sudo firewall-cmd --reloadTenga cuidado con esa zona. Abre todos los puertos del servidor a esa dirección, incluida la base de datos que creía privada. Use una rich rule cuando quiera permitir un solo puerto y no todo el host.
¿Qué es un servicio de firewalld?
Un servicio es un conjunto de puertos con nombre que se distribuye como un archivo XML. --add-service=https abre 443/tcp porque /usr/lib/firewalld/services/https.xml define qué significa https.
sudo firewall-cmd --get-services
sudo firewall-cmd --info-service=https--info-service muestra los puertos que se ocultan tras el nombre:
https
ports: 443/tcpUse el nombre cuando exista. Se entiende mejor en --list-all seis meses después, y paquetes como Cockpit instalan su propio archivo de servicio. Use --add-port para cualquier elemento que no tenga una definición.
Debe tener en cuenta esta diferencia: el servicio ssh significa 22/tcp y nada más. Si cambió SSH a otro puerto al reforzar el acceso SSH al servidor, --add-service=ssh no abre el puerto que realmente usa.
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reloadEn una reinstalación de RHEL hay un segundo control sobre ese puerto. SELinux (security-enhanced Linux) asigna etiquetas a los números de puerto, y sshd no puede enlazarse a un puerto que esté fuera de sus etiquetas. Por eso no se inicia y el registro muestra error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. Etiquete primero el puerto.
sudo dnf install -y policycoreutils-python-utils
sudo semanage port -a -t ssh_port_t -p tcp 2222¿Cómo puedo ver qué está abierto ahora mismo?
sudo firewall-cmd --list-all
sudo firewall-cmd --list-rich-rules
sudo nft list table inet firewalld | head -n 40Los dos primeros comandos muestran lo que firewalld considera activo. El tercero lee las reglas que mantiene realmente el kernel, en la tabla que administra firewalld. Deberían coincidir.
Nada de esto es una prueba concluyente. Haga la prueba desde otra máquina:
nc -zv 203.0.113.20 443No ejecute la prueba en el propio servidor. firewalld acepta todo lo que llega por la interfaz de loopback, por lo que curl http://localhost:8080 se ejecuta correctamente independientemente de lo que indiquen las reglas. Esta prueba confirma que el servicio está activo. No proporciona información sobre el firewall.
Permitir un puerto web
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-servicesEl último comando ahora debería mostrar http https junto a lo que ya aparecía. Si el sitio sigue sin responder, es posible que el firewall no sea el problema. Una regla permite el paso de un paquete. Aun así, un proceso debe estar escuchando para recibirlo.
sudo ss -tlnpUn socket que aparece como 0.0.0.0:443 o *:443 acepta conexiones desde cualquier dirección. Uno que aparece como 127.0.0.1:443 responde sólo en loopback, y ninguna regla del firewall hará que sea accesible desde fuera. Puertos y sockets de escucha en Linux explica esta diferencia con más detalle.
¿Cómo cierro un puerto?
sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --reloadLa regla --permanent también se aplica aquí, y las consecuencias son peores en esta dirección. Si elimina un servicio sólo del tiempo de ejecución, el puerto parece cerrado, pero la siguiente recarga o el siguiente reinicio lo vuelve a abrir desde el archivo guardado. Es una brecha que no detectará, porque la comprobación que ejecutó terminó correctamente.
Si elimina algo que nunca estuvo definido, se muestra Warning: NOT_ENABLED: http y el comando sigue terminando con el código 0. Si añade lo mismo dos veces, se muestra Warning: ALREADY_ENABLED: http. Ambos casos son seguros. Un nombre mal escrito es diferente: Error: INVALID_SERVICE significa que firewalld no tiene ninguna definición con ese nombre y que no se modificó nada.
Si --list-all muestra cockpit y no utiliza la consola web Cockpit en el puerto 9090, elimínelo. Cada puerto abierto corresponde a un servicio que debe mantener actualizado. Para los servicios que decida conservar, dnf-automatic puede instalar las actualizaciones de seguridad mediante un temporizador, de modo que esta tarea no dependa de que usted la recuerde. Sin embargo, instalar un parche no equivale a aplicarlo, y needs-restarting muestra qué servicios siguen usando las bibliotecas antiguas cuando esas actualizaciones ya se han instalado.
Restringir un puerto a una única dirección de origen
Las reglas enriquecidas son la forma extendida que se usa cuando un nombre de servicio simple no puede expresar lo que se necesita. Restringir SSH a una única dirección de la oficina requiere dos comandos, y el segundo es el que se suele olvidar.
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" service name="ssh" accept'
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reloadUna zona es un conjunto de permisos, no una lista numerada que se detiene en la primera coincidencia. La regla enriquecida añade una aceptación para una dirección. No deniega a nadie. Mientras ssh siga en la línea services:, todo Internet seguirá accediendo al puerto 22 y la regla enriquecida no cambiará nada observable. Elimine la entrada general; de lo contrario, la específica sólo será decorativa.
Para un puerto sin nombre de servicio, especifique el puerto directamente.
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port port="5432" protocol="tcp" accept'Para descartar una red que genera ruido y conservar un registro, coloque el elemento log antes de la acción. Ese es el orden que espera el lenguaje de reglas enriquecidas.
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.0/24" log prefix="fw-drop " level="info" limit value="3/m" drop'
sudo firewall-cmd --reload
sudo journalctl -k -g fw-dropEl valor limit evita que una avalancha de paquetes llene el journal. Antes de restringir SSH a una única dirección, asegúrese de que esa dirección sea estable. Una conexión doméstica con una dirección IP dinámica puede bloquearle el acceso el día que cambie. Por eso, pruebe antes que el acceso a la consola de su proveedor esté disponible y funcione.
comandos de ufw y sus equivalentes de firewall-cmd
Misma tarea, otra herramienta. Cada línea --permanent necesita una línea sudo firewall-cmd --reload después. Es el único aspecto que una lista como esta no puede mostrar.
sudo ufw enablese convierte ensudo systemctl enable --now firewalldsudo ufw disablese convierte ensudo systemctl disable --now firewalldsudo ufw status verbosese convierte ensudo firewall-cmd --list-allsudo ufw allow OpenSSHse convierte ensudo firewall-cmd --permanent --add-service=sshsudo ufw allow 443/tcpse convierte ensudo firewall-cmd --permanent --add-port=443/tcpsudo ufw delete allow 443/tcpse convierte ensudo firewall-cmd --permanent --remove-port=443/tcpsudo ufw allow from 203.0.113.10 to any port 22se convierte en la regla enriquecida mostrada arribasudo ufw reloadse convierte ensudo firewall-cmd --reloadsudo ufw default deny incomingya es el comportamiento de la zonapublic, y--set-target=DROPes su versión silenciosasudo ufw logging onse convierte ensudo firewall-cmd --set-log-denied=all
Conviene señalar claramente una diferencia. ufw mantiene una lista numerada y permite insertar una regla en la posición 1. firewalld no tiene números de regla, por lo que aquí no significa nada «colocar esta regla en primer lugar». Cuando dos entradas de firewalld parecen contradecirse, prevalece la aceptación general porque ninguna regla del conjunto deniega el tráfico. La entrada general debe eliminarla usted mismo.
¿Por qué se puede acceder a mi contenedor Docker si el firewall parece cerrado?
Porque un puerto publicado del contenedor nunca pasa por la parte del firewall que controla la zona. docker run -d -p 8080:80 nginx indica a Docker que escriba sus propias reglas de NAT (traducción de direcciones de red) y de reenvío. Un paquete que llega al puerto 8080 se reescribe y se enruta al contenedor, por lo que se reenvía en lugar de entregarse al host. Las líneas services: y ports: de la zona controlan los paquetes entregados al host. Las reglas de Docker controlan la ruta de reenvío y los aceptan.
El resultado es un servidor en el que sudo firewall-cmd --list-all no muestra el puerto 8080, pero nc -zv 203.0.113.20 8080 desde otra máquina se conecta igualmente. Compruebe lo que instaló Docker:
sudo iptables -t nat -L DOCKER -nLa solución está en la opción de publicación. Enlace el puerto a loopback y coloque un reverse proxy delante.
docker run -d -p 127.0.0.1:8080:80 nginxEl contenedor ahora responde en curl http://127.0.0.1:8080 en el servidor y no acepta conexiones externas. Los usuarios de Ubuntu encuentran el mismo problema, descrito en por qué los contenedores Docker publican puertos directamente a través de ufw. Rootful Podman, que Rocky y AlmaLinux incluyen en los repositorios base, publica los puertos con el mismo enfoque de NAT. Por tanto, pruebe desde otra máquina en lugar de confiar en la lista de la zona. Esta coincidencia también explica por qué instalar Docker Engine en estas distribuciones requiere algunos pasos que una guía para Ubuntu nunca menciona, empezando por que Podman ya controla el comando docker.
Hacer que sobreviva a un reinicio y los errores que verá
sudo systemctl is-enabled firewalld
sudo systemctl status firewalldenabled y active (running) son los valores que necesita. Un firewall que está activo pero no habilitado le protege hasta el primer reinicio. Esta comprobación debe formar parte de la lista que revisa durante los primeros diez minutos en un VPS nuevo, junto con las claves SSH y las actualizaciones.
Los comandos sin procesar de nftables y firewalld no deben mezclarse. firewalld administra una tabla llamada inet firewalld. sudo nft flush ruleset la elimina, y el servidor queda abierto a todo. firewall-cmd --list-all sigue mostrando la configuración prevista porque firewalld informa de lo que cree que existe, no de lo que mantiene el kernel. sudo firewall-cmd --reload vuelve a instalar las reglas. Escriba las reglas con firewall-cmd para que se restauren después de una recarga.
Dos gestores de firewall en un mismo servidor. Instalar ufw o iptables-services junto a firewalld hace que dos programas escriban reglas sin conocerse entre sí. El resultado depende de qué servicio se haya iniciado en último lugar. Elija uno. En Rocky y AlmaLinux, firewalld es el que cuenta con soporte de la distribución.
El firewall del proveedor delante del servidor. Muchos paneles de VPS tienen un firewall de red independiente. Si --list-all muestra un puerto abierto y una conexión externa sigue fallando, compruebe el panel antes de cambiar nada en el servidor. También puede ocurrir lo contrario: una regla abierta en el panel no sirve de nada mientras firewalld rechace el paquete.
Ejecutar firewall-cmd sin sudo. Cada cambio necesita root. Sin ello, la solicitud se rechaza durante la comprobación de autorización y no se modifica nada. A primera vista, parece que el comando se ha ignorado.
Seis comandos cubren la mayoría de las tareas diarias: --list-all para consultar el estado, --permanent --add-service o --add-port para abrir algo, --permanent --remove-service para cerrarlo, --reload para aplicar el archivo guardado y --runtime-to-permanent después de una ronda de pruebas. La zona es public, la opción es --permanent y la única comprobación fiable se realiza desde otra máquina.
FAQ
¿Por qué desapareció mi regla de firewalld después de reiniciar?
La regla sólo se añadió a la configuración de ejecución. sudo firewall-cmd --add-service=http se aplica de inmediato y se descarta en la siguiente recarga o durante el arranque, porque nunca se modificó la configuración guardada en /etc/firewalld/zones/public.xml. Añada --permanent y ejecute sudo firewall-cmd --reload. Para conservar las reglas que ya añadió manualmente, ejecute sudo firewall-cmd --runtime-to-permanent, que copia el conjunto activo en el archivo guardado.
¿Por qué no cambia nada después de añadir una regla con --permanent?
Porque --permanent escribe el archivo y no modifica el firewall en ejecución. El puerto permanece cerrado hasta que sudo firewall-cmd --reload carga la configuración guardada en el kernel. Compare sudo firewall-cmd --list-services con sudo firewall-cmd --permanent --list-services: si la lista guardada contiene una entrada que no aparece en la lista activa, falta ejecutar la recarga.
¿Debo usar --add-service o --add-port?
Use --add-service cuando exista un nombre para el servicio que ejecuta. Indica la intención, y sudo firewall-cmd --info-service=https muestra exactamente qué puertos cubre ese nombre. Use --add-port cuando no haya una definición para su servicio o cuando escuche en un puerto no estándar. El servicio ssh sólo permite 22/tcp, por lo que, si SSH se trasladó a 2222, necesita --add-port=2222/tcp y una etiqueta de SELinux para ese puerto.
¿Por qué se puede acceder a mi contenedor Docker cuando firewall-cmd muestra el puerto cerrado?
Docker reescribe un puerto publicado mediante sus propias reglas NAT y lo reenvía al contenedor. Por tanto, el paquete nunca se entrega al host, y las listas de servicios y puertos de una zona sólo cubren los paquetes entregados al host. El contenedor responde desde Internet mientras --list-all no muestra nada. Publique el puerto sólo en loopback con docker run -d -p 127.0.0.1:8080:80 nginx y coloque un reverse proxy delante.
¿Puedo instalar ufw en Rocky Linux en lugar de firewalld?
Dos gestores de firewall en un mismo servidor escriben reglas sin conocer las reglas del otro. El conjunto que permanece depende de qué servicio se inició en último lugar. firewalld es la herramienta compatible con Rocky Linux y AlmaLinux, ya está instalado y utiliza el mismo backend nftables que usaría ufw. Aprenda una vez la zona predeterminada y la opción --permanent, y tendrá todo lo necesario para usar la herramienta.