Sustituir fail2ban por el agente para Linux
Cómo pasa un host de fail2ban al agente sin interrupción: primero comprobar qué posee ya los objetos del cortafuegos, ejecutar los dos un tiempo, después desactivar fail2ban de forma definitiva, y cómo volver atrás en segundos si hace falta.
Sustituir fail2ban
El agente no se pone al lado de fail2ban, lo releva. fail2ban sigue disponible como una fuente entre muchas, así que un host que todavía lo usa sigue notificando sus bloqueos, y un host sin fail2ban no pierde nada: cada tipo de ataque que antes llegaba por un jail tiene ahora un detector que lee el registro original.
Medido durante once días en un host de producción: el agente encontró 187 de las 189 direcciones que
fail2ban había bloqueado en la misma máquina, las dos que faltaban eran direcciones de prueba metidas
a mano, notificó 78 direcciones más que se habían quedado por debajo de los umbrales de fail2ban, y
acertó los bloqueos de FTP tres de tres. La escalada para reincidentes sale del historial de bloqueos
propio del agente, y reportedip-agent test <file> ocupa el lugar de
fail2ban-regex.
Comprobar primero si algo ya posee estos nombres
Esto costó una tarde en una flota de producción, así que va antes que todo lo demás. Un script de
cortafuegos escrito a mano, del tipo que este sitio documentaba antes, puede usar
exactamente los nombres que usa el agente. Encontrados en el campo: los conjuntos
rip-whitelist, rip-ssh, rip-mail, rip-web,
rip-ftp y rip-edge, más una cadena rip-blacklist colgada con
-A INPUT -j rip-blacklist, reconstruida cada hora por una tarea de cron, y casi carácter
por carácter la cadena que el agente construye él mismo.
En un host así el orden habitual es erróneo en las dos direcciones. La primera sincronización del
agente reconstruye la cadena y sustituye las reglas DROP existentes. En un host instalado
con --mode log las sustituye por reglas LOG, lo que deja al host sin
protección hasta que la tarea de cron antigua vuelva a ejecutarse, es decir hasta una hora más tarde. Y cuando esa tarea se ejecuta, reconstruye la cadena a
su manera y retira de paso la regla rip-local del agente. Dos programas, una cadena, y
cada uno deshaciendo el trabajo del otro cada hora.
Comprueba por tanto antes de la primera sincronización, y si los nombres chocan, no instales este host con
--mode log. Instala el agente, que no toca ninguna regla, importa la lista
blanca antigua, desactiva la tarea de cron antigua, y solo después lanza la primera sincronización
con mode: drop, el valor por defecto. Una fase de observación con --mode log
tiene su sitio en los hosts donde no chocan nombres.
# 1. Does something already own these names?
ipset list -n | grep "^rip-"
iptables -S INPUT | grep rip-
iptables -S rip-blacklist 2>/dev/null | head
crontab -l | grep -iE "ipset|blacklist|reportedip"
ls -l /etc/cron.d/ /etc/cron.hourly/ 2>/dev/null
# 2. If yes, take the old whitelist over FIRST. The feed can be
# downloaded again; that list cannot.
ipset list rip-whitelist | sed -n "/^Members/,\$p" | tail -n +2 > /tmp/rip-wl
ipset list rip-whitelist-v6 | sed -n "/^Members/,\$p" | tail -n +2 >> /tmp/rip-wl
wc -l /tmp/rip-wl
while read -r a; do
[ -n "$a" ] && reportedip-agent whitelist add "$a" "imported from the old script"
done < /tmp/rip-wl
reportedip-agent whitelist list | wc -l
# 3. Disable the old job, then hand the chain over in one step.
crontab -l | grep -v blacklist | crontab - # or: rm /etc/cron.d/<job>
# Only if the agent was installed with --mode log:
sed -i "s/^mode: log/mode: drop/" /etc/reportedip-agent/config.yaml
reportedip-agent sync
reportedip-agent status
reportedip-agent whitelist list.
Ejecutar los dos un tiempo
Ejecutar los dos es seguro y es la forma sensata de migrar. El agente nunca escribe en
/etc/fail2ban, y fail2ban no sabe nada de los conjuntos rip-, así que los
dos usan cadenas separadas y estados separados. Dos reglas que descartan la misma dirección cuestan
una comparación de paquete.
Lo único que no conviene hacer es notificar el mismo evento dos veces. Si conservas la acción de fail2ban que envía los bloqueos por HTTP y configuras fail2ban como fuente del agente, cada bloqueo sale del host dos veces y se paga dos veces de tu cuota diaria. Elige el agente o la acción.
Instala con REPORTEDIP_FAIL2BAN_SOURCE=0 cuando estés sustituyendo
fail2ban. El instalador detecta un fail2ban en marcha y lo escribe como fuente, lo que es
correcto para un host que lo conserva y erróneo para una migración: cuando fail2ban desaparece,
desaparece también el registro al que apunta esa fuente. La variable deja la fuente fuera, y sin
ella la instalación imprime una línea que dice que la fuente está ahí y qué hacer con ella tras la
retirada. En un host ya instalado, quita a mano la entrada fail2ban de
sources y después para y arranca el servicio watch.
# What did fail2ban ban, and what does the agent make of the
# same logs? Same file, same window, two verdicts.
fail2ban-client status sshd
reportedip-agent test /var/log/auth.log
# Per jail, then the agent over the file behind it
for j in $(fail2ban-client status | sed -n "s/.*Jail list:\t*//p" | tr -d " " | tr "," " "); do
echo "== $j"; fail2ban-client status "$j" | grep -E "Total banned|Currently banned"
done
reportedip-agent status | sed -n "/^sources:/,/^queue:/p"
Desactivar fail2ban de forma definitiva
Hazlo cuando hayas comparado los dos durante unos días y la lista de fuentes del agente cubra cada jail que tenías.
# 1. Stop it and keep it stopped.
systemctl stop fail2ban
systemctl disable fail2ban
# 2. Remove the fail2ban source from the agent config, then stop
# and start the daemon. A restart is not enough for a removed
# source: the daemon keeps tailing the old file.
$EDITOR /etc/reportedip-agent/config.yaml
systemctl stop reportedip-agent.service
systemctl start reportedip-agent.service
# 3. Check what fail2ban left in the kernel, in both address
# families. Stopping it does not always clean up: a jail that
# once used an iptables action leaves old-style f2b- chains behind.
nft list tables | grep f2b || echo "no f2b table"
iptables -S | grep "f2b-" || echo "no f2b chain (v4)"
ip6tables -S | grep "f2b-" || echo "no f2b chain (v6)"
ipset list -n | grep f2b || echo "no f2b set"
# 4. Remove the leftovers from the RUNNING ruleset. The jump rule
# carries -p and --dports, so it is deleted by its full text; a
# plain -D INPUT -j f2b-sshd does not match it, and the chain then
# refuses to go ("Device or resource busy"). Measured on a fleet:
# the v6 leftover was the one that stayed.
nft delete table inet f2b-table 2>/dev/null
for T in iptables ip6tables; do
$T -S INPUT | grep "f2b-" | sed "s/^-A INPUT //" | while read -r rule; do eval "$T -D INPUT $rule"; done
for c in $($T -S | awk "/^-N f2b-/{print \$2}"); do $T -F "$c" && $T -X "$c"; done
done
# 5. And from the SAVED one, or they come back at the next boot.
# Save without the rip- lines as well: the agent rebuilds its chain
# at every sync, and a saved rule that names a rip- set which does
# not exist yet at boot makes iptables-restore fail as a whole.
iptables-save | grep -v -e "f2b-" -e "rip-" > /etc/iptables/rules.v4
ip6tables-save | grep -v -e "f2b-" -e "rip-" > /etc/iptables/rules.v6
# 6. Confirm the agent is happy without it.
reportedip-agent status | grep -i fail2ban
reportedip-agent status; echo "exit $?"
f2b- solo del juego de reglas en marcha, netfilter-persistent las
restaura en cada arranque desde /etc/iptables/rules.v4 y desde
/etc/iptables/rules.v6. Vuelven vacías, así que no descartan nada y parecen
inofensivas, y dentro de un año seguirán ahí desconcertando a quien lea el juego de reglas
después. Revisa los dos archivos: en un host los restos estaban solo en el archivo v6, y quien
busca únicamente en rules.v4 pasa de largo. Limpia primero el juego de reglas en
marcha, luego guárdalo, y compruébalo después de un reinicio.
Una vuelta atrás es un comando y tarda segundos, porque el agente nunca tocó
/etc/fail2ban: systemctl enable --now fail2ban devuelve cada jail y su
propia tabla, y el agente no se ve afectado. Medido en un host en producción, los once jails habían
vuelto en ocho segundos.
Última actualización: · Mantenido por el equipo de ReportedIP