Hola, vamos a aprender cómo podemos desplegar Fail2ban. Fail2ban es una herramienta que protege los servidores de ataques de fuerza bruta mediante el bloqueo de direcciones IP que intentan acceder al servidor de forma repetida. En concreto vamos a implementar este contenedor: https://github.com/crazy-max/docker-fail2ban
Lo primero que tenemos que hacer es crear esta estructura de carpetas:
Ahora vamos a crear el archivo con la magia docker-compose.yml
version: "3.1" services: fail2ban: image: crazymax/fail2ban:latest container_name: fail2ban network_mode: "host" cap_add: - NET_ADMIN - NET_RAW volumes: - "./config/fail2ban/data:/data" - "./config/fail2ban/logs:/var/log:ro" - "./config/fail2ban/custom_configs:/etc/fail2ban/custom-config:ro" #Para sshd - ${PATH_DATA_LOGS_SSHD}:/var/data/sshd-logs #Para npm - ${PATH_DATA_LOGS_NPM}:/var/data/npm-logs env_file: - "./env/.fail2ban.env" restart: always
Ahora voy a explicar el docker compose:
Este código es un archivo de configuración de Docker Compose que define un servicio llamado «fail2ban».
En la sección «services», se define el servicio «fail2ban» que utiliza la imagen «crazymax/fail2ban:latest». Esta imagen contiene la herramienta Fail2ban y se descarga automáticamente desde Docker Hub.
El servicio se ejecuta en modo host, lo que significa que utiliza la red del host en lugar de una red aislada de Docker. Esto es necesario para que Fail2ban pueda acceder a los registros del sistema y bloquear las direcciones IP.
El servicio también tiene permisos de administrador de red y acceso a paquetes de red crudos. Esto es necesario para que Fail2ban pueda leer los registros del sistema y bloquear las direcciones IP.
En la sección «volumes», se definen varios volúmenes que se montan en el contenedor. Estos volúmenes se utilizan para almacenar los registros de sshd y npm, así como para almacenar los datos de configuración de Fail2ban.
En la sección «env_file», se carga el archivo de variables de entorno «.fail2ban.env» en el contenedor. Este archivo contiene variables de entorno que se utilizan para configurar Fail2ban.
Además ahora vamos a definir un .env para cargar las variables del contenedor Docker ${PATH_DATA_LOGS_SSHD} y ${PATH_DATA_LOGS_NPM}
Por último, en la sección «restart», se especifica que el servicio se reiniciará automáticamente en caso de fallo.
En resumen, este archivo de configuración de Docker Compose define un servicio de Fail2ban que se ejecuta en modo host y tiene acceso a los registros del sistema y permisos de administrador de red. También se definen varios volúmenes y se carga un archivo de variables de entorno para configurar Fail2ban.
Ahora vamos a crear el archivo .fail2ban.env
TZ=Europe/Paris F2B_LOG_TARGET=STDOUT F2B_LOG_LEVEL=INFO F2B_DB_PURGE_AGE=1d SSMTP_HOST=smtp.example.com SSMTP_PORT=587 SSMTP_HOSTNAME=example.com SSMTP_USER=smtp@example.com SSMTP_PASSWORD= SSMTP_TLS=YES
Recuerda indicar tus datos SSMTP (solo si quieres utilizar SSMT para recibir correos), por el momento no es necesario añadirlo.
Ahora nos creamos nuestro .env
#Logs de sistema: PATH_DATA_LOGS_NPM=/data/logs PATH_DATA_LOGS_SSHD=/var/logs
En el .env tenemos que localizar dos directorios:
- El directorio de NPM (Nginx Proxy Manager) PATH_DATA_LOGS_NPM (suele ser): /data/logs
- El directorio de SSH PATH_DATA_LOGS_SSHD (dónde tiene auth.log el sistema https://devcodelight.com/?p=6283&preview_id=6283&pre): /var/logs
Una vez configurado vamos a empezar a añadir las reglas:
Para SSH:
- Añadimos en la carpeta config/fail2ban/data/jail.d el archivo sshd.conf
[sshd] enabled = true chain = INPUT port = ssh filter = sshd logpath = /var/data/sshd-logs/auth.log maxretry = 5
Podemos activar o desactivar con enabled. Aplicamos el filtro por defecto para ssh y el logpath está apuntando al directorio de docker-compose definido en el docker-compose.yml y el .env
Para Nginx Proxy manager (NPM) (esta configuración la reciclo de este tutorial (https://forum.openmediavault.org/index.php?thread/49480-nginx-proxy-manager-with-fail2ban-guide/):
- Añadimos en la carpeta config/fail2ban/data/jail.d el archivo npm.conf
[npm-docker] enabled = true ignoreip = 127.0.0.1/8 #192.168.2.0/24 #Set ignored ranges chain = INPUT action = iptables-nft[type=allports, chain=DOCKER-USER] logpath = /var/data/npm-logs/default-host_*.log /var/data/npm-logs/proxy-host-*.log maxretry = 3 bantime = 60 findtime = 300
Con esto indicamos que no tenga en cuenta las ips locales.
Aplicamos una acción que vamos a definir para que tenga en cuenta, llamada iptables-nft
Indicamos los logpath del Docker.
- Ahora vamos a crear la accion, para ello añadimos en la carpeta config/fail2ban/data/filter.d el archivo npm-docker.conf
[INCLUDES] [Definition] failregex = ^<HOST>.+" (4\d\d|3\d\d) (\d\d\d|\d) .+$ ^.+ 4\d\d \d\d\d - .+ \[Client <HOST>\] \[Length .+\] ".+" .+$
Aquí definimos el patrón dónde va a localizar errores e identificar la IP que debe bloquear.
- Ahora vamos a crear la acción, para ello añadimos en la carpeta config/fail2ban/data/action.d el archivo iptables-nft.conf
## Version 2022/08/06iptables-nft # Fail2Ban configuration file # # Authors: Sergey G. Brester (sebres), Cyril Jaquier, Daniel Black, # Yaroslav O. Halchenko, Alexander Koeppe et al. # [Definition] # Option: type # Notes.: type of the action. # Values: [ oneport | multiport | allports ] Default: oneport # type = oneport # Option: actionflush # Notes.: command executed once to flush IPS, by shutdown (resp. by stop of the jail or this action) # Values: CMD # actionflush = <iptables-nft> -F f2b-<name> # Option: actionstart # Notes.: command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false). # Values: CMD # actionstart = { <iptables-nft> -C f2b-<name> -j <returntype> >/dev/null 2>&1; } || { <iptables-nft> -N f2b-<name> || true; <iptables-nft> -A f2b-<name> -j <returntype>; } <_ipt_add_rules> # Option: actionstop # Notes.: command executed at the stop of jail (or at the end of Fail2Ban) # Values: CMD # actionstop = <_ipt_del_rules> <actionflush> <iptables-nft> -X f2b-<name> # Option: actioncheck # Notes.: command executed once before each actionban command # Values: CMD # actioncheck = <_ipt_check_rules> # Option: actionban # Notes.: command executed when banning an IP. Take care that the # command is executed with Fail2Ban user rights. # Tags: See jail.conf(5) man page # Values: CMD # actionban = <iptables-nft> -I f2b-<name> 1 -s <ip> -j <blocktype> # Option: actionunban # Notes.: command executed when unbanning an IP. Take care that the # command is executed with Fail2Ban user rights. # Tags: See jail.conf(5) man page # Values: CMD # actionunban = <iptables-nft> -D f2b-<name> -s <ip> -j <blocktype> # Option: pre-rule # Notes.: prefix parameter(s) inserted to the begin of rule. No default (empty) # pre-rule = rule-jump = -j <_ipt_rule_target> # Several capabilities used internaly: _ipt_for_proto-iter = for proto in $(echo '<protocol>' | sed 's/,/ /g'); do _ipt_for_proto-done = done _ipt_add_rules = <_ipt_for_proto-iter> { %(_ipt_check_rule)s >/dev/null 2>&1; } || { <iptables-nft> -I <chain> %(_ipt_chain_rule)s; } <_ipt_for_proto-done> _ipt_del_rules = <_ipt_for_proto-iter> <iptables-nft> -D <chain> %(_ipt_chain_rule)s <_ipt_for_proto-done> _ipt_check_rules = <_ipt_for_proto-iter> %(_ipt_check_rule)s <_ipt_for_proto-done> _ipt_chain_rule = <pre-rule><ipt_<type>/_chain_rule> _ipt_check_rule = <iptables-nft> -C <chain> %(_ipt_chain_rule)s _ipt_rule_target = f2b-<name> [ipt_oneport] _chain_rule = -p $proto --dport <port> <rule-jump> [ipt_multiport] _chain_rule = -p $proto -m multiport --dports <port> <rule-jump> [ipt_allports] _chain_rule = -p $proto <rule-jump> [Init] # Option: chain # Notes specifies the iptables chain to which the Fail2Ban rules should be # added # Values: STRING Default: INPUT chain = INPUT # Default name of the chain # name = default # Option: port # Notes.: specifies port to monitor # Values: [ NUM | STRING ] Default: # port = ssh # Option: protocol # Notes.: internally used by config reader for interpolations. # Values: [ tcp | udp | icmp | all ] Default: tcp # protocol = tcp # Option: blocktype # Note: This is what the action does with rules. This can be any jump target # as per the iptables man page (section 8). Common values are DROP # REJECT, REJECT --reject-with icmp-port-unreachable # Values: STRING blocktype = DROP # Option: returntype # Note: This is the default rule on "actionstart". This should be RETURN # in all (blocking) actions, except REJECT in allowing actions. # Values: STRING returntype = RETURN # Option: lockingopt # Notes.: Option was introduced to iptables to prevent multiple instances from # running concurrently and causing irratic behavior. -w was introduced # in iptables 1.4.20, so might be absent on older systems # See https://github.com/fail2ban/fail2ban/issues/1122 # Values: STRING lockingopt = -w # Option: iptables # Notes.: Actual command to be executed, including common to all calls options # Values: STRING iptables-nft = iptables-nft <lockingopt> [Init?family=inet6] # Option: blocktype (ipv6) # Note: This is what the action does with rules. This can be any jump target # as per the iptables man page (section 8). Common values are DROP # REJECT, REJECT --reject-with icmp6-port-unreachable # Values: STRING blocktype = DROP # Option: iptables (ipv6) # Notes.: Actual command to be executed, including common to all calls options # Values: STRING iptables-nft = ip6tables-nft <lockingopt>
Y la acción que va a realizar en este caso insertarlo en iptables para bloquear el acceso.
Ahora, una vez todo montado, tenemos que ejecutarlo poniendo:
docker compose up -d
Ingeniero en Informática, Investigador, me encanta crear cosas o arreglarlas y darles una nueva vida. Escritor y poeta. Más de 20 APPs publicadas y un libro en Amazon.