Para proteger los servicios que tenemos montados con Docker a través del proxy inverso Traefik podemos usar Fail2ban. De este modo podremos evitar los ataques de fuerza bruta bloqueando a todo usuario o bot que introduzca las credenciales de autenticación incorrectamente de forma reiterada. Para ello deberemos proceder del siguiente modo.
INSTALAR FAIL2BAN PARA EVITAR ATAQUES DE FUERZA BRUTA EN LOS SERVICIOS DOCKER DETRÁS DE TRAEFIK
Obviamente tenemos que instalar Fail2ban. Fail2ban será el encargado de bloquear la totalidad de ataques de fuerza bruta a nuestros servicios autoalojados mediante Traefik. Podemos instalar Fail2ban directamente en nuestro servidor o mediante un contenedor de Docker. Ambos métodos funcionan, pero en mi caso lo he instalado locamente usando los repositorios de mi distribución Linux. Para ello he ejecutado el siguiente comando en la terminal:
sudo apt-get install fail2ban
Nota: En el caso que usen un gestor de paquetes diferentes a apt deberán adaptar el comando de instalación. Por ejemplo si usan una distro que use el gestor de paquetes yum deberán ejecutar sudo yum install epel-release && sudo yum update -y && sudo yum install -y fail2ban
Si por lo contrario quieren instalar Fail2ban usando un contenedor de Docker pueden usar el siguiente Docker-Compose:
version: '2' services: fail2ban: image: crazymax/fail2ban:latest restart: unless-stopped network_mode: "host" cap_add: - NET_ADMIN - NET_RAW volumes: - /var/log:/var/log:ro - /home/geekland/services/fail2ban/data:/data env_file: - ./fail2ban.env
Nota: El docker-compose lo he probado y funciona, pero lo dejaremos de lado en este artículo. Si quieren usar el Docker tendréis que cambiar las rutas de los volúmenes de persistencia.
HACER QUE TRAEFIK GUARDE LOS LOGS EN UN VOLUMEN DE PERSISTENCIA
En su día instalamos Traefik v2 siguiendo las siguientes indicaciones. La configuración estándar no genera ningún tipo de log y esto es un problema importante para que fail2ban pueda funcionar con Traefik. Para activar y filtrar los logs de autenticación en Traefik deben modificar el fichero de configuración traefik.toml
para que quede del siguiente modo:
[entryPoints] [entryPoints.web] address = ":80" [entryPoints.web.http.redirections.entryPoint] to = "websecure" scheme = "https" [entryPoints.websecure] address = ":443" [api] dashboard = true [certificatesResolvers.lets-encrypt.acme] email = "tuemail@geekland.com" storage = "acme.json" [certificatesResolvers.lets-encrypt.acme.tlsChallenge] [providers.docker] watch = true network = "web" [providers.file] filename = "traefik_dynamic.toml" [accessLog] filePath = "/var/log/access.log" [accessLog.filters] statusCodes = ["400-499"] retryAttempts = true minDuration = "10ms"
Nota: Lo único que tienen que hacer es añadir la parte de color azul en su fichero de configuración.
Los nuevos parámetros introducidos en el fichero de configuración tienen el siguiente significado:
filePath = "/var/log/access.log"
: Definimos que la ruta de los logs de Traefik sea /var/log/access.log
statusCodes = ["400-499"]
: Definimos/filtramos los logs que almacenará Fail2ban. Con el rango 400-499 se registrarán los intentos de autenticación en la totalidad de servicios que están detrás de Traefik.
retryAttempts = true
: Se loguearan y almacenarán la totalidad de intentos/reintentos de autenticación.
minDuration = "10ms"
: Se registran todas las peticiones que tardan más de 10ms en resolverse en el log.
Una vez realizada la configuración guardan los cambios, cierran el fichero y borran el contenedor actual de Traefik mediante los siguiente comandos:
docker stop traefik docker rm traefik
A continuación generan el siguiente docker-compose para levantar un nuevo contenedor de Traefik. Si os fijáis, el siguiente docker-compose tiene la particularidad de crear un volumen de persistencia de los logs de Traefik en la ubicación /var/log/traefik
. El docker-compose para levantar el nuevo contenedor de Traefik es el siguiente:
version: '2' services: traefik: image: traefik:latest restart: unless-stopped container_name: traefik volumes: - /var/run/docker.sock:/var/run/docker.sock - /home/geekland/traefik.toml:/traefik.toml - /home/geekland/traefik_dynamic.toml:/traefik_dynamic.toml - /home/geekland/acme.json:/acme.json - /var/log/traefik:/var/log ports: - 8080:8080/tcp - 80:80/tcp - 443:443/tcp networks: - web
Nota: Deberéis reemplazar las partes verdes por la ruta en que querías montar vuestro volumen de persistencia.
Una vez generado el docker-compose guardan los cambios, cierran el fichero y levantamos el contenedor ejecutando el siguiente comando en la terminal:
docker-compose up -d
Una vez levantado el Docker habrán realizado el paso más importante para poder usar Fail2ban con la totalidad de servicios montados usando Traefik.
CONFIGURACIÓN PARA PODER USAR FAIL2BAN CON TRAEFIK
Para que Fail2ban pueda detectar los ataques de fuerza bruta debemos definir filtros y acciones. A modo de ejemplo crearemos un filtro y una acción para bloquear a los usuarios o bots que excedan el número límite de intentos de autenticación al panel web de Traefik.
Filtro de fail2ban para detectar los intentos fallidos de autenticación al panel web de Traefik
Lo primero que tenemos que realizar es acceder a la ruta donde Fail2ban almacena los filtros. Para ello ejecutaremos el siguiente comando en la terminal:
pi@raspberrypi:~ $ cd /etc/fail2ban/filter.d/
A continuación crearemos el filtro traefik-auth.conf
ejecutando el siguiente comando en la terminal:
pi@raspberrypi:/etc/fail2ban/filter.d $ sudo nano traefik-auth.conf
Cuando se abra el editor de textos nano pegaremos el código para generar el filtro para este servicio.
[Definition] failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) .+\" 401 .+$ ignoreregex =
Nota: El filtro no es nada más que una expresión regular que coincide con lo que registra el log de Traefik cuando un usuario introduce la contraseña de forma incorrecta al autenticarse.
A continuación crearemos un segundo filtro con nombre traefik-botsearch.conf
ejecutando el siguiente comando en la terminal:
pi@raspberrypi:/etc/fail2ban/filter.d $ sudo nano traefik-botsearch.conf
Una vez se abra el editor de textos nano pegaremos el código para el segundo de los filtros.
[INCLUDES] before = botsearch-common.conf [Definition] failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) \/<block> \S+\" 404 .+$
Con el segundo de los filtros creados guardamos los cambios y cerramos el fichero. En estos momentos deberemos definir una acción para cada uno de los filtros que acabamos de crear.
Definir las acciones para los filtros que acabamos de crear
Para definir las acciones de los filtros traefik-auth
y traefik-botsearch
accederemos el directorio /etc/fail2ban/filter.d. Para ello en mi caso tengo que ejecutar el siguiente comando:
pi@raspberrypi:/etc/fail2ban/data/filter.d $ cd .. && cd jail.d
A continuación crearemos el fichero traefik.conf
que contendrá la totalidad de acciones para los filtros definidos para detectar los intentos fallidos de autenticación. Para ello ejecutaremos el siguiente comando:
pi@raspberrypi:/etc/fail2ban/data/jail.d $ sudo nano traefik.conf
Cuando se abra el fichero de texto pegaremos el siguiente código:
[traefik-auth] enabled = true chain = DOCKER-USER port = http,https filter = traefik-auth logpath = /var/log/traefik/access.log maxretry = 6 bantime = 30m [traefik-botsearch] enabled = true chain = DOCKER-USER port = http,https filter = traefik-botsearch logpath = /var/log/traefik/access.log maxretry = 6 bantime = 30m
Una vez pegado el código guardamos los cambios y cerramos el fichero. A partir de estos momentos lo único que tenemos que realizar es reiniciar fail2ban ejecutando el siguiente comando en la terminal:
pi@raspberrypi:/etc/fail2ban/data/jail.d $ sudo service fail2ban restart
A partir de este momento todo usuario o bot que introduzca las credenciales de autenticación de forma incorrecta en el panel de administración web de Traefik más de 6 veces se le bloqueará el acceso durante 30 minutos.
Para entender más el funcionamiento de Fail2ban les recomiendo que lean los siguientes artículos:
COMPROBAR EL FUNCIONAMIENTO DE FAIL2BAN
A partir de esto momento entráis en vuestro panel de administración web de Traefik y lo único que tenéis que realizar es introducir las credenciales de acceso erróneamente de forma deliberada. Después de introducir la contraseña erróneamente 6 veces se os bloqueará el acceso al servicio durante media hora. Lo que veréis en pantalla será lo siguiente:
¿COMO AÑADIR MÁS SERVICIOS DETRÁS DE TRAEFIK A FAIL2BAN?
Para añadir más servicios tan solo tenemos que crear filtros y acciones. A modo de ejemplo crearemos un filtro y una acción para la nube Filerun.
Si observamos el log de Traefik ubicado en /var/log/traefik
vemos que cada vez que un usuario se autentica de forma incorrecta se registra lo siguiente:
23.34.173.XXX - - [17/Apr/2021:08:10:40 +0000] "POST /?module=fileman&page=login&action=login HTTP/2.0" 200 67 "-" "-" 295 "filerun@docker" "http://172.20.0.4:80" 64ms
Por lo tanto tendremos que crear un filtro para detectar la aparición de esta línea en el log de Traefik. Para ello ejecutaremos el siguiente comando:
pi@raspberrypi:~ $ sudo nano /etc/fail2ban/filter.d/filerun-auth.conf
Una vez se abra el editor de textos nano introduciremos el filtro que en mi caso es el siguiente:
[Definition] failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) .+\" 200 [6-6][0-9] .+$ ignoreregex =
Nota: En failregex he introducido una expresión regular que concuerde con lo que registra el log de Traefik cada vez que un usuario introduzca la contraseña de forma incorrecta en Filerun.
A continuación guardamos los cambios, cerramos el fichero y ejecutamos el siguiente comando en la terminal para definir la acción.
pi@raspberrypi:~ $ sudo nano /etc/fail2ban/jail.d/filerun.conf
Cuando se abra el editor de textos nano introducís la siguiente acción:
[filerun-auth] enabled = true chain = DOCKER-USER port = http,https filter = filerun-auth logpath = /var/log/traefik/access.log maxretry = 6 bantime = 30m
Ahora tan solo tenemos que guardar los cambios, cerrar el fichero y reiniciar el servicio Fail2ban mediante el siguiente comando:
sudo service fail2ban restart
A partir de estos momentos Fail2ban también estará protegiendo el servicio Filerun.
SOLUCIÓN A POSIBLES PROBLEMAS AL INTENTAR COMBINAR EL USO DE FAIL2BAN Y TRAEFIK
En mi caso he aplicado el tutorial que acabáis de ver en una Raspberry Pi y no me ha funcionado a la primera. El momento en que fail2ban tenia que bloquear la IP fallaba y me daba el siguiente mensaje de error:
iptables: No chain/target/match by that name.
Para solucionar este error he tenido que implementar las siguientes acciones.
Usar Iptables en modo Legacy
En el caso que su distribución use el backend de Nftables deberán realizar lo que describe en el siguiente apartado.
Inicialmente instalaremos los siguientes paquetes para asegurar que nuestro sistema operativo pueda usar Iptables en modo Legacy:
sudo apt-get install -y iptables arptables ebtables
Una vez instalados los paquetes pondremos Iptables en modo Legacy ejecutando los siguientes comandos en la terminal:
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy sudo update-alternatives --set arptables /usr/sbin/arptables-legacy sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy
Si algún día quieren volver a usar el backend de nftables deberán ejecutar los siguientes comandos en la terminal:
sudo update-alternatives --set iptables /usr/sbin/iptables-nft sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-nft sudo update-alternatives --set arptables /usr/sbin/arptables-nft sudo update-alternatives --set ebtables /usr/sbin/ebtables-nft
Una vez aplicados los cambios que acabo de citar reinicien el equipo.
Activar
Otra acción que he realizado para solucionar el error es activar el módulo del Kernel Multiport. Para ver si tenéis este módulo cargado en el Kernel hay que ejecutar el siguiente comando en la terminal:
pi@raspberrypi:~ $ sudo cat /proc/net/ip_tables_matches addrtype state conntrack conntrack conntrack addrtype udplite udp tcp icmp
Como pueden ver la salida del comando no contiene la palabra multiport. Por lo tanto tendremos que cargar el módulo ejecutando el siguiente comando en la terminal:
sudo modprobe -v xt_multiport
Ahora si volvemos a ejecutar el comando que vimos con anterioridad veremos que el módulo multiport está cargado.
pi@raspberrypi:~ $ sudo cat /proc/net/ip_tables_matches addrtype state conntrack conntrack conntrack addrtype multiport udplite udp tcp icmp
Una vez realizadas todas las modificaciones les recomiendo reiniciar el equipo.