Baneos persistentes con Fail2Ban 2018
Fase 1: Recolección de IP
Lo primero de todo es construirnos nuestra base de datos de direcciones IP, que cumplan al menos 2 requisitos:
1. Nos hayan atacado alguna vez.
2. No sean de España para no perder visitas en el intento de protegernos.
Esta base de datos la usaremos en la Fase 2: Todo esto para qué? Integración con fail2ban.
La fuente son los logs y/o fuentes de terceros que posiblemente también se alimenten de logs; yo voy a usar los que me proporcionan los siguientes programas:
- Fail2ban: un sistema de baneo de IP en base al estudio que hace de los logs de los diferentes servicios (ssh, http…) y expresiones regulares ¿No lo conoces? pues lee VPS Ubuntu: Configuración sobre un dominio 2018 donde se securiza un vps con fail2ban.
- Logwatch: Una vez al día recibo resumen de actividad en el servidor
- Wordfence: conocido plugin WordPress de seguridad
Las fuentes pueden ser también ficheros /etc/hosts.deny que obtengamos de otros servidores, bases de datos de fuentes abiertas, etc…
Un fichero de este tipo de fuente puede ser así:
This email was sent from your website "SOSpedia" by the Wordfence plugin at Friday 2nd of February 2018 at 03:05:08 AM The Wordfence administrative URL for this site is: https://sospedia.net/wp-admin/admin.php?page=Wordfence Wordfence has blocked IP address 217.147.169.235. The reason is: "Exceeded the maximum number of page not found errors per minute for humans.". User IP: 217.147.169.235 User hostname: 217.147.169.235 User location: Ukraine
De toda la información, en el proyecto que nos ocupa, nos interesan las direcciones IP, de forma rápida podemos expraer las IP de una cadena con una expresión regular como esta:
“((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9] |[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9] |[01]?[0-9][0-9]?))”
Nos vamos a hacer un script al que le digamos una carpeta donde encontrar ficheros de este tipo, en este ejemplo resulta ser la bandeja de entrada de un buzon que recibe este tipo de correos:
#ipeses.sh egrep -o -R -h -s "((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9] |[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9] |[01]?[0-9][0-9]?))" $1 |sort|uniq > ips.dat
En $1 se espera una carpeta, aunque si le pasamos un fichero también lo procesará, de ahí el parámetro Recursivo. Como véis va a generar un fichero llamado ips.dat.
Un ejemplo de ejecución seria este:
./ipeses.sh /var/vmail/joseblanco.pro/p/o/s/postmaster/Maildir/.Conocimiento.f2b/
Ahora vamos a extraer o quitar las IP procedentes de España, algún falso positivo por consumo excesivo, quizá un script kiddie, para ello usaremos Python, y las librerias Geoip.
El script que realizará el trabajo es este:
#!/usr/bin/env python #geoip.py import os import GeoIP import pyparsing as pp import socket import re log_path = 'ipeses.dat' if os.path.exists(log_path): log = open(log_path, 'r') else: log = open('/var/log/messages', 'r') os.remove("ip.blacklist") os.remove("ipeses.info") geo = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE) octet = pp.Word(pp.nums, min=1, max=3) ip_matcher = pp.Combine(octet + ('.' + octet) * 3) file1 = open("ip.blacklist","w") file2 = open("ipeses.info","w") jail="sshd" action="DROP" for line in log: match = ip_matcher.searchString(line) if match: esip = re.search(r'((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9] |[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9] |[01]?[0-9][0-9]?))', line) if esip: ip = match.pop()[0] code = geo.country_code_by_addr(ip) name = geo.country_name_by_addr(ip) hostname = "" try: hostname = socket.gethostbyaddr(ip)[0] except socket.error: hostname = "NOT FOUND" pass if code != "ES": print "%s:%s:%s:%s" % (code, name, hostname, ip) file2.write("%s:%s:%s:%s\n" % (code, name, hostname, ip)) file1.write("%s:%s:%s\n" % (jail, ip, action)) file1.close() file2.close()
Cuya salida produce algo así:
Si generamos ipeses.info con la salida anterior (puede tardar horas en generarse, por cada IP ha de realizar una consulta a una base de datos local, y una consulta DNS para intentar averiguar el nombre de host), podemos generar un fichero ip.blacklist para alimentar fail2ban en el siguiente apartado. Por ejemplo, nos quedamos solo con la IP:
cat ipeses.info|cut -f2 -d:|cut -f2 -d,
Pero si esto lo “enbuclamos” podemos generar un fichero del tipo:
apache-badbots:115.160.171.42:REJECT --reject-with icmp-port-unreachable apache-postflood:94.76.82.164:REJECT --reject-with icmp-port-unreachable apache-badbots:194.87.147.74:REJECT --reject-with icmp-port-unreachable apache-postflood:89.218.84.214:REJECT --reject-with icmp-port-unreachable
Con el siguiente Script:
cat ipeses.info|cut -f2 -d:|cut -f2 -d, > soloips.tmp jail="sshd" accion="DROP" while read ip do echo $jail:$ip:$accion done < soloips.tmp rm soloips.tmp
Fase 2: Integración con fail2ban
Por lo que respecta a fail2ban genero el fichero ip.blacklist, que en cada reinicio del servicio fail2ban, carga haciendo persistente la lista de baneos, todas las IP’s contra el filtro ssh que lo he hecho de amplio espectro utilizando un rango de puertos 0:65535 (todos).
Los fichero de fail2ban a tocar para cargar una lista de IP bloqueadas en el reinicio del servicio, o del propio servidor, son estos:
jail.local
Aunque existe otro fichero /etc/fail2ban/jail.conf, se utiliza para opciones globales, si no existe jail.local podemos partir de una copia de este otro para personalizar al gusto. Tanto en uno como en otro existirá una sección [DEFAULT] y varias secciones, tantas como jaulas, por ejemplo la siguiente para monitorizar los LOGS del servicio SSH:
[sshd] enabled = true filter = sshd action = iptables[name=ssh, port="0:65535", protocol=tcp] %(mta)s[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"] logpath = /var/log/auth.log
En la sección [DEFAULT] podemos especificar parámetros como número de intentos erróneos antes del bloqueo, tiempo de bloqueo, una lista blanca de IP’s, una dirección de email donde lleguen avisos (destemail), etc… Para mandar un email cada vez que se bloquee una IP, interesante en modo depuración hay que incluir en la action de jail.local algo así:
%(mta)s[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
Cada jaula que configuremos, al menos las que tengamos enabled, tendrán asociados 2 ficheros más, un filtro donde se recogen las expresiones regulares de los intentos de ataque (FAILREGEX), y las expresiones regulares de las consideradas exclusiones (IGNOREREGEX), uno en la carpeta filters.d/ y otro fichero que define la acción a desencadenar cuando ocurra algo en la carpeta actions.d/, que definirá que hacer cuando:
1) Se inicie la jaula
2) Se bloquee una IP
3) Se des-bloquee una IP
4) Se pare la jaula
El fichero a editar en filters.d/ viene dado por el nombre que tiene entre corchetes la jaula en jail.local y el del action.d/ por el del parámetro action de jail.local para esta jaula. Así pues en este caso podríamos tocar cosas de action.d/iptables-multiport.conf y de filter.d/sshd.conf para afinar nuestra jaula [sshd].
action.d/iptables-multiport.conf
Aquí se modifican las acciones start, ban y unban, usando el fichero ip.blacklist para conseguir la persistencia entre reinicios del servidor.
actionstart = iptables -N fail2ban-<name> iptables -A fail2ban-<name> -j RETURN iptables -I <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name> cat /etc/fail2ban/ip.blacklist|grep <name> | while IFS=: read Servicio IP Accion; do iptables -I fail2ban-$Servicio 1 -s $IP -j $Accion ; done ... actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype> echo <name>:<ip>:<blocktype> >> /etc/fail2ban/ip.blacklist ... actionunban = iptables -D fail2ban-<name> -s <ip> -j <blocktype> echo <name>:<ip>:<blocktype> >> /etc/fail2ban/ip.blacklist.unbaned
Me pareció también interesante registrar los “unbaneos” en otro fichero ip.blacklist.unbaned.
Ficheros que hemos tocado:
jail.local (Debian en /etc/fail2ban/)
iptables-multiport.conf (Debian en /etc/fail2ban/action.d/)
Luego tocaremos también el filtro asociado a esta misma jaula [sshd] y editaremos el fichero filter.d/sshd.conf para optimizar un poco el rendimiento de la jaula, al excluir del análisis funcionamiento normal del servidor, que provoca registros en los logs por parte de demonios del sistema.
RECORDAD: Cada vez que toquemos la configuración de fail2ban podemos, como servicio que es:
service fail2ban restart
O bien stop | status | start
En algunas distros deberéis hacer:
/etc/init.d/fail2ban status
RECORDAD: Pero si lo que habéis tocado es una jaula, podéis recargar solo esa jaula:
fail2ban-client reload sshd
iptables
Puedes hacerte una idea muy práctica aquí: Manual práctico de iptables
Ahora comentamos las que aparecen en el fichero iptables-multiport.conf:
En el action start del fichero usamos estas:
actionstart = iptables -N fail2ban-<name> iptables -A fail2ban-<name> -j RETURN iptables -I <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name> cat /etc/fail2ban/ip.blacklist|grep <name> | while IFS=: read Servicio IP Accion; do iptables -I fail2ban-$Servicio 1 -s $IP -j $Accion ; done
En el bucle, para añadir todas las IP del fichero de datos ip.blacklist añadimos una iptable como esta:
iptables -I fail2ban-$Servicio 1 -s $IP -j $Accion
En el action ban usamos estas:
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype> echo <name>:<ip>:<blocktype> >> /etc/fail2ban/ip.blacklist
Para que haya persistencia añadimos al final del fichero ip.blacklist la nueva ip baneada y el servicio (name) que lo provocó.
Aquí <name>, <blocktype> e <ip> son variables que pone a nuestra disposición el framework de fail2ban y que podemos usar a nuestro antojo.
Por último, en el action unban usamos estas:
actionunban = iptables -D fail2ban-<name> -s <ip> -j <blocktype> echo <name>:<ip>:<blocktype> >> /etc/fail2ban/ip.blacklist.unbaned
Además de borrar la regla iptable (iptable -D [Delete]), si queremos tener un histórico de las IP que el propio fail2ban “baneó” y pasado el tiempo “unbaneó” (parámetro bantime en jail.local o incluso a nivel de jaula).
Banear / Unbanear IP’s de forma manual
En las versiones 0.8x de fail2ban no funciona el cliente en este sentido:
fail2ban-client set ssh-iredmail banip A.B.C.D
Lo haremos usando el propio comando iptables.
Si nos fijamos en las reglas para banear y unbanear de los ficheros de configuración de fail2ban tenemos:
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
y
actionunban = iptables -D fail2ban-<name> -s <ip> -j <blocktype>
Por ello podemos deducir que:
- Para banear una ip A.B.C.D
iptables -I fail2ban-ssh 1 -s A.B.C.D -j DROP
- Para unbanear una ip A.B.C.D
iptables -D fail2ban-ssh -s A.B.C.D -j DROP
El comando iptables I (Inserta) un DROP en la cadena (CHAIN) fail2ban-ssh, o bien D (Delete), aquellas reglas que tengan como -s (Source) la ip A.B.C.D.
Listado de iptables
Para conocer las cadenas iptables podéis usar varios comandos:
iptables -L -n
Otro que uso mucho es
iptables -S
Es conveniente utilizar la opción -n de lo contrario se hará resolución de nombres lo que ralentizará considerablemente la respuesta.
Probad si queréis (se puede detener con CTLR+C):
iptables -L
Podemos obtener un listado con números de línea así:
iptables -vnL --line-numbers
Con este otro puedes inspeccionar en tiempo real lo que pasa en tu servidor:
watch -d 'iptables -vnL --line-numbers'
Chains & Jails
El cliente fail2ban utiliza el concepto de jaula (JAIL) en lugar de cadena (CHAIN).
Se puede ver el estado de jaulas activas así:
fail2ban-client status
Donde se apreciará que jaulas tenemos activas (enabled=true en jail.local):
root@server:~# fail2ban-client status Status |- Number of jail: 9 `- Jail list: roundcube, apache-noscript, apache-badbots, ssh, apache, postfix, dovecot, apache-phpmyadmin, apache-overflows
Podéis interrogar por una jaula en concreto así:
root@server:~# fail2ban-client status ssh Status for the jail: ssh |- filter | |- File list: /var/log/auth.log | |- Currently failed: 0 | `- Total failed: 1 `- action |- Currently banned: 0 | `- IP list: `- Total banned: 0
Optimizar los filtros fail2ban
Podemos optimizar los filtros evitando analizar actividad normal en el servidor, como los inicios de sesión de los demonios.
Para ello y centrándonos como ejemplo en la jaula SSH con la que estamos jugando hoy, echaremos un vistazo al filtro sshd.conf, para lo que editaremos el fichero:
nano /etc/fail2ban/filter.d/sshd.conf
E incluiremos esta lista de expresiones regulares a obviar:
ignoreregex = : pam_unix\((cron|sshd|systemd-user):session\): session (open|clos)ed for user (daemon|munin|postgres|root)( by \(uid=0\))?$ : Successful su for (postgres) by root$ New session \d+ of user (postgres)\.$ Removed session \d+\.$
Para aplicar cambios sin hacer restart del servicio fail2ban:
fail2ban-client reload sshd
Aquí recordad se utiliza el nombre de la jaula, lo que sale al ejecutar fail2ban-client status).
Testear una jaula
Podemos testear una jaula / filtro fail2ban mediante la utilidad fail2ban-regex, con este comando por ejemplo:
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf /etc/fail2ban/filter.d/sshd.conf | less
Donde según man fail2ban-regex, le estamos diciendo que testee el fichero de log /var/log/auth.log (inicios de sesión), usando como expresión regular FAIL que contiene el fichero de configuración /etc/fail2ban/filter.d/sshd.conf, y aunque es opcional lo ponemos, usando como expresión regular IGNORE la que contiene el mismo fichero /etc/fail2ban/filter.d/sshd.conf.
Si queremos testear alguna jaula de apache:
fail2ban-regex /var/log/apache2/error.log /etc/fail2ban/filter.d/apache.conf /etc/fail2ban/filter.d/apache.conf
Ahora el log está en /var/log/apache2/error.log, y la configuración de expresiones regulares en /etc/fail2ban/filter.d/apache.conf
Chuleta fail2ban
service fail2ban restart | stop | status | start
O bien (según distro):
/etc/init.d/fail2ban restart | stop | status | start
Para hacer cosas con el servicio, como pararlo.
fail2ban-client reload sshd
Donde sshd nombre de jaula, la vuelve a cargar con la nueva configuración.
fail2ban-client set ssh-iredmail banip A.B.C.D
Para bloquear (ban) una IP en versiones 9.x o superiores.
fail2ban-client set ssh-iredmail unbanip A.B.C.D
Para des-bloquear una IP en versiones 9.x o superiores.
fail2ban-client status
Ver el estado de jaulas activas, sí funciona en versiones inferiores a 0.9.x
fail2ban-client status ssh
Estado de la jaula concreta sshd.
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf /etc/fail2ban/filter.d/sshd.conf
Testea filtros de fail2ban
Chuleta iptables
iptables -L -n
iptables -L
iptables -vnL --line-numbers
iptables -S
Lista iptables de diferentes formas.
watch -d 'iptables -vnL --line-numbers'
Monitorizar iptables en tiempo real.
iptables -D fail2ban-ssh -s A.B.C.D -j DROP -> "Des-Banea" la IP A.B.C.D
Quita un bloqueo / unban.
iptables -I fail2ban-ssh 1 -s A.B.C.D -j DROP
Banea / bloquea una IP.
//## Editado 2018:
- El proceso de extracción de IP ahora se realiza con el comando egrep y una expresión regular procesando carpetas de forma recursiva.
- Todo el proceso de geolocalizacion y generacion de ficheros para alimentar fail2ban y la base de datos ipeses.info se han pasado a un solo script Python.
Espero que sirva!
También te puede interesar:
- https://sospedia.net/iptables/
- https://sospedia.net/iptables-borrar-una-regla-unban-ip/
- https://sospedia.net/tutorial-basico-de-iptables/
- https://sospedia.net/vps-ubuntu-configuracion-sobre-un-dominio-2018/
- https://sospedia.net/script-php-para-obtener-los-dominios-que-nos-han-atacado/
- https://sospedia.net/fail2ban-iptables/
Pingback: Bonus Pack 2017: Recopilación de 80 artículos – SOSpedia
Pingback: Bonus Pack 2017: Recopilación de 80 artículos – SOSpedia
Pingback: Configuración de un VPS Linux sobre un dominio usando Debian 8 – SOSpedia
Pingback: Bonus Pack 2018: Recopilación de artículos de sospedia.net – Jose Blanco Vega