Introducción
SSH es el método por defecto para conectarse a un servidor en la nube. Es duradero y es extensible; a medida que se desarrollan nuevos estándares de cifrado, se pueden utilizar para generar nuevas claves SSH, asegurando que el protocolo principal permanezca seguro. Sin embargo, ningún protocolo o conjunto de software es totalmente infalible, y dado que SSH está tan ampliamente desplegado en Internet, representa una superficie de ataque muy predecible o vector de ataque a través del cual las personas pueden intentar obtener acceso.
Cualquier servicio expuesto a la red es un objetivo potencial de esta manera. Si revisa los registros de su servicio SSH en cualquier servidor con mucho tráfico, a menudo verá intentos de inicio de sesión repetidos y sistemáticos que representan ataques de fuerza bruta tanto por parte de usuarios como de bots. Aunque puede realizar algunas optimizaciones en su servicio SSH para reducir la probabilidad de que estos ataques tengan éxito casi a cero, como deshabilitar la autenticación de contraseña a favor de las claves SSH, aún pueden representar una responsabilidad menor y continua.
Implantaciones de producción a gran escala para quienes esta responsabilidad es completamente inaceptable generalmente implementarán una VPN como WireGuard frente a su servicio SSH, de modo que sea imposible conectarse directamente al puerto SSH predeterminado 22 desde Internet sin software de abstracción adicional o pasarelas. Estas soluciones de VPN son ampliamente confiables, pero agregarán complejidad y pueden romper algunas automatizaciones u otros ganchos de software pequeños.
Antes de comprometerse o además de comprometerse con una configuración completa de VPN, puede implementar una herramienta llamada Fail2ban. Fail2ban puede mitigar significativamente los ataques de fuerza bruta mediante la creación de reglas que alteran automáticamente la configuración de su firewall para prohibir IPs específicas después de cierto número de intentos de inicio de sesión fallidos. Esto permitirá que su servidor se proteja contra estos intentos de acceso sin intervención de su parte.
En otro tutorial, discutimos Cómo proteger SSH con Fail2ban. En esta guía, discutiremos más en profundidad cómo funciona Fail2ban y cómo puede utilizar este conocimiento para modificar o extender el comportamiento de este servicio.
Los Fundamentos de Fail2ban El propósito de Fail2ban es monitorear los registros de servicios comunes para detectar patrones en los fallos de autenticación.
El propósito de Fail2ban es monitorear los registros de servicios comunes para detectar patrones en los fallos de autenticación.
Cuando Fail2ban está configurado para monitorear los registros de un servicio, busca un filtro que ha sido configurado específicamente para ese servicio. El filtro está diseñado para identificar fallos de autenticación para ese servicio específico mediante el uso de expresiones regulares complejas. Las expresiones regulares son un lenguaje de plantillas común utilizado para el emparejamiento de patrones. Define estos patrones de expresión regular en una variable interna llamada failregex
.
Por defecto, Fail2ban incluye archivos de filtro para servicios comunes. Cuando un registro de cualquier servicio, como un servidor web, coincide con el failregex
en su filtro, se ejecuta una acción predefinida para ese servicio. El action
es una variable que se puede configurar para hacer muchas cosas diferentes, dependiendo de las preferencias del administrador.
La acción predeterminada es prohibir la dirección IP/host ofensiva modificando las reglas del firewall local. Puedes ampliar esta acción, por ejemplo, enviando un correo electrónico a tu administrador de sistemas.
Por defecto, se tomará acción cuando se hayan detectado tres fallos de autenticación en10 minutos, y el tiempo de prohibición predeterminado es de10 minutos. Esto es configurable.
Al utilizar el cortafuegos predeterminado iptables
, fail2ban
crea un nuevo conjunto de reglas de firewall, también llamado cadena, cuando el servicio se inicia. Agrega una nueva regla a la cadena INPUT que envía todo el tráfico TCP dirigido al puerto22 a la nueva cadena. En la nueva cadena, inserta una sola regla que regresa a la cadena INPUT. La cadena y las reglas asociadas se eliminan si el servicio Fail2ban se detiene.
Explorando la configuración del servicio Fail2ban
Fail2ban se configura a través de varios archivos ubicados dentro de una jerarquía bajo el directorio /etc/fail2ban/
.
El archivo fail2ban.conf
configura algunas opciones de operación como la forma en que el demonio registra información y los archivos de socket y pid que utilizará. Sin embargo, la configuración principal se especifica en los archivos que definen las “celdas” por aplicación.
Por defecto, fail2ban incluye un archivo jail.conf
. Sin embargo, esto puede sobrescribirse en las actualizaciones, por lo que debe copiar este archivo a un archivo jail.local
y hacer ajustes allí.
Si ya tiene un archivo jail.local
, ábralo usando nano
o su editor de texto favorito:
Si no tiene un archivo jail.local
ya existente, o el archivo que abrió estaba en blanco, copie el archivo jail.conf
y luego abra el nuevo archivo:
Echaremos un vistazo a las opciones disponibles aquí y veremos cómo este archivo interactúa con otros archivos de configuración en el sistema.
La sección predeterminada
La primera parte del archivo definirá los valores predeterminados para la política de fail2ban. Estas opciones se pueden anular en la sección de configuración de cada servicio individual.
Con los comentarios eliminados, la sección predeterminada en su totalidad se ve algo así:
[DEFAULT]
ignoreip = 127.0.0.1/8
bantime = 10m
findtime = 10m
maxretry = 3
backend = auto
usedns = warn
destemail = root@localhost
sendername = Fail2Ban
banaction = iptables-multiport
mta = sendmail
protocol = tcp
chain = INPUT
action_ = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s", sendername="%(sendername)s"]
action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s", sendername="%(sendername)s"]
action = %(action_)s
Repasemos lo que significa algunas de estas cosas:
- ignoreip: Este parámetro identifica las direcciones IP que deben ignorarse por el sistema de prohibición. Por defecto, esto solo está configurado para ignorar el tráfico proveniente de la máquina en sí, para que no llenes tus propios registros o te bloquees.
- bantime: Este parámetro establece la duración de una prohibición, en segundos. El valor predeterminado es de10 minutos.
- findtime: Este parámetro establece la ventana que Fail2ban prestará atención al buscar intentos repetidos de autenticación fallidos. El valor predeterminado está establecido en10 minutos, lo que significa que el software contará el número de intentos fallidos en los últimos10 minutos.
- maxretry: Esto establece el número de intentos fallidos que se tolerarán dentro de la ventana de
findtime
antes de que se instituya una prohibición. - usedns: Esto define si se utiliza el DNS inverso para ayudar a implementar prohibiciones. Establecer esto en “no” prohibirá las direcciones IP en sí mismas en lugar de sus nombres de host de dominio. La configuración
warn
intentará buscar un nombre de host y prohibir de esa manera, pero registrará la actividad para su revisión. - destemail: Esta es la dirección a la que se enviará el correo de notificación si configura su acción para enviar alertas por correo.
- sendername: Esto se utilizará en el campo de remitente del correo electrónico para los correos electrónicos de notificación generados.
- banaction: Esto establece la acción que se utilizará cuando se alcance el umbral. En realidad, esto es una ruta a un archivo ubicado en
/etc/fail2ban/action.d/
llamadoiptables-multiport.conf
. Esto maneja la manipulación real del firewalliptables
para prohibir una dirección IP. Lo veremos más adelante. - mta: Este es el agente de transferencia de correo que se utilizará para enviar correos electrónicos de notificación.
- protocol: Este es el tipo de tráfico que se eliminará cuando se implemente una prohibición de IP. También es el tipo de tráfico que se envía a la nueva cadena iptables.
- chain: Esta es la cadena que se configurará con una regla de salto para enviar el tráfico al embudo de fail2ban.
El resto de los parámetros definen diferentes acciones que se pueden especificar. Pasan algunos de los parámetros que hemos definido anteriormente utilizando la sustitución de variables dentro de cadenas de texto como esta: %(nombre_var)s
- cadena: Esta es la cadena que se configurará con una regla de salto para enviar tráfico al embudo de fail2ban.
El resto de los parámetros definen diferentes acciones que se pueden especificar. Pasan algunos de los parámetros que hemos definido anteriormente utilizando sustitución de variables dentro de cadenas de texto como esta:
%(var_name)s
La línea anterior se reemplazaría con el contenido de var_name
. Usando esto, podemos ver que la variable action
está establecida por defecto en la definición action_
(solo prohibir, sin alertas por correo electrónico).
Esto, a su vez, se configura llamando a la acción iptables-multiport
con una lista de parámetros (nombre del servicio, puerto, protocolo y cadena) que se necesita para realizar la prohibición. El __name__
se sustituye con el nombre del servicio como se especifica en los encabezados de sección a continuación.
Secciones específicas del servicio
Debajo de la sección predeterminada, hay secciones para servicios específicos que se pueden usar para anular la configuración predeterminada. Esto sigue una convención de modificar solo los parámetros que difieren de los valores normales (convención sobre configuración).
Cada encabezado de sección se especifica así:
[service_name]
Cualquier sección que tenga la línea enabled = true
se leerá y habilitará.
Dentro de cada sección, se configuran los parámetros, incluyendo el archivo de filtro que se debe utilizar para analizar los registros (menos la extensión del archivo) y la ubicación de los archivos de registro en sí.
Teniendo esto en cuenta, la sección que especifica las acciones para el servicio SSH se ve así:
[SSH]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 6
Esto habilita esta sección y establece el puerto en el puerto “ssh” predeterminado (puerto22). Indica a Fail2ban que revise el registro ubicado en /var/log/auth.log
para esta sección y que analice el registro utilizando los mecanismos de filtrado definidos en el directorio /etc/fail2ban/filters.d
en un archivo llamado sshd.conf
.
Todas las demás piezas de información que necesita se toman de los parámetros definidos en la sección [DEFAULT]
. Por ejemplo, la acción se establecerá en action_
que prohibirá la dirección IP ofensiva utilizando la acción de prohibición iptables-multiport
, que hace referencia a un archivo llamado iptables-multiport.conf
encontrado en /etc/fail2ban/action.d
.
Como puede ver, las acciones en la sección [DEFAULT]
deben ser generales y flexibles. El uso de la sustitución de parámetros junto con parámetros que proporcionan valores predeterminados sensibles hará posible anular las definiciones cuando sea necesario.
Examinando el Archivo de Filtro
Para comprender lo que está sucediendo en nuestra configuración, necesitamos comprender los archivos de filtro y acción, que realizan la mayor parte del trabajo.
El archivo de filtro determinará las líneas que fail2ban buscará en los archivos de registro para identificar características ofensivas. El archivo de acción implementa todas las acciones necesarias, desde la construcción de una estructura de firewall cuando el servicio se inicia, hasta la adición y eliminación de reglas, y la descomposición de la estructura de firewall cuando el servicio se detiene.
Echemos un vistazo al archivo de filtro que nuestro servicio SSH llamó en la configuración anterior:
[INCLUDES]
before = common.conf
[Definition]
_daemon = sshd
failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error) for .* from <HOST>( via \S+)?\s*$
^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
^%(__prefix_line)sFailed \S+ for .*? from <HOST>(?: port \d*)?(?: ssh\d*)?(: (ruser .*|(\S+ ID \S+ \(serial \d+\) CA )?\S+ %(__md5hex)s(, client user ".*", client host ".*")?))?\s*$
^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
^%(__prefix_line)s[iI](?:llegal|nvalid) user .* from <HOST>\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because not in any group\s*$
^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
ignoreregex =
La sección de encabezado [INCLUDES]
especifica otros archivos de filtro que se leen antes o después de este archivo. En nuestro ejemplo, el archivo common.conf
se lee y coloca antes que las otras líneas en este archivo. Esto establece algunos parámetros que estaremos utilizando en nuestra configuración.
A continuación, tenemos una sección [Definition]
que define las reglas reales para nuestras coincidencias de filtro. Primero, establecemos el nombre del demonio que estamos monitoreando usando el parámetro _daemon
.
Después de eso, pasamos a la definición real de failregex
, que establece los patrones que se activarán cuando se encuentre una línea coincidente en el archivo de registro. Estas son expresiones regulares que coinciden en función de los diferentes errores y fallos que pueden ocurrir cuando un usuario no se autentica correctamente.
Porciones de la línea como %(__prefix_line)s
serán sustituidas por el valor de un parámetro configurado en el archivo common.conf
que obtuvimos. Esto se usa para coincidir con la diferente información inicial que los sistemas operativos escriben en los archivos de registro cuando usan métodos estándar. Por ejemplo, algunas líneas del archivo /var/log/auth.log
podrían parecerse a esto:
May 6 18:18:52 localhost sshd[3534]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=101.79.130.213
May 6 18:18:54 localhost sshd[3534]: Failed password for invalid user phil from 101.79.130.213 port 38354 ssh2
May 6 18:18:54 localhost sshd[3534]: Received disconnect from 101.79.130.213: 11: Bye Bye [preauth]
La parte destacada es un patrón estándar que inserta el sistema operativo para proporcionar más contexto. Después de eso, hay varias formas diferentes en que el servicio de firewall iptables escribe intentos fallidos en el registro.
Vemos dos fallas separadas en las primeras dos líneas anteriores (un error de autenticación PAM y un error de contraseña). Las expresiones regulares definidas en el filtro están diseñadas para coincidir con cualquiera de las posibles líneas de falla. No debería ser necesario ajustar ninguna de estas líneas, pero debe tener en cuenta la necesidad de capturar todas las entradas de registro que signifiquen un error de uso no autorizado para la aplicación que está tratando de proteger si alguna vez tiene que crear un archivo de filtro usted mismo.
En la parte inferior, puede ver un parámetro ignoreregex
, que actualmente está en blanco. Esto se puede usar para excluir patrones más específicos que normalmente coincidirían con una condición de falla en caso de que desee negar el desencadenador de falla para fail2ban para ciertos escenarios. No ajustaremos esto.
Guarde y cierre el archivo cuando haya terminado de examinarlo.
Examinando el Archivo de Acción
Ahora, veamos el archivo de acción. Este archivo es responsable de configurar el firewall con una estructura que permite modificaciones para prohibir hosts maliciosos, y para agregar y eliminar esos hosts según sea necesario.
La acción que invoca nuestro servicio SSH se llama iptables-multiport
. Abra el archivo asociado ahora:
Con los comentarios eliminados, este archivo se ve algo así:
[INCLUDES]
before = iptables-blocktype.conf
[Definition]
actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN
iptables -I <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
actionstop = iptables -D <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
actioncheck = iptables -n -L <chain> | grep -a 'fail2ban-<name>[ \t]'
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype>
actionunban = iptables -D fail2ban-<name> -s <ip> -j <blocktype>
[Init]
name = default
port = ssh
protocol = tcp
chain = INPUT
El archivo comienza por importar otro archivo de acción llamado iptables-blocktype.conf
que define el parámetro blocktype
, que configura la restricción que se establecerá cuando un cliente sea prohibido. Por defecto, el blocktype
está configurado para rechazar paquetes y responder a pings enviados por clientes prohibidos con un mensaje de rechazo indicando que el puerto no está disponible. Utilizaremos esto en nuestras reglas de prohibición a continuación.
A continuación, llegamos a las definiciones de reglas en sí. La acción actionstart
configura el firewall iptables cuando se inicia el servicio fail2ban. Crea una nueva cadena, añade una regla a esa cadena para volver a la cadena de llamada, e inserta una regla al principio de la cadena INPUT que pasa el tráfico que coincide con el protocolo y los destinos de puerto correctos a la nueva cadena.
Esto se hace utilizando los valores que pasamos con la acción
que definimos en nuestro archivo jail.local
. El nombre
se toma del encabezado de sección para cada servicio. El cadena
, protocolo
, y puerto
se toman de la línea de acción
en ese archivo.
Aquí, todos los parámetros que son establecidos por el otro archivo son referenciados incluyendo el nombre del parámetro entre corchetes angulares:
<nombre_param>
Cuando descendemos a la definición de actionstop
complementaria, podemos ver que los comandos de firewall están implementando una reversión de los comandos actionstart
. Cuando el servicio Fail2ban se detiene, elimina limpiamente cualquier regla de firewall que haya añadido.
Otra acción llamada actioncheck
se asegura de que se haya creado la cadena adecuada antes de intentar agregar reglas de prohibición.
Luego, pasamos a la regla de prohibición real, llamada actionban
. Esta regla funciona agregando una nueva regla a nuestra cadena creada. La regla coincide con la dirección IP de origen del cliente ofensor, este parámetro se lee en los registros de autorización cuando se alcanza el límite de maxretry
. Instituye el bloque definido por el parámetro blocktype
que obtuvimos en la sección [INCLUDE]
en la parte superior del archivo.
La regla actionunban
elimina esta regla. Esto se hace automáticamente por fail2ban cuando ha transcurrido el tiempo de prohibición.
Finalmente, llegamos a la sección [Init]
. Esto simplemente proporciona algunos valores predeterminados en caso de que el archivo de acción se llame sin pasar todos los valores apropiados.
Cómo el Servicio Fail2ban Procesa Archivos de Configuración para Implementar Prohibiciones
Ahora que hemos visto los detalles específicos, repasemos el proceso que ocurre cuando fail2ban se inicia.
Cargando los Archivos de Configuración Iniciales
Primero, se lee el archivo principal fail2ban.conf
para determinar las condiciones bajo las cuales debe operar el proceso principal. Se crean los archivos de socket, pid y registro si es necesario y comienza a utilizarlos.
A continuación, fail2ban lee el archivo jail.conf
para obtener detalles de configuración. A esto le sigue la lectura, en orden alfabético, de cualquier archivo encontrado en el directorio jail.d
que termine en .conf
. Agrega las configuraciones encontradas en estos archivos a su configuración interna, dando preferencia a los nuevos valores sobre los valores descritos en el archivo jail.conf
.
Luego, busca un archivo jail.local
y repite este proceso, adaptando los nuevos valores. Finalmente, busca nuevamente en el directorio jail.d
, leyendo en orden alfabético archivos que terminen en .local
.
En nuestro caso, solo tenemos un archivo jail.conf
y un archivo jail.local
. En nuestro archivo jail.local
, solo necesitamos definir los valores que difieren del archivo jail.conf
. El proceso de fail2ban ahora tiene un conjunto de directivas cargadas en memoria que representan una combinación de todos los archivos que encontró.
Examina cada sección y busca una directiva enabled = true
. Si encuentra una, utiliza los parámetros definidos bajo esa sección para construir una política y decidir qué acciones son necesarias. Cualquier parámetro que no se encuentre en la sección del servicio utiliza los parámetros definidos en la sección [DEFAULT]
.
Análisis de los archivos de acción para determinar las acciones iniciales
Fail2ban busca una directiva action
para determinar qué script de acción llamar para implementar las políticas de prohibición/desprohibición. Si no se encuentra uno, recurre a la acción predeterminada determinada anteriormente.
La directiva de acción consiste en el nombre del archivo de acción(es) que se leerá, así como un diccionario de pares clave-valor que pasa los parámetros necesarios por esos archivos. Los valores de estos a menudo toman la forma de sustituciones de parámetros al hacer referencia a la configuración establecida en la sección del servicio. La clave “nombre” suele recibir el valor de la variable especial __name__
que se establecerá en el valor del encabezado de la sección.
Fail2ban luego utiliza esta información para encontrar los archivos asociados en el directorio action.d
. Primero busca el archivo de acción asociado que termine en .conf
y luego modifica la información encontrada allí con cualquier configuración contenida en un archivo .local
acompañante también encontrado en el directorio action.d
.
Parsa esos archivos para determinar las acciones que necesita tomar. Lee el valor actionstart
para ver las acciones que debe tomar para configurar el entorno. Esto incluye a menudo la creación de una estructura de firewall para adaptarse a las reglas de prohibición en el futuro.
Las acciones definidas en este archivo utilizan los parámetros que se le pasan desde la directiva action
. Usará estos valores para crear dinámicamente las reglas apropiadas. Si una cierta variable no fue establecida, puede mirar los valores predeterminados establecidos en el archivo de acción para completar los espacios en blanco.
Análisis de los Archivos de Filtro para Determinar Reglas de Filtrado
Los parámetros para el servicio en los archivos jail.*
también incluyen la ubicación del archivo de registro, así como el mecanismo de sondeo que se debe usar para verificar el archivo (esto está definido por el parámetro backend
). También incluye un filtro que se debe usar para determinar si una línea en el registro representa un fallo.
Fail2ban busca en el directorio filter.d
para encontrar el archivo de filtro coincidente que termina con .conf
. Lee este archivo para definir los patrones que se pueden utilizar para hacer coincidir líneas ofensivas. Luego busca un archivo de filtro coincidente que termine con .local
para ver si se sobrescribieron algunos de los parámetros predeterminados.
Utiliza las expresiones regulares definidas en estos archivos mientras lee el archivo de registro del servicio. Intenta cada línea failregex
definida en los archivos filter.d
contra cada nueva línea escrita en el archivo de registro del servicio.
Si la expresión regular devuelve una coincidencia, verifica la línea contra las expresiones regulares definidas por el ignoreregex
. Si también coincide, fail2ban lo ignora. Si la línea coincide con una expresión en el failregex
pero no coincide con una expresión en el ignoreregex
, se incrementa un contador interno para el cliente que causó la línea y se crea una marca de tiempo asociada para el evento.
A medida que se alcanza la ventana de tiempo establecida por el parámetro findtime
en los archivos jail.*
(según lo determinado por la marca de tiempo del evento), el contador interno se decrementa nuevamente y el evento ya no se considera relevante para la política de prohibición.
Si, con el tiempo, se registran fallos adicionales de autenticación, cada intento incrementa el contador. Si el contador alcanza el valor establecido por el parámetro maxretry
dentro de la ventana de tiempo configurada, fail2ban instituye una prohibición llamando a la acción actioncheck
para el servicio según lo definido en los archivos action.d/
para el servicio. Esto es para determinar si la acción actionstart
configuró la estructura necesaria. Luego llama a la acción actionban
para prohibir al cliente infractor. Establece una marca de tiempo para este evento también.
Cuando haya transcurrido la cantidad de tiempo especificada por el parámetro bantime
, fail2ban desbloquea al cliente llamando a la acción actionunban
.
Conclusión
En este momento, tienes un entendimiento bastante profundo de cómo opera fail2ban. Cuando te apartas de la configuración estándar, es útil saber cómo funciona fail2ban para manipular su comportamiento de manera predecible.
Para aprender cómo proteger otros servicios con fail2ban, puedes leer Cómo proteger un servidor Nginx con Fail2Ban en Ubuntu 22.04.