Como o Fail2Ban Funciona para Proteger Serviços em um Servidor Linux

Introdução

O SSH é o método padrão de conexão a um servidor na nuvem. É durável e extensível — à medida que novos padrões de criptografia são desenvolvidos, eles podem ser usados para gerar novas chaves SSH, garantindo que o protocolo principal permaneça seguro. No entanto, nenhum protocolo ou pilha de software é totalmente à prova de falhas, e o fato do SSH ser tão amplamente implantado pela internet significa que ele representa uma superfície de ataque muito previsível ou vetor de ataque através do qual as pessoas podem tentar obter acesso.

Qualquer serviço exposto à rede é um alvo potencial dessa forma. Se você revisar os logs do seu serviço SSH em qualquer servidor com alto tráfego, frequentemente verá tentativas de login repetidas e sistemáticas que representam ataques de força bruta tanto por usuários quanto por bots. Embora você possa fazer algumas otimizações em seu serviço SSH para reduzir a chance de sucesso desses ataques para quase zero, como desabilitar a autenticação por senha em favor das chaves SSH, eles ainda podem representar uma responsabilidade menor e contínua.

Implantações de produção em grande escala para as quais essa responsabilidade é completamente inaceitável geralmente implementarão uma VPN como o WireGuard na frente de seu serviço SSH, para que seja impossível conectar diretamente à porta SSH padrão 22 da internet externa sem abstrações de software adicionais ou gateways. Essas soluções de VPN são amplamente confiáveis, mas adicionam complexidade e podem quebrar algumas automações ou outros ganchos de software pequenos.

Antes ou além de se comprometer com uma configuração de VPN completa, você pode implementar uma ferramenta chamada Fail2ban. O Fail2ban pode mitigar significativamente ataques de força bruta criando regras que alteram automaticamente a configuração do seu firewall para banir IPs específicos após um certo número de tentativas de login sem sucesso. Isso permitirá que seu servidor se proteja contra essas tentativas de acesso sem intervenção sua.

Em outro tutorial, discutimos Como proteger o SSH com o Fail2ban. Neste guia, discutiremos mais detalhadamente como o Fail2ban realmente funciona e como você pode usar esse conhecimento para modificar ou estender o comportamento deste serviço.

Os Fundamentos do Fail2ban

O objetivo do Fail2ban é monitorar os logs dos serviços comuns para detectar padrões em falhas de autenticação.

Quando o fail2ban é configurado para monitorar os logs de um serviço, ele examina um filtro que foi configurado especificamente para esse serviço. O filtro é projetado para identificar falhas de autenticação para esse serviço específico por meio do uso de expressões regulares complexas. Expressões regulares são uma linguagem de modelo comum usada para correspondência de padrões. Ele define esses padrões de expressão regular em uma variável interna chamada failregex.

Por padrão, o Fail2ban inclui arquivos de filtro para serviços comuns. Quando um log de qualquer serviço, como um servidor web, corresponde ao failregex em seu filtro, uma ação predefinida é executada para esse serviço. A ação é uma variável que pode ser configurada para fazer muitas coisas diferentes, dependendo das preferências do administrador.

A ação padrão é banir o host/endereço IP ofensivo modificando as regras do firewall local. Você pode expandir esta ação para, por exemplo, enviar um e-mail para o administrador do sistema.

Por padrão, a ação será tomada quando três falhas de autenticação forem detectadas em 10 minutos, e o tempo de banimento padrão é de 10 minutos. Isso é configurável.

Ao usar o firewall padrão iptables, o fail2ban cria um novo conjunto de regras de firewall, também chamado de chain, quando o serviço é iniciado. Ele adiciona uma nova regra à chain INPUT que envia todo o tráfego TCP direcionado à porta 22 para a nova chain. Na nova chain, ele insere uma única regra que retorna à chain INPUT. A chain e as regras associadas são removidas se o serviço Fail2ban for interrompido.

Explorando as configurações do serviço Fail2ban

O Fail2ban é configurado por meio de vários arquivos localizados em uma hierarquia sob o diretório /etc/fail2ban/.

O arquivo fail2ban.conf configura algumas configurações operacionais, como a maneira como o daemon registra informações e o arquivo de soquete e PID que ele usará. No entanto, a configuração principal é especificada nos arquivos que definem os “jails” específicos de cada aplicação.

