LinuxParty
En uno de nuestros servidores Linux administrados, comenzamos a observar un comportamiento bastante extraño: el firewall funcionaba correctamente, pero, sin motivo aparente, todas sus reglas desaparecían.
Al ejecutar:
iptables -L -n
encontrábamos las cadenas vacías y las políticas predeterminadas en ACCEPT. Es decir, el servidor continuaba funcionando, pero había perdido todas las reglas de bloqueo que debían protegerlo.
Lo más desconcertante era que, después de restaurar el firewall, las reglas volvían a desaparecer pocos minutos después.
En este artículo explicamos la investigación que realizamos, las pistas que encontramos y cómo descubrimos que APF estaba ejecutando periódicamente un flush de todas las reglas de iptables.
Un script de vigilancia detectó la caída
El servidor contaba con un script propio que se ejecutaba periódicamente desde cron. Su función era comprobar el estado de diferentes servicios esenciales:
cat /root/bin/Comprueba.sh
Su contenido era similar al siguiente:
#!/bin/bash
#
# Comprueba que una serie de servicios
#
echo "Comprobando 1 de 10"
/root/bin/compruebasshd.sh
echo "Comprobando 2 de 10"
/root/bin/compruebafirewall.sh
echo "Comprobando 3 de 10"
/root/bin/compruebanamed.sh
echo "Comprobando 4 de 10"
/root/bin/compruebanamed-old.sh
echo "Comprobando 5 de 10"
/root/bin/compruebapostfix.sh
echo "Comprobando 6 de 10"
/root/bin/compruebadovecot.sh
echo "Comprobando 7 de 10"
/root/bin/compruebaccesosfallidos.sh
if [ ! -f /root/cargatrabajo.tmp ]; then
echo "Comprobando 8 de 10"
/root/bin/compruebahttpd.sh
echo "Comprobando 9 de 10"
/root/bin/compruebanginx.sh
echo "Comprobando 10 de 10"
/root/bin/compruebamysqld.sh
fi
if [ -f /root/cargatrabajo.tmp ]; then
source /root/cargatrabajo.tmp
quedan=$(echo "$quedan - 1" | bc)
echo "Quedan: $quedan - $(date) -> $A" >> "$REGISTROSLOGS"
echo "quedan=$quedan" > /root/cargatrabajo.tmp
if [ "$quedan" -le 0 ]; then
rm -f /root/cargatrabajo.tmp
/root/bin/cargatrabajo.sh
else
exit 0
fi
fi
Entre esas comprobaciones se encontraba:
/root/bin/compruebafirewall.sh
Este segundo script examinaba las reglas de iptables y buscaba alguna regla que utilizara el destino DROP:
CadenaFW=$(/sbin/iptables -L -n | grep DROP)
if [ "$CadenaFW" == "" ]; then
/sbin/service iptables restart
/sbin/service fail2ban restart
/usr/bin/chmod a+x /root/addreglasxtras.sh
/root/addreglasxtras.sh
/root/bin/addreglasfromfwnuevos.sh > /root/sec.sh
/usr/bin/chmod a+x /root/sec.sh
/root/sec.sh
echo "$(date) - SE CAYÓ EL FIREWALL" >> /root/logeaeldia.log
echo "$(date) - LEVANTANDO EL SERVICIO DEL FIREWALL" |
mail -s "$(hostname) FIREWALL CAÍDO" Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.
fi
Cuando el script no encontraba ninguna regla DROP, consideraba que el firewall estaba caído e intentaba recuperarlo.
Gracias a esta vigilancia recibíamos un correo cada vez que sucedía el problema. El script detectaba correctamente que las reglas habían desaparecido, pero todavía no sabíamos quién las estaba borrando.
¿Estaba realmente caído el servicio iptables?
Una de las primeras comprobaciones fue consultar el estado del servicio:
systemctl status iptables --no-pager -l
El resultado mostraba algo parecido a:
iptables.service - IPv4 firewall with iptables Loaded: loaded Active: active (exited)
La indicación active (exited) puede parecer un fallo, pero en iptables-services es completamente normal.
iptables no necesita mantener un proceso ejecutándose permanentemente. El servicio carga las reglas en el kernel y después termina. Las reglas continúan aplicándose aunque no exista un proceso llamado iptables.
Por este motivo, una comprobación como la siguiente no es fiable:
ps ax | grep -v grep | grep iptables
No encontrar un proceso llamado iptables no significa que el firewall esté detenido.
Una comprobación más adecuada sería:
systemctl is-active --quiet iptables
Pero incluso eso solo confirma el estado administrativo del servicio. Para asegurarnos de que existen reglas reales podemos utilizar:
iptables-save | grep -q -- '-A INPUT'
La advertencia sobre iptables-legacy
Durante la investigación también apareció este mensaje:
Warning: iptables-legacy tables present, use iptables-legacy to see them
Comprobamos que el comando iptables estaba enlazado con el frontend nftables:
readlink -f "$(command -v iptables)"
El resultado fue:
/usr/sbin/xtables-nft-multi
Sin embargo, el kernel también tenía cargadas tablas del backend antiguo:
cat /proc/net/ip_tables_names
Resultado:
mangle filter
Esto indicaba una convivencia entre tablas nftables y tablas legacy.
La advertencia no significaba por sí sola que el firewall hubiera fallado. Simplemente informaba de que existían tablas legacy que el comando actual no podía mostrar.
No obstante, al consultar las reglas visibles encontramos únicamente:
iptables -S
-P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT
En ese momento sí estaba claro que el conjunto de reglas consultado se encontraba vacío.
Las reglas guardadas seguían siendo válidas
El servidor conservaba una copia de las reglas en:
/etc/sysconfig/iptables
Comprobamos su sintaxis sin aplicarlas:
iptables-restore --test /etc/sysconfig/iptables echo $?
El código devuelto fue:
0
Por tanto, el archivo era válido y contenía las cadenas y reglas esperadas, incluidas reglas propias, cadenas de Fail2Ban y numerosos bloqueos de direcciones IP.
Podíamos restaurarlas mediante:
iptables-restore < /etc/sysconfig/iptables
Después de restaurarlas, el firewall volvía a funcionar. Sin embargo, poco después desaparecían nuevamente.
Esto demostraba que el problema no estaba en el archivo guardado, sino en algún proceso que actuaba posteriormente sobre las tablas.
La investigación con journalctl
Para descubrir qué sucedía exactamente en los minutos posteriores a la restauración utilizamos el journal de systemd:
journalctl --since "-5 minutes" --no-pager | grep -iE 'iptables|fail2ban|firewall|CROND|systemctl|service'
En los registros apareció una secuencia muy reveladora:
01:49:58 Stopping IPv4 firewall with iptables... 01:49:59 iptables: Flushing firewall rules: [ OK ] 01:49:59 Starting IPv4 firewall with iptables... 01:49:59 iptables: Applying firewall rules: [ OK ] 01:49:59 Started IPv4 firewall with iptables.
Hasta ahí, el comportamiento era el esperado: el servicio había restaurado correctamente las reglas.
Pero apenas dos segundos después apareció esta ejecución:
01:50:01 CROND root: /etc/apf/apf -f
Habíamos encontrado al responsable.
APF ejecutaba un flush cada cinco minutos
APF, o Advanced Policy Firewall, no es un firewall independiente de iptables. Es una herramienta que administra y modifica las mismas reglas de Netfilter.
Por ese motivo:
apf -l
mostraba reglas similares a:
iptables -L -n
La opción problemática era:
apf -f
La opción -f realiza un flush, es decir, vacía las reglas administradas por el firewall.
Buscamos dónde estaba programada esa ejecución:
grep -RniF '/etc/apf/apf -f' \
/etc/crontab \
/etc/cron.d \
/etc/cron.hourly \
/etc/cron.daily \
/var/spool/cron 2>/dev/null
El resultado fue:
/etc/cron.d/apf_develmode:1:*/5 * * * * root /etc/apf/apf -f >> /dev/null 2>&1
El contenido de /etc/cron.d/apf_develmode era:
*/5 * * * * root /etc/apf/apf -f >> /dev/null 2>&1
Eso significaba que APF vaciaba deliberadamente las reglas de iptables cada cinco minutos.
El ciclo era, por tanto, el siguiente:
- iptables cargaba correctamente las reglas.
- Nuestro script comprobaba que el firewall estaba funcionando.
- APF ejecutaba apf -f.
- Todas las reglas desaparecían.
- Nuestro monitor detectaba que ya no existían reglas DROP.
- El script reiniciaba iptables y Fail2Ban.
- Las reglas volvían a cargarse.
- Cinco minutos después, APF volvía a vaciarlas.
No era una caída espontánea ni una intrusión. Dos sistemas de administración estaban compitiendo sobre las mismas tablas.
Por qué existía esa tarea programada
El nombre del archivo proporcionaba una pista:
apf_develmode
APF puede utilizar un modo de desarrollo pensado para evitar que una configuración incorrecta del firewall deje al administrador sin acceso al servidor.
Cuando se trabaja remotamente mediante SSH, una regla incorrecta podría bloquear el acceso. Para reducir ese riesgo, el modo de desarrollo programa un vaciado periódico de reglas.
Es una protección útil durante una configuración inicial, pero no debe permanecer activa permanentemente en un servidor de producción.
Comprobamos la configuración con:
grep -nE '^[[:space:]]*DEVEL_MODE' /etc/apf/conf.apf
Si aparece:
DEVEL_MODE="1"
APF continúa en modo desarrollo.
Para un servidor en producción, una vez revisadas cuidadosamente las reglas, debe quedar configurado como:
DEVEL_MODE="0"
Antes de hacer este cambio hay que asegurarse de que el acceso SSH esté permitido. De lo contrario, una configuración incorrecta podría dejar el servidor inaccesible.
Cómo desactivamos el vaciado automático
La primera medida fue comentar la tarea programada:
# */5 * * * * root /etc/apf/apf -f >> /dev/null 2>&1
El archivo quedó así:
cat /etc/cron.d/apf_develmode
# */5 * * * * root /etc/apf/apf -f >> /dev/null 2>&1
Con ello cron dejó de ejecutar el vaciado cada cinco minutos.
También es necesario revisar /etc/apf/conf.apf y desactivar permanentemente el modo de desarrollo:
DEVEL_MODE="0"
De lo contrario, una futura recarga, reinstalación o actualización de APF podría volver a generar la tarea programada.
Después restauramos las reglas:
iptables-restore < /etc/sysconfig/iptables
Las guardamos nuevamente:
service iptables save
Y habilitamos su carga durante el arranque:
systemctl enable iptables
Finalmente verificamos:
systemctl is-enabled iptables systemctl is-active iptables iptables -S
Esperamos más de cinco minutos y repetimos la consulta. Las reglas continuaban cargadas, confirmando que APF era quien las eliminaba.
Mejoras necesarias en nuestros scripts de vigilancia
La investigación también nos permitió detectar algunas comprobaciones antiguas que convenía modernizar.
No comprobar iptables mediante ps
Esta comprobación no es válida:
ps ax | grep -v grep | grep iptables
iptables-services puede encontrarse correctamente como active (exited) sin mantener ningún proceso residente.
Es preferible utilizar:
systemctl is-active --quiet iptables
No depender únicamente de la palabra DROP
Buscar una regla DROP puede servir como comprobación rápida, pero no garantiza que todo el firewall esté correctamente cargado:
iptables -L -n | grep DROP
Una verificación más sólida puede comprobar la existencia de reglas concretas o contar las reglas de la cadena INPUT:
iptables-save | grep -q -- '-A INPUT'
Por ejemplo:
if systemctl is-active --quiet iptables &&
iptables-save | grep -q -- '-A INPUT'
then
echo "Firewall cargado correctamente"
else
echo "Firewall sin reglas; intentando restaurar"
if iptables-restore --test /etc/sysconfig/iptables; then
iptables-restore < /etc/sysconfig/iptables
systemctl restart fail2ban
else
echo "ERROR: /etc/sysconfig/iptables no es válido"
fi
fi
En un sistema con tablas nft y legacy mezcladas, también debe verificarse que la herramienta consultada corresponde al backend que realmente contiene las reglas.
Mejorar el archivo de bloqueo
El script utilizaba /root/fwfuncionando.tmp como semáforo para evitar ejecuciones simultáneas. Sin embargo, en la rama que recuperaba el firewall no siempre eliminaba el archivo temporal.
Puede resolverse mediante:
trap 'rm -f /root/fwfuncionando.tmp' EXIT
Una opción más robusta sería utilizar flock:
exec 9>/run/compruebafirewall.lock flock -n 9 || exit 0
Así se evita que dos instancias modifiquen simultáneamente las tablas.
Demasiados gestores para un mismo firewall
En el servidor coexistían varias herramientas capaces de modificar Netfilter:
- iptables-services.
- APF.
- Fail2Ban.
- BFD.
- DDOS Deflate.
- Imunify360.
- Scripts propios de bloqueo y monitorización.
La convivencia es posible, pero debe existir un responsable principal de la configuración.
En nuestro caso, la arquitectura más razonable es:
- iptables-services para guardar y restaurar las reglas principales.
- Fail2Ban para los bloqueos dinámicos.
- Scripts propios para incorporar reglas adicionales y verificar el estado.
- APF desactivado como gestor automático si ya no resulta necesario.
- Revisión de BFD, DDOS Deflate e Imunify360 para asegurarnos de que no recargan o vacían las tablas completas.
Cuando varias herramientas intentan administrar el mismo firewall, una recarga de cualquiera de ellas puede eliminar reglas creadas por las demás.
¿Se instaló APF junto con Maldet?
Durante la investigación observamos que el directorio de APF y el paquete descargado de Linux Malware Detect presentaban fechas prácticamente idénticas.
Ambas herramientas proceden de R-fx Networks, lo que inicialmente hizo sospechar que APF podía haberse instalado junto con Maldet.
Sin embargo, la instalación oficial actual de Linux Malware Detect no incluye APF como componente obligatorio. La coincidencia de fechas puede indicar:
- La ejecución de un instalador combinado.
- La instalación consecutiva de varias herramientas de R-fx Networks.
- Un script externo que instaló ambos productos.
- Una reinstalación o actualización realizada durante la misma sesión.
Para comprobar el paquete concreto descargado utilizamos:
grep -RniE '(/etc/apf|apf_develmode|advanced policy firewall|rfxn.*apf)' \
/root/tmp/linux-malware-detect 2>/dev/null
Por tanto, no debe atribuirse la instalación de APF a Maldet únicamente por la coincidencia de fechas sin revisar antes el instalador exacto utilizado.
Conclusión
El firewall no se caía por un fallo de iptables. APF estaba ejecutando conscientemente:
/etc/apf/apf -f
cada cinco minutos desde:
/etc/cron.d/apf_develmode
Nuestro sistema de vigilancia detectaba correctamente la desaparición de las reglas y las restauraba, pero APF volvía a vaciarlas en la siguiente ejecución programada.
La orden que permitió descubrirlo fue:
journalctl --since "-5 minutes" --no-pager | grep -iE 'iptables|fail2ban|firewall|CROND|systemctl|service'
La investigación demuestra por qué, cuando un firewall pierde sus reglas de forma periódica, no basta con restaurarlas. Hay que revisar:
- El journal de systemd.
- Las tareas de cron.
- Los gestores de firewall instalados.
- Los scripts propios.
- La convivencia entre iptables-nft e iptables-legacy.
- Los modos de desarrollo que programan vaciados preventivos.
En nuestro caso, una sola línea de cron explicaba todo el comportamiento:
*/5 * * * * root /etc/apf/apf -f
Comentada esa línea y desactivado el modo de desarrollo de APF, las reglas dejaron de desaparecer.
Desinstalar APF: una buena práctica antes de cambiar de solución
Si has instalado APF para realizar pruebas y decides volver a utilizar el firewall nativo de tu distribución, conviene eliminarlo completamente para evitar posibles conflictos.
Comprobar si APF está en ejecución
Antes de eliminar nada, verifica si el servicio está activo:
systemctl status apf
También puedes comprobar si se inicia automáticamente con el sistema:
systemctl is-enabled apf
Si el servicio está activo, deténlo y desactívalo:
sudo systemctl stop apf sudo systemctl disable apf
Eliminar los archivos instalados
APF no incorpora actualmente un script oficial de desinstalación, por lo que basta con eliminar los archivos que instaló:
sudo rm -rf /etc/apf sudo rm -f /usr/local/sbin/apf sudo rm -f /usr/local/sbin/fwmgr sudo rm -f /etc/systemd/system/apf.service sudo systemctl daemon-reload sudo rm -f /etc/cron.d/apf sudo rm -f /etc/bash_completion.d/apf sudo rm -f /etc/logrotate.d/apf sudo rm -f /usr/share/man/man8/apf.8.gz sudo mandb
Si descargaste el código fuente únicamente para instalarlo, también puedes eliminar el directorio donde lo clonaste:
rm -rf /root/tmp/advanced-policy-firewall
Comprueba el estado de firewalld
Existe un detalle importante que muchos administradores pasan por alto.
Durante la instalación, APF detecta si firewalld o ufw están activos y, en caso afirmativo, puede detenerlos y deshabilitarlos para evitar conflictos, ya que APF gestiona directamente las reglas de iptables.
Por ello, una vez eliminado APF es recomendable comprobar el estado de firewalld:
systemctl status firewalld systemctl is-enabled firewalld
Si deseas volver a utilizar el firewall estándar de RHEL, AlmaLinux, Rocky Linux o Fedora, simplemente actívalo de nuevo:
sudo systemctl enable --now firewalld
Una recomendación
Antes de instalar cualquier herramienta de seguridad conviene evaluar si realmente aporta alguna ventaja respecto a las soluciones incluidas en la distribución.
APF fue una herramienta excelente durante muchos años y marcó una época en la administración de servidores Linux. Sin embargo, en sistemas actuales suele ser más recomendable utilizar firewalld, nftables o el firewall integrado del panel de administración, manteniendo APF únicamente por motivos de compatibilidad con entornos heredados.
Eliminando el aviso «iptables-legacy tables present»
Tras desinstalar APF es posible que, al ejecutar cualquier comando de iptables, aparezca un mensaje similar al siguiente:
# Warning: iptables-legacy tables present, use iptables-legacy to see them
Este aviso suele generar bastante confusión, ya que puede aparecer incluso cuando APF ya no está instalado y el sistema utiliza el backend moderno basado en nf_tables.
En realidad, el problema no reside en iptables, sino en que todavía permanecen cargadas en el kernel algunas tablas heredadas (legacy) creadas durante la ejecución de APF u otras herramientas antiguas de administración del cortafuegos.
Podemos comprobar fácilmente si existen tablas heredadas:
cat /proc/net/ip_tables_names
Si el resultado muestra algo parecido a:
mangle filter
significa que todavía existen tablas iptables-legacy activas en memoria.
En nuestro caso, el responsable era el módulo del kernel iptable_mangle, que permanecía cargado aunque APF ya hubiera sido desinstalado.
Podemos comprobar qué módulos siguen activos mediante:
lsmod | grep iptable
La solución consiste en descargar dichos módulos del kernel:
sudo modprobe -r iptable_mangle sudo modprobe -r iptable_filter
Una vez descargados, comprobamos de nuevo el estado:
cat /proc/net/ip_tables_names
Si el comando ya no devuelve ninguna salida, significa que las tablas heredadas han desaparecido correctamente.
A partir de ese momento, el aviso:
Warning: iptables-legacy tables present
dejará de aparecer al utilizar iptables.
Importante
Si algún módulo no puede descargarse porque está siendo utilizado (
Module ... is in use), conviene comprobar previamente que ningún servicio (APF, CSF, firewalld u otro firewall) continúe utilizándolo antes de forzar su eliminación. En algunos casos, un simple reinicio del servidor también elimina definitivamente las tablas heredadas si ya no existe ningún servicio que vuelva a cargarlas.
Este comportamiento no es un fallo de iptables, sino una consecuencia de mantener cargados en memoria módulos del antiguo subsistema xtables, aun cuando el sistema ya trabaja con el backend moderno basado en nf_tables.
-
Artículos
- Cómo descubrimos que APF vaciaba las reglas de iptables cada cinco minutos
- Cómo detectar archivos PHP sospechosos, malwares y posibles webshells con Auditor Web
- Script "auditor-web.sh" Para detectar malware
- ¿DNI para acceder a Internet? Ursula von der Leyen (UE) ya no quiere una Internet libre como la conocemos
- Automatiza tareas en Linux con n8n (IV): Inteligencia Artificial, agentes autónomos y el futuro de la administración de sistemas
- Vulnerabilidad crítica en Helix Ultimate y SP Page Builder para Joomla 3: cómo comprobar si tu sitio ha sido comprometido y cómo limpiarlo
- Automatiza tareas en Linux con n8n (III): casos reales para administradores de sistemas y DevOps
- Automatiza tareas en Linux con n8n (II): primeros pasos y tres automatizaciones prácticas para empezar (2A)
- Automatiza tareas en Linux con n8n (II): primeros pasos y tres automatizaciones prácticas para empezar (2B)
- China asume que no habrá un nuevo baby boom y apuesta por la "economía plateada": IA, robótica y tecnología para una sociedad cada vez más longeva
- De modelo erótica a presunta saboteadora del Nord Stream: la increíble historia de "Freya" que sacude Europa
- Herramientas imprescindibles para desarrollar aplicaciones modernas en Linux



