firewalld es el gestor de cortafuegos por defecto en Rocky Linux, AlmaLinux, CentOS Stream y RHEL. En lugar de editar reglas de nftables a mano, agrupas interfaces y direcciones de origen en zonas y decides qué servicios acepta cada una. En este tutorial configurarás firewalld en Rocky Linux 9 para un servidor web: abrirás los servicios necesarios, restringirás SSH a tus IP de confianza, bloquearás direcciones concretas y redirigirás puertos, todo sin cortarte el acceso.
Los comandos son idénticos en AlmaLinux 9, CentOS Stream 9 y RHEL 9, y también funcionan en las versiones 8 y 10.
Requisitos previos
- Un servidor con Rocky Linux 9 o AlmaLinux 9, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - Acceso a una consola alternativa (consola VNC o de emergencia del proveedor) por si una regla bloquea tu sesión SSH.
- La IP pública desde la que te conectas. Puedes consultarla con
curl -4 ifconfig.medesde tu equipo local.
Paso 1: Instalar y arrancar firewalld
La mayoría de imágenes de Rocky Linux traen firewalld instalado. Si no está, instálalo:
sudo dnf install firewalld
Habilítalo para que arranque con el sistema y arráncalo ahora:
sudo systemctl enable --now firewalld
Comprueba que está en ejecución:
sudo firewall-cmd --state
running
Importantefirewalld permite SSH en la zona
publicpor defecto, así que activarlo no corta tu sesión. Si alguien ha modificado esa zona antes, revisa el paso 2 antes de continuar.
Paso 2: Revisar la zona activa
Cada interfaz de red pertenece a una zona, y la zona determina qué tráfico entrante se acepta. Consulta la zona por defecto y las zonas activas:
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zones
public
public
interfaces: eth0
El nombre de la interfaz puede ser eth0, ens18 u otro según el hipervisor. Muestra toda la configuración de la zona:
sudo firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
Las zonas predefinidas más útiles son estas:
| Zona | Comportamiento por defecto |
|---|---|
drop | Descarta todo el tráfico entrante sin responder |
block | Rechaza el tráfico entrante con un mensaje ICMP |
public | Solo acepta los servicios listados. Es la zona por defecto |
internal | Pensada para redes internas, admite más servicios |
trusted | Acepta todo el tráfico |
En todas ellas se permite siempre el tráfico de respuesta a conexiones que inicia el propio servidor.
Paso 3: Entender la configuración runtime y permanente
firewalld mantiene dos configuraciones:
- Runtime: la que está activa ahora. Se pierde al recargar o reiniciar.
- Permanente: la guardada en
/etc/firewalld/. Se aplica al arrancar o al ejecutar--reload.
Un comando sin --permanent solo cambia la configuración runtime. Uno con --permanent solo cambia la permanente y no tiene efecto hasta el siguiente --reload. Esta separación te da una forma segura de trabajar con reglas arriesgadas:
- Aplica el cambio sin
--permanenty comprueba que todo funciona. - Guarda el estado actual con
sudo firewall-cmd --runtime-to-permanent.
Si algo sale mal, sudo firewall-cmd --reload descarta los cambios runtime y vuelve a la configuración permanente. Para cambios sencillos, como abrir HTTP, es más directo usar --permanent y recargar.
Paso 4: Permitir servicios
firewalld incluye definiciones de servicio con sus puertos (están en /usr/lib/firewalld/services/). Lista las disponibles:
sudo firewall-cmd --get-services
Para un servidor web, permite HTTP y HTTPS de forma permanente y recarga:
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload
success
success
Si no usas Cockpit (la consola web en el puerto 9090), quita ese servicio de la zona:
sudo firewall-cmd --permanent --remove-service=cockpit
sudo firewall-cmd --reload
Comprueba los servicios permitidos:
sudo firewall-cmd --list-services
dhcpv6-client http https ssh
Para ver qué puertos abre un servicio concreto:
sudo firewall-cmd --info-service=https
https
ports: 443/tcp
protocols:
source-ports:
modules:
destination:
includes:
helpers:
Paso 5: Abrir puertos concretos
Cuando una aplicación no tiene definición de servicio, abre el puerto directamente. Por ejemplo, para una aplicación en el puerto 8080/tcp:
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
También puedes abrir rangos, como los puertos pasivos de un servidor FTP:
sudo firewall-cmd --permanent --add-port=40000-40100/tcp
sudo firewall-cmd --reload
Verifica los puertos abiertos:
sudo firewall-cmd --list-ports
8080/tcp 40000-40100/tcp
Para cerrar un puerto, usa --remove-port con el mismo valor.
Crear un servicio propio
Si una aplicación usa varios puertos, es más legible definir un servicio con nombre. Este ejemplo crea myapp con los puertos 9000/tcp y 9001/tcp:
sudo firewall-cmd --permanent --new-service=myapp
sudo firewall-cmd --permanent --service=myapp --set-description="Mi aplicación"
sudo firewall-cmd --permanent --service=myapp --add-port=9000/tcp
sudo firewall-cmd --permanent --service=myapp --add-port=9001/tcp
sudo firewall-cmd --reload
La definición se guarda en /etc/firewalld/services/myapp.xml. Ahora puedes añadirla a una zona como cualquier otro servicio:
sudo firewall-cmd --permanent --add-service=myapp
sudo firewall-cmd --reload
Paso 6: Restringir SSH a tus IP de confianza
Dejar SSH abierto a todo Internet atrae intentos de fuerza bruta constantes. Una forma limpia de restringirlo es asignar tus IP de confianza a la zona internal, que permite SSH, y quitar SSH de la zona public.
En los comandos siguientes, sustituye 203.0.113.10 por tu IP pública. Como este cambio puede bloquear tu acceso, hazlo primero en runtime.
Añade tu IP como origen de la zona internal:
sudo firewall-cmd --zone=internal --add-source=203.0.113.10/32
Quita SSH de la zona public:
sudo firewall-cmd --zone=public --remove-service=ssh
Abre una segunda sesión SSH desde tu equipo sin cerrar la actual. Si conecta, el cambio funciona y puedes hacerlo permanente:
sudo firewall-cmd --runtime-to-permanent
Si la nueva sesión no conecta, ejecuta sudo firewall-cmd --reload en la sesión que sigue abierta para deshacer los cambios.
Comprueba el resultado:
sudo firewall-cmd --get-active-zones
internal
sources: 203.0.113.10/32
public
interfaces: eth0
El tráfico que llega desde 203.0.113.10 se evalúa con las reglas de internal; el resto sigue en public, que ya no permite SSH.
ConsejoSi tu IP de casa cambia a menudo, añade también la IP de un bastión o una VPN, o usa la regla con límite de conexiones del paso siguiente en lugar de restringir por origen.
Paso 7: Usar reglas enriquecidas
Las reglas enriquecidas (rich rules) permiten condiciones que no cubren los servicios y puertos simples: origen concreto, registro, límites de frecuencia o rechazo explícito.
Bloquear una IP o una red
Para descartar todo el tráfico de una dirección:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.23" drop'
sudo firewall-cmd --reload
Funciona igual con una red completa, por ejemplo 198.51.100.0/24. Una alternativa equivalente es asignar el origen a la zona drop:
sudo firewall-cmd --permanent --zone=drop --add-source=198.51.100.0/24
sudo firewall-cmd --reload
Permitir un puerto solo desde una IP
Para que solo tu servidor de aplicaciones 10.0.0.5 pueda conectar a MySQL (puerto 3306):
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.5" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --reload
No añadas el servicio mysql a la zona, o el puerto quedará abierto para todos.
Limitar la frecuencia de conexiones
Esta regla acepta como máximo 10 conexiones nuevas por minuto al servicio SSH. Tiene sentido si mantienes SSH abierto en public:
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --add-rich-rule='rule service name="ssh" accept limit value="10/m"'
sudo firewall-cmd --reload
Primero se quita el servicio ssh de la zona, porque si no se aceptaría todo el tráfico SSH antes de llegar a la regla con límite. Las conexiones que superen el límite se rechazan.
Listar y eliminar reglas enriquecidas
sudo firewall-cmd --list-rich-rules
rule family="ipv4" source address="198.51.100.23" drop
rule family="ipv4" source address="10.0.0.5" port port="3306" protocol="tcp" accept
rule service name="ssh" accept limit value="10/m"
Para eliminar una, copia la línea exacta en --remove-rich-rule:
sudo firewall-cmd --permanent --remove-rich-rule='rule family="ipv4" source address="198.51.100.23" drop'
sudo firewall-cmd --reload
Paso 8: Redirigir puertos
firewalld puede redirigir un puerto entrante a otro puerto local. Esto es útil cuando una aplicación escucha en 8080 y quieres servirla en el 80 sin ejecutarla como root:
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080
sudo firewall-cmd --reload
Para redirigir a otra máquina (por ejemplo, 10.0.0.20 en tu red privada), también necesitas activar el enmascaramiento (NAT):
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --permanent --add-forward-port=port=2222:proto=tcp:toport=22:toaddr=10.0.0.20
sudo firewall-cmd --reload
Verifica las redirecciones:
sudo firewall-cmd --list-forward-ports
port=80:proto=tcp:toport=8080:toaddr=
port=2222:proto=tcp:toport=22:toaddr=10.0.0.20
Paso 9: Registrar el tráfico rechazado
Para diagnosticar qué bloquea el cortafuegos, activa el registro de paquetes denegados:
sudo firewall-cmd --set-log-denied=unicast
Este ajuste se guarda automáticamente en la configuración permanente. Los paquetes rechazados aparecen en el registro del kernel:
sudo journalctl -k -g 'REJECT' --since "10 minutes ago"
Sep 25 10:12:03 server kernel: filter_IN_public_REJECT: IN=eth0 OUT= SRC=192.0.2.44 DST=203.0.113.50 PROTO=TCP SPT=51544 DPT=3306 ...
Cuando termines de diagnosticar, desactívalo para no llenar el diario:
sudo firewall-cmd --set-log-denied=off
Solución de problemas
Una regla con --permanent no tiene efecto. Los cambios permanentes no se aplican hasta recargar. Ejecuta sudo firewall-cmd --reload y comprueba con --list-all.
Una regla sin --permanent desaparece al reiniciar. Solo estaba en runtime. Guárdala con sudo firewall-cmd --runtime-to-permanent.
Te has quedado sin acceso SSH. Entra por la consola VNC o de emergencia del proveedor y ejecuta sudo firewall-cmd --reload (si el cambio era solo runtime) o revisa la zona con sudo firewall-cmd --list-all --zone=public y vuelve a añadir SSH con sudo firewall-cmd --permanent --add-service=ssh seguido de --reload.
El puerto está abierto pero no responde. Comprueba que la aplicación escucha en todas las interfaces y no solo en 127.0.0.1 con sudo ss -tlnp. Si la aplicación escucha correctamente, revisa SELinux: un puerto no estándar puede necesitar su etiqueta, por ejemplo sudo semanage port -a -t http_port_t -p tcp 8080 para un servidor web.
Error ALREADY_ENABLED o NOT_ENABLED. Es un aviso, no un fallo: la regla ya existía o ya no estaba. La configuración no cambia.
Conclusión
Has configurado firewalld con una zona public que solo expone los servicios necesarios, acceso SSH limitado a tus IP de confianza, reglas enriquecidas para casos concretos y redirección de puertos. Trabajar primero en runtime y guardar con --runtime-to-permanent te permite probar cualquier cambio sin riesgo de quedarte fuera.
Como siguientes pasos, puedes instalar Fail2ban con la acción firewallcmd-rich-rules para bloquear IP que fallan el inicio de sesión, cambiar el puerto SSH añadiendo el nuevo puerto a firewalld y a SELinux, o revisar la configuración de SELinux de tu servidor.