Por padrão, o fail2ban vem com um arquivo jail.conf. No entanto, este arquivo pode ser substituído em atualizações, então você deve copiá-lo para um arquivo jail.local e fazer ajustes lá.

Se você já tiver um arquivo jail.local, abra-o usando o nano ou seu editor de texto favorito:

  1. sudo nano /etc/fail2ban/jail.local

Se você ainda não tiver um arquivo jail.local, ou se o arquivo que você abriu estava em branco, copie o arquivo jail.conf e então abra o novo arquivo:

  1. sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
  2. sudo nano /etc/fail2ban/jail.local

Vamos dar uma olhada nas opções disponíveis aqui e ver como esse arquivo interage com outros arquivos de configuração no sistema.

A seção padrão

A primeira parte do arquivo definirá os padrões para a política do fail2ban. Essas opções podem ser substituídas na seção de configuração de cada serviço individual.

Sem os comentários, a seção padrão inteira se parece com isso:

/etc/fail2ban/jail.local
[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

Vamos revisar o significado de algumas dessas configurações:

  • ignoreip: Este parâmetro identifica os endereços IP que devem ser ignorados pelo sistema de banimento. Por padrão, isso é definido apenas para ignorar o tráfego proveniente da própria máquina, para que você não preencha seus próprios logs ou se bloqueie.
  • bantime: Este parâmetro define o tempo de duração de um banimento, em segundos. O padrão é de 10 minutos.
  • findtime: Este parâmetro define a janela que o Fail2ban observará ao procurar tentativas de autenticação falhadas repetidas. O padrão é definido para 10 minutos, o que significa que o software contará o número de tentativas falhadas nos últimos 10 minutos.
  • maxretry: Isso define o número de tentativas falhadas que serão toleradas dentro da janela de findtime antes que um banimento seja instituído.
  • backend: Esta entrada especifica como o Fail2ban monitorará os arquivos de log. A configuração de auto significa que o fail2ban tentará pyinotify, depois gamin, e então um algoritmo de polling baseado no que estiver disponível. inotify é um recurso incorporado no kernel do Linux para rastrear quando os arquivos são acessados, e pyinotify é uma interface Python para inotify, usada pelo Fail2ban.
  • usedns: Isso define se o DNS reverso é usado para ajudar a implementar proibições. Definir isso como “não” irá banir os IPs em si em vez de seus nomes de host de domínio. A configuração warn tentará procurar um nome de host e banirá dessa forma, mas registrará a atividade para revisão.
  • destemail: Este é o endereço para o qual será enviado o e-mail de notificação se você configurou sua ação para enviar alertas por e-mail.
  • sendername: Isso será usado no campo de remetente do e-mail para e-mails de notificação gerados
  • banaction: Isso define a ação que será usada quando o limite for atingido. Na verdade, este é um caminho para um arquivo localizado em /etc/fail2ban/action.d/ chamado iptables-multiport.conf. Isso manipula a manipulação real do firewall iptables para banir um endereço IP. Vamos analisar isso mais tarde.
  • mta: Este é o agente de transferência de correio que será usado para enviar e-mails de notificação.
  • protocol: Este é o tipo de tráfego que será descartado quando uma proibição de IP for implementada. Este também é o tipo de tráfego que é enviado para a nova cadeia de iptables.
  • chain: Esta é a cadeia que será configurada com uma regra de salto para enviar o tráfego para o funil fail2ban.

O resto dos parâmetros define diferentes ações que podem ser especificadas. Eles passam alguns dos parâmetros que definimos acima usando substituição de variáveis dentro de strings de texto como esta:

%(nome_var)s

A linha acima seria substituída pelo conteúdo de var_name. Usando isso, podemos dizer que a variável action é definida por padrão como action_ (apenas ban, sem alertas por e-mail).

Isso, por sua vez, é configurado chamando a ação iptables-multiport com uma lista de parâmetros (nome do serviço, porta, protocolo e cadeia) necessários para realizar o banimento. O __name__ é substituído pelo nome do serviço conforme especificado pelas seções abaixo.

Seções Específicas do Serviço

Abaixo da seção padrão, existem seções para serviços específicos que podem ser usadas para substituir as configurações padrão. Isso segue uma convenção de apenas modificar os parâmetros que diferem dos valores normais (convenção sobre configuração).

Cada cabeçalho de seção é especificado assim:

[nome_do_serviço]

Qualquer seção que tenha a linha enabled = true será lida e habilitada.

Dentro de cada seção, os parâmetros são configurados, incluindo o arquivo de filtro que deve ser usado para analisar os logs (sem a extensão do arquivo) e a localização dos próprios arquivos de log.

Mantendo isso em mente, a seção que especifica as ações para o serviço SSH se parece com isso:

/etc/fail2ban/jail.local
[SSH]

enabled     = true
port        = ssh
filter      = sshd
logpath     = /var/log/auth.log
maxretry    = 6

Isso habilita esta seção e define a porta para a porta padrão “ssh” (porta 22). Diz ao Fail2ban para olhar o log localizado em /var/log/auth.log para esta seção e analisar o log usando os mecanismos de filtragem definidos no diretório /etc/fail2ban/filters.d em um arquivo chamado sshd.conf.

Todas as outras informações de que ele precisa são retiradas dos parâmetros definidos na seção [DEFAULT]. Por exemplo, a ação será definida como action_ que banirá o endereço IP ofensivo usando a ação de banimento iptables-multiport, que referencia um arquivo chamado iptables-multiport.conf encontrado em /etc/fail2ban/action.d.

Como você pode ver, as ações na seção [DEFAULT] devem ser gerais e flexíveis. Usar substituição de parâmetro junto com parâmetros que fornecem padrões sensatos tornará possível substituir definições quando necessário.

Examinando o Arquivo de Filtro

Para entender o que está acontecendo em nossa configuração, precisamos entender os arquivos de filtro e ação, que fazem a maior parte do trabalho.

O arquivo de filtro determinará as linhas que o fail2ban procurará nos arquivos de log para identificar características ofensivas. O arquivo de ação implementa todas as ações necessárias, desde a construção de uma estrutura de firewall quando o serviço é iniciado, até a adição e exclusão de regras, e a desmontagem da estrutura do firewall quando o serviço é interrompido.

Vamos dar uma olhada no arquivo de filtro que nosso serviço SSH chamou na configuração acima:

  1. sudo nano /etc/fail2ban/filter.d/sshd.conf
/etc/fail2ban/sshd.conf
[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 =

A seção de cabeçalho [INCLUDES] especifica outros arquivos de filtro que são lidos antes ou depois deste arquivo. Em nosso exemplo, o arquivo common.conf é lido e colocado antes das outras linhas neste arquivo. Isso configura alguns parâmetros que estaremos usando em nossa configuração.

Em seguida, temos uma seção [Definition] que define as regras reais para nossas correspondências de filtro. Primeiro, definimos o nome do daemon que estamos monitorando usando o parâmetro _daemon.

Depois disso, passamos pela definição real de failregex, que define os padrões que serão acionados quando uma linha correspondente no arquivo de log é encontrada. Estas são expressões regulares que correspondem com base nos diferentes erros e falhas que podem ser gerados quando um usuário não se autentica corretamente.

Partes da linha como %(__prefix_line)s serão substituídas pelo valor de um parâmetro configurado no arquivo common.conf que nós incluímos. Isso é usado para corresponder as diferentes informações principais que os sistemas operacionais escrevem nos arquivos de log quando usam métodos padrão. Por exemplo, algumas linhas do /var/log/auth.log podem se parecer com isso:

/var/log/auth.log
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]

A parte destacada é um padrão padrão que o sistema operacional insere para fornecer mais contexto. Depois disso, há várias maneiras diferentes que o serviço de firewall iptables escreve tentativas de falha no log.

Vemos duas falhas separadas nas duas primeiras linhas acima (um erro de autenticação PAM e um erro de senha). As expressões regulares definidas no filtro são projetadas para corresponder a qualquer uma das possíveis linhas de falha. Você não deve ajustar nenhuma dessas linhas, mas deve estar ciente da necessidade de capturar todas as entradas de log que sinalizam um erro de uso não autorizado para a aplicação que você está tentando proteger se precisar criar um arquivo de filtro você mesmo.

No final, você pode ver um parâmetro ignoreregex, que está atualmente em branco. Isso pode ser usado para excluir padrões mais específicos que normalmente corresponderiam a uma condição de falha, caso você queira negar o acionador de falha do fail2ban para cenários específicos. Não vamos ajustar isso.

Salve e feche o arquivo quando terminar de examiná-lo.

Examinando o Arquivo de Ação

Agora, vamos dar uma olhada no arquivo de ação. Este arquivo é responsável por configurar o firewall com uma estrutura que permite modificações para banir hosts maliciosos e para adicionar e remover esses hosts conforme necessário.

A ação que nosso serviço SSH invoca é chamada de iptables-multiport. Abra o arquivo associado agora:

  1. sudo nano /etc/fail2ban/action.d/iptables-multiport.conf

Com os comentários removidos, este arquivo se parece com isto:

/etc/fail2ban/action.d/iptables-multiport.conf
[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

O arquivo começa por importar outro arquivo de ação chamado iptables-blocktype.conf que define o parâmetro blocktype, que configura a restrição que será definida quando um cliente for banido. Por padrão, o blocktype é definido para rejeitar pacotes e responder aos pings enviados pelos clientes banidos com uma mensagem de rejeição informando que a porta é inalcançável. Vamos usar isso em nossas regras de banimento abaixo.

Em seguida, chegamos às definições das regras em si. A ação actionstart configura o firewall iptables quando o serviço fail2ban é iniciado. Ele cria uma nova cadeia, adiciona uma regra a essa cadeia para retornar à cadeia de chamada e, em seguida, insere uma regra no início da cadeia INPUT que passa o tráfego correspondente aos protocolos e destinos de porta corretos para a nova cadeia.

Ele faz isso usando os valores que passamos com a ação que definimos em nosso arquivo jail.local. O nome é retirado do cabeçalho de seção para cada serviço. A cadeia, protocolo e porta são retirados da própria linha de ação nesse arquivo.

Aqui, todos os parâmetros que são definidos pelo outro arquivo são referenciados incluindo o nome do parâmetro entre colchetes angulares:

&lt;nome_do_parametro&gt;

Quando descemos para a definição de actionstop, podemos ver que os comandos de firewall estão implementando uma reversão dos comandos de actionstart. Quando o serviço Fail2ban é parado, ele remove limparmente quaisquer regras de firewall que tenha adicionado.

Outra ação chamada actioncheck garante que a cadeia apropriada tenha sido criada antes de tentar adicionar regras de banimento.

Em seguida, chegamos à regra de banimento real, chamada actionban. Esta regra funciona adicionando uma nova regra à cadeia que criamos. A regra corresponde ao endereço IP de origem do cliente infrator – este parâmetro é lido nos logs de autorização quando o limite de maxretry é atingido. Ela institui o bloqueio definido pelo parâmetro blocktype que buscamos na seção [INCLUDE] no topo do arquivo.

A regra actionunban remove esta regra. Isso é feito automaticamente pelo fail2ban quando o tempo de banimento expira.

Finalmente, chegamos à seção [Init]. Isso fornece apenas alguns padrões caso o arquivo de ação seja chamado sem passar todos os valores apropriados.

Como o Serviço Fail2ban Processa Arquivos de Configuração para Implementar Bans

Agora que vimos os detalhes específicos, vamos revisar o processo que ocorre quando o fail2ban é iniciado.

Carregando os Arquivos de Configuração Iniciais

Primeiro, o arquivo principal fail2ban.conf é lido para determinar as condições nas quais o processo principal deve operar. Ele cria os arquivos de socket, PID e log se necessário e começa a usá-los.

Em seguida, o fail2ban lê o arquivo jail.conf para detalhes de configuração. Segue-se lendo, em ordem alfabética, quaisquer arquivos encontrados no diretório jail.d que terminam em .conf. Ele adiciona as configurações encontradas nesses arquivos à sua configuração interna, dando preferência aos novos valores sobre os valores descritos no arquivo jail.conf.

Em seguida, ele procura por um arquivo jail.local e repete esse processo, adaptando os novos valores. Por fim, ele pesquisa novamente o diretório jail.d, lendo em ordem alfabética os arquivos que terminam em .local.

No nosso caso, só temos um arquivo jail.conf e um arquivo jail.local. No nosso arquivo jail.local, só precisamos definir os valores que diferem do arquivo jail.conf. O processo fail2ban agora tem um conjunto de diretivas carregadas na memória que representam uma combinação de todos os arquivos que ele encontrou.

Ele examina cada seção e procura pela diretiva enabled = true. Se encontrar uma, utiliza os parâmetros definidos sob essa seção para construir uma política e decidir quais ações são necessárias. Quaisquer parâmetros que não sejam encontrados na seção do serviço utilizam os parâmetros definidos na seção [DEFAULT].

Analisando os Arquivos de Ação para Determinar as Ações Iniciais

O Fail2ban procura por uma diretiva action para descobrir qual script de ação chamar para implementar as políticas de banimento/desbanimento. Se não encontrar uma, recorre à ação padrão determinada acima.

A diretiva de ação consiste no nome do(s) arquivo(s) de ação que serão lidos, assim como um dicionário chave-valor que passa os parâmetros necessários por esses arquivos. Os valores desses frequentemente assumem a forma de substituições de parâmetros ao fazer referência às configurações configuradas na seção do serviço. A chave “nome” geralmente recebe o valor da variável especial __name__ que será definida como o valor do cabeçalho da seção.

O Fail2ban então utiliza essas informações para encontrar os arquivos associados no diretório action.d. Primeiro, procura pelo arquivo de ação associado com final em .conf e depois complementa as informações encontradas lá com quaisquer configurações contidas em um arquivo .local acompanhante também encontrado no diretório action.d.

Ele analisa esses arquivos para determinar as ações que precisa tomar. Ele lê o valor actionstart para ver as ações que deve executar para configurar o ambiente. Isso frequentemente inclui a criação de uma estrutura de firewall para acomodar regras de banimento no futuro.

As ações definidas neste arquivo utilizam os parâmetros passados para ela pela diretiva action. Ela usará esses valores para criar dinamicamente as regras apropriadas. Se uma variável específica não foi definida, pode-se observar os valores padrão definidos no arquivo de ação para preencher as lacunas.

Analisando os Arquivos de Filtro para Determinar Regras de Filtragem

Os parâmetros para o serviço nos arquivos jail.* também incluem a localização do arquivo de log, bem como o mecanismo de verificação que deve ser usado para verificar o arquivo (isso é definido pelo parâmetro backend). Também inclui um filtro que deve ser usado para determinar se uma linha no log representa uma falha.

O Fail2ban procura no diretório filter.d para encontrar o arquivo de filtro correspondente que termina com .conf. Ele lê este arquivo para definir os padrões que podem ser usados para coincidir com as linhas ofensivas. Em seguida, busca por um arquivo de filtro correspondente que termina com .local para ver se algum dos parâmetros padrão foi sobrescrito.

Ele utiliza as expressões regulares definidas nesses arquivos enquanto lê o arquivo de log do serviço. Ele tenta cada linha failregex definida nos arquivos filter.d contra cada nova linha escrita no arquivo de log do serviço.

Se a expressão regular retornar uma correspondência, ele verifica a linha contra as expressões regulares definidas pelo ignoreregex. Se também corresponder, o fail2ban ignora. Se a linha corresponder a uma expressão em failregex mas não corresponder a uma expressão em ignoreregex, um contador interno é incrementado para o cliente que causou a linha e um timestamp associado é criado para o evento.

Conforme a janela de tempo definida pelo parâmetro findtime nos arquivos jail.* é alcançada (conforme determinado pelo timestamp do evento), o contador interno é decrementado novamente e o evento não é mais considerado relevante para a política de banimento.

Se, ao longo do tempo, forem registradas falhas adicionais de autenticação, cada tentativa incrementa o contador. Se o contador atingir o valor definido pelo parâmetro maxretry dentro da janela de tempo configurada, o fail2ban institui um banimento chamando a ação actioncheck para o serviço conforme definido nos arquivos action.d/ para o serviço. Isso é para determinar se a ação actionstart configurou a estrutura necessária. Em seguida, chama a ação actionban para banir o cliente ofensivo. Define também um timestamp para este evento.

Quando o tempo especificado pelo parâmetro bantime tiver decorrido, o fail2ban remove o banimento do cliente chamando a ação actionunban.

Conclusão

Agora você tem um entendimento bastante profundo de como o fail2ban opera. Quando você se desvia da configuração padrão, é útil saber como o fail2ban funciona para manipular seu comportamento de maneira previsível.

Para aprender como proteger outros serviços com o fail2ban, você pode ler Como Proteger um Servidor Nginx com Fail2Ban no Ubuntu 22.04.

Source:
https://www.digitalocean.com/community/tutorials/how-fail2ban-works-to-protect-services-on-a-linux-server