Aula 21: fail2ban — proteção automática contra ataques de força bruta

Aula 21: fail2ban — proteção automática contra ataques de força bruta

Em mais de 15 anos atuando com infraestrutura Linux e segurança da informação, uma das ferramentas que mais nos salvou de incidentes graves em servidores foi o fail2ban. Se você administra qualquer serviço exposto à internet — SSH, Apache, Nginx, Postfix, Dovecot, MySQL — saiba que ataques de força bruta são uma realidade diária. O fail2ban atua como um escudo inteligente: ele monitora logs em tempo real, identifica padrões de acesso malicioso e bloqueia automaticamente os IPs ofensores usando regras de firewall. Nesta aula, vamos além do básico “instalar e esquecer”. Você sairá daqui capaz de criar jails personalizadas, ajustar thresholds de acordo com o perfil do seu ambiente e auditar bloqueios em tempo real, exatamente como fazemos em nossos projetos na JRT Technology Solutions para proteger ambientes críticos de clientes corporativos.

O cenário é este: um servidor recém-implantado com SSH na porta 22 recebe, em média, centenas de tentativas de login por hora vindas de bots espalhados pelo mundo. Sem proteção automatizada, a única defesa é a complexidade da senha — e todos sabemos que senhas, por mais fortes, são um elo frágil. O fail2ban resolve esse problema de forma elegante: após um número configurável de falhas, o IP atacante é banido por um período determinado. Isso reduz drasticamente a superfície de ataque e libera recursos do sistema que estariam sendo consumidos por essas tentativas. Nesta aula, você aprenderá a dominar completamente essa ferramenta, desde a instalação até a criação de regras customizadas para cenários complexos.

Os pré-requisitos para acompanhar esta aula são sólidos: você precisa ter um servidor Linux com acesso root ou sudo, conhecimento intermediário de linha de comando, familiaridade com arquivos de log do sistema e noções básicas de iptables ou firewalld. Se você concluiu as aulas anteriores do nosso curso, especialmente as aulas sobre gerenciamento de serviços com systemd e fundamentos de firewall, estará perfeitamente preparado. Vamos trabalhar com dois ambientes distintos: distribuições baseadas em Debian (Ubuntu Server 24.04 LTS) e distribuições baseadas em RHEL (Rocky Linux 9 e CentOS Stream).

Uma observação importante antes de começarmos: em todos os servidores que implementamos na JRT Technology Solutions, o fail2ban é um dos primeiros pacotes que instalamos logo após o firewall base. Ele não substitui um firewall bem configurado, mas complementa a estratégia de defesa em profundidade. Ao final desta aula, você terá um serviço fail2ban em produção, com jails ativas para SSH e serviços web, logs centralizados, notificações por e-mail e um entendimento profundo de cada diretiva de configuração. Prepare seu terminal e vamos começar.

O que você vai aprender nesta aula

  • Compreender a arquitetura interna do fail2ban — como ele interage com logs, filtros e ações
  • Instalar e configurar o fail2ban em Ubuntu/Debian e Rocky Linux/CentOS/RHEL
  • Dominar a estrutura de diretórios e arquivos de configuração: jail.conf, jail.local, filter.d e action.d
  • Criar jails personalizadas para proteger SSH, Apache, Nginx e serviços customizados
  • Escrever filtros de regex sob medida para logs de aplicações específicas
  • Configurar notificações por e-mail e integração com ferramentas externas
  • Utilizar o cliente fail2ban-client para monitoramento e gerenciamento em tempo real
  • Diagnosticar e corrigir os erros mais comuns na operação do fail2ban
  • Aplicar boas práticas de segurança e desempenho em ambientes de produção

Pré-requisitos e Ambiente

Para executar todos os exemplos desta aula, você precisará de um servidor Linux recém-instalado ou uma máquina virtual com pelo menos 1 GB de RAM e 10 GB de disco. O sistema deve ter acesso à internet para instalação de pacotes. Vamos trabalhar com dois ambientes principais, e você pode escolher o que melhor se adequa ao seu cenário: Ubuntu Server 24.04 LTS (ou 22.04 LTS) para o ecossistema Debian, e Rocky Linux 9 (ou CentOS Stream 9) para o ecossistema RHEL. Em ambos os casos, você deve ter o serviço SSH ativo e acessível, pois será nosso primeiro alvo de proteção.

Verifique também se o firewall do sistema está em execução. No Ubuntu, utilizaremos o ufw (Uncomplicated Firewall) como frontend para iptables. No Rocky Linux/CentOS, utilizaremos o firewalld. O fail2ban se integrará diretamente com essas ferramentas para aplicar os bloqueios. Certifique-se de que o daemon de logs (rsyslog ou systemd-journald) está ativo, pois é dos arquivos de log que o fail2ban extrai as informações sobre tentativas de acesso. Por fim, tenha um editor de texto de sua preferência instalado — utilizaremos o vim nos exemplos, mas você pode usar nano se preferir.

Um ponto crucial que muitos tutoriais ignoram: verifique a hora e a data do seu servidor com o comando timedatectl. O fail2ban depende de timestamps precisos nos arquivos de log para correlacionar eventos. Se o relógio do servidor estiver incorreto, as regras de banimento podem não ser aplicadas corretamente. Recomendamos configurar o NTP (chrony ou systemd-timesyncd) antes de prosseguir. Em nossos projetos na JRT Technology Solutions, a sincronização de horário é um pré-requisito não negociável antes de implementar qualquer ferramenta de monitoramento de logs.

Arquitetura do fail2ban — como a mágica acontece

Antes de instalar qualquer pacote, é fundamental entender a arquitetura interna do fail2ban. O sistema é composto por três camadas principais: filtros (filters), jails e ações (actions). Os filtros são expressões regulares (regex) que definem quais linhas de log devem ser consideradas como tentativas maliciosas. Cada filtro está associado a um arquivo de log específico. Quando uma linha corresponde ao padrão, o fail2ban registra o IP de origem e incrementa um contador interno para aquela jail.

As jails são as unidades de configuração que conectam um filtro a uma ação. Cada jail define: qual arquivo de log monitorar, qual filtro aplicar, quantas falhas são toleradas antes do banimento (parâmetro maxretry), em qual janela de tempo essas falhas devem ocorrer (parâmetro findtime) e por quanto tempo o IP permanecerá banido (parâmetro bantime). Essa combinação de parâmetros é o coração da política de segurança que você está implementando. Uma jail bem calibrada bloqueia atacantes reais sem punir usuários legítimos que erraram a senha algumas vezes.

As ações são os mecanismos que efetivamente bloqueiam o tráfego. A ação padrão utiliza iptables para inserir regras de DROP ou REJECT na cadeia apropriada. Existem ações alternativas para firewalld (via firewall-cmd), ufw, hosts.deny (obsoleto, mas ainda usado em sistemas legados), Cloudflare API, nginx e até mesmo para enviar e-mails de notificação. O fail2ban também suporta ações combinadas: por exemplo, bloquear com iptables e simultaneamente enviar um alerta por e-mail ao administrador.

Todo esse fluxo é orquestrado pelo daemon fail2ban-server, que roda em background e é gerenciado pelo systemd. O comando fail2ban-client é a interface de linha de comando para interagir com o servidor: permite iniciar, parar, recarregar a configuração, consultar o status das jails e até mesmo banir ou desbanir IPs manualmente. Entender essa separação entre cliente e servidor é importante para diagnosticar problemas: se o cliente não consegue se comunicar com o servidor via socket, as operações falharão silenciosamente.

Instalação passo a passo — Ubuntu/Debian e Rocky Linux/CentOS

Vamos iniciar com a instalação em sistemas baseados em Debian. Em distribuições como Ubuntu Server, o pacote fail2ban está disponível nos repositórios oficiais e pode ser instalado diretamente com o gerenciador de pacotes apt. Abra um terminal e execute os comandos abaixo. Primeiro, é boa prática atualizar a lista de pacotes disponíveis para garantir que você está instalando a versão mais recente oferecida pela distribuição.

# Atualiza o índice de pacotes do repositório
sudo apt update

# Instala o pacote fail2ban e suas dependências
sudo apt install fail2ban -y

# Verifica se o serviço foi iniciado automaticamente
sudo systemctl status fail2ban
● fail2ban.service - Fail2Ban Service
     Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2026-07-19 10:15:30 UTC; 5s ago
       Docs: man:fail2ban(1)
   Main PID: 1242 (fail2ban-server)
      Tasks: 5 (limit: 2310)
     Memory: 12.4M
        CPU: 234ms
     CGroup: /system.slice/fail2ban.service
             └─1242 /usr/bin/python3 /usr/bin/fail2ban-server -xf start

Jul 19 10:15:30 ubuntu-server systemd[1]: Started Fail2Ban Service.
Jul 19 10:15:30 ubuntu-server fail2ban-server[1242]: Server ready

Agora, vamos à instalação em sistemas da família Red Hat, como Rocky Linux 9 e CentOS Stream. Nesses sistemas, o fail2ban está disponível no repositório EPEL (Extra Packages for Enterprise Linux). O EPEL precisa estar habilitado antes da instalação. Se você ainda não o instalou, o comando abaixo resolve isso. Em seguida, instalamos o pacote e iniciamos o serviço.

# Instala o repositório EPEL (necessário para acessar o pacote fail2ban)
sudo dnf install epel-release -y

# Instala o pacote fail2ban
sudo dnf install fail2ban -y

# Habilita o serviço para iniciar automaticamente no boot
sudo systemctl enable fail2ban

# Inicia o serviço imediatamente
sudo systemctl start fail2ban

# Verifica o status do serviço
sudo systemctl status fail2ban
● fail2ban.service - Fail2Ban Service
     Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; vendor preset: disabled)
     Active: active (running) since Sun 2026-07-19 10:22:45 UTC; 3s ago
       Docs: man:fail2ban(1)
   Main PID: 1891 (fail2ban-server)
      Tasks: 3 (limit: 11140)
     Memory: 18.7M
        CPU: 156ms
     CGroup: /system.slice/fail2ban.service
             └─1891 /usr/bin/python3 /usr/bin/fail2ban-server -xf start

Jul 19 10:22:45 rocky9-server systemd[1]: Started Fail2Ban Service.
Jul 19 10:22:45 rocky9-server fail2ban-server[1891]: Server ready

Observe que, em ambas as distribuições, o serviço sobe com o status active (running). Isso significa que o daemon está operacional. Contudo, neste momento, nenhuma jail está ativa porque ainda não configuramos nada. O arquivo de configuração padrão /etc/fail2ban/jail.conf contém definições de jails comentadas e prontas para serem personalizadas. A abordagem recomendada — e que seguimos rigorosamente na JRT Technology Solutions — é nunca editar o arquivo jail.conf diretamente, pois ele pode ser sobrescrito durante atualizações do pacote. Em vez disso, criamos um arquivo jail.local que sobrescreve as definições padrão de forma segura e versionável.

Configuração Essencial — O arquivo jail.local e a jail SSH

O arquivo /etc/fail2ban/jail.local é onde você colocará todas as suas customizações. Ele é lido após o jail.conf e suas diretivas têm precedência sobre as originais. Vamos criar este arquivo com uma configuração robusta para a jail do SSH, que será nosso primeiro escudo de proteção. Abaixo está o conteúdo completo que recomendamos como ponto de partida para servidores de produção. Cada linha está comentada para que você entenda exatamente o que está configurando.

# /etc/fail2ban/jail.local
# Configuração customizada do fail2ban para ambiente de produção
# Criado em 19/07/2026 — Aula 21 do curso Linux do Zero ao Avançado

[DEFAULT]

# IPs e redes que NUNCA serão banidos — liste aqui seus IPs de administração
# e redes internas confiáveis para evitar lockout acidental
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16 10.0.0.0/8

# Tempo de banimento em segundos (3600 = 1 hora)
# Em produção, recomendamos começar com 1 hora e ajustar conforme necessidade
bantime = 3600

# Janela de tempo para contabilizar falhas (600 segundos = 10 minutos)
# Se maxretry falhas ocorrerem dentro deste intervalo, o IP é banido
findtime = 600

# Número máximo de falhas permitidas dentro da janela findtime
maxretry = 5

# Backend para leitura de logs — "auto" tenta systemd primeiro, depois arquivos tradicionais
# Em sistemas modernos com systemd, "systemd" é mais eficiente, mas "polling" lê arquivos diretamente
backend = auto

# Destinatário dos alertas por e-mail (opcional, configure se desejar notificações)
destemail = admin@seudominio.com.br

# Remetente dos e-mails de notificação
sendername = fail2ban

# Ação padrão para todas as jails — bloqueia com iptables e envia e-mail com whois
# Alterne para %(action_mwl)s se quiser logs mais detalhados por e-mail
action = %(action_mw)s

# Habilita a codificação UTF-8 para logs
logencoding = utf-8

# Caminho para o socket de comunicação com o fail2ban-client
socket = /var/run/fail2ban/fail2ban.sock

[sshd]

# Habilita a jail do SSH — fundamental para proteger o acesso remoto
enabled = true

# Filtro a ser utilizado — o arquivo /etc/fail2ban/filter.d/sshd.conf contém as regex
filter = sshd

# Porta monitorada pela jail — padrão 22, ajuste se seu SSH usa porta não padrão
port = ssh

# Arquivo de log monitorado — %(sshd_log)s é uma variável que expande para o caminho correto
logpath = %(sshd_log)s

# Sobrescreve o maxretry padrão para SSH — 3 falhas é um bom equilíbrio
maxretry = 3

# Modo agressivo: ativa detecção adicional de padrões de ataque
mode = aggressive

Vamos detalhar as diretivas mais importantes deste arquivo. A seção [DEFAULT] estabelece valores que serão herdados por todas as jails, a menos que sejam explicitamente sobrescritos. O parâmetro ignoreip é crítico: se você esquecer de incluir seu próprio IP de administração, poderá ser banido do seu servidor após exceder o número de tentativas de senha. Na JRT Technology Solutions, sempre incluímos não apenas o IP do administrador, mas também as faixas de rede interna da empresa e a rede de gerenciamento do data center.

O parâmetro bantime define por quanto tempo um IP permanecerá bloqueado. O valor de 3600 segundos (1 hora) é um bom ponto de partida. Em ambientes com muitos ataques automatizados, você pode aumentar para 86400 (24 horas) ou até mesmo configurar banimentos progressivos usando a ação recidive, que abordaremos mais adiante. O findtime e maxretry trabalham juntos: com findtime=600 e maxretry=5, o sistema banirá qualquer IP que cause 5 falhas em um período de 10 minutos.

A seção [sshd] habilita a proteção do SSH. O parâmetro mode = aggressive merece atenção especial. No modo normal, o filtro sshd.conf detecta apenas falhas de autenticação por senha. No modo agressivo, ele também detecta tentativas de conexão que não completam o handshake SSH (como scanners de porta), tornando a detecção mais sensível. Se você usa autenticação por chave SSH, o modo agressivo pode evitar que scanners consumam recursos do seu servidor mantendo conexões semi-abertas.

Após criar o arquivo jail.local, é necessário recarregar a configuração do fail2ban para que as mudanças entrem em vigor. Diferente de muitos tutoriais que ensinam a reiniciar o serviço com systemctl restart, a forma correta e mais segura em produção é utilizar o fail2ban-client reload, que recarrega a configuração sem interromper os banimentos ativos. Se você reiniciar o serviço pelo systemd, todos os IPs que estavam banidos serão liberados momentaneamente até que o serviço suba novamente e reaplique as regras — uma janela de vulnerabilidade que deve ser evitada.

# Recarrega a configuração do fail2ban sem derrubar banimentos ativos
sudo fail2ban-client reload

# Verifica o status geral do servidor fail2ban
sudo fail2ban-client status

# Verifica o status específico da jail sshd
sudo fail2ban-client status sshd
Status
|- Number of jail:      1
`- Jail list:           sshd

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 0
   |- Total banned:     0
   `- Banned IP list:

Observe que a saída mostra a jail sshd ativa e monitorando o arquivo /var/log/auth.log. Neste momento, não há IPs banidos, pois nenhum ataque foi detectado ainda. Na seção de verificação, simularemos um ataque real para confirmar que o sistema está funcionando.

Criando Jails Personalizadas para Apache e Nginx

O SSH é apenas o ponto de partida. Em servidores web, os ataques de força bruta contra painéis administrativos (WordPress, Joomla, Drupal) e tentativas de exploração de vulnerabilidades são extremamente comuns. Vamos criar jails personalizadas para proteger servidores Apache e Nginx. Começaremos pelo Apache, utilizando filtros já existentes que acompanham o pacote fail2ban. Inspecione o diretório /etc/fail2ban/filter.d/ e você encontrará dezenas de filtros pré-configurados para os mais diversos serviços.

Abaixo, adicionamos ao jail.local as configurações para o Apache. Criaremos três jails distintas: uma para autenticação básica HTTP (apache-auth), uma para páginas não encontradas suspeitas que indicam scanning (apache-noscript) e uma para proteção contra ataques de negação de serviço em PHP (apache-badbots). Cada uma monitora logs diferentes e tem thresholds específicos adequados ao tipo de ataque que detectam.

# Adicione estas seções ao arquivo /etc/fail2ban/jail.local
# Coloque-as após a seção [sshd]

[apache-auth]
enabled = true
filter = apache-auth
port = http,https
logpath = /var/log/apache2/error.log
maxretry = 3
bantime = 7200
findtime = 300

[apache-noscript]
enabled = true
filter = apache-noscript
port = http,https
logpath = /var/log/apache2/error.log
maxretry = 2
bantime = 86400
findtime = 120

[apache-badbots]
enabled = true
filter = apache-badbots
port = http,https
logpath = /var/log/apache2/access.log
maxretry = 1
bantime = 172800
findtime = 86400

[apache-overflows]
enabled = true
filter = apache-overflows
port = http,https
logpath = /var/log/apache2/error.log
maxretry = 2
bantime = 3600
findtime = 600

Para o Nginx, a abordagem é similar, mas os caminhos de log são diferentes. O Nginx não possui módulo de autenticação HTTP nativo como o Apache (ele usa auth_basic com arquivos de senha), mas ainda assim podemos protegê-lo contra acessos repetidos a URLs sensíveis. O filtro nginx-http-auth monitora falhas de autenticação, enquanto o nginx-botsearch detecta bots tentando acessar URLs típicas de administração. Adicione estas seções ao mesmo arquivo jail.local:

# Jails para Nginx — adicione ao /etc/fail2ban/jail.local

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 7200
findtime = 300

[nginx-botsearch]
enabled = true
filter = nginx-botsearch
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 86400
findtime = 300

[nginx-limit-req]
enabled = true
filter = nginx-limit-req
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 1800
findtime = 120

Note que os parâmetros bantime e findtime variam conforme a severidade do ataque. Para apache-badbots, que detecta scanners automatizados conhecidos, usamos um bantime de 172800 segundos (48 horas) e maxretry igual a 1 — ou seja, na primeira ocorrência, o IP já é banido por dois dias. Essa política agressiva se justifica porque bots maliciosos conhecidos não têm motivo legítimo para acessar seu servidor. Por outro lado, o apache-auth usa parâmetros mais conservadores (maxretry=3, bantime=7200), pois usuários reais podem errar a senha ocasionalmente.

Escrevendo Filtros Customizados com Regex

Os filtros pré-configurados cobrem a maioria dos cenários comuns, mas em ambientes corporativos frequentemente precisamos proteger aplicações customizadas. Na JRT Technology Solutions, já enfrentamos situações em que uma API interna em Python ou uma aplicação legada em Java estava sofrendo ataques de força bruta e não existia filtro pronto. A solução foi criar filtros personalizados usando expressões regulares. Vamos aprender a fazer isso na prática.

Imagine uma aplicação web que registra tentativas de login em /var/log/minhaapp/login.log com o seguinte formato:

2026-07-19 11:23:45 ERRO: Falha de autenticação para usuário 'admin' de IP 203.0.113.42
2026-07-19 11:23:50 ERRO: Falha de autenticação para usuário 'root' de IP 203.0.113.42
2026-07-19 11:23:55 ERRO: Falha de autenticação para usuário 'teste' de IP 198.51.100.17

Criaremos um filtro chamado minhaapp. Para isso, criamos o arquivo /etc/fail2ban/filter.d/minhaapp.conf com o conteúdo abaixo. A diretiva failregex contém a expressão regular que captura o IP do atacante. O padrão <HOST> é uma tag especial do fail2ban que identifica automaticamente endereços IPv4 e IPv6.

# /etc/fail2ban/filter.d/minhaapp.conf
# Filtro customizado para aplicação MinhaApp
# Criado em 19/07/2026

[Definition]

# Padrão de log que indica tentativa de login mal-sucedida
failregex = ^\s*<DATE> ERRO: Falha de autenticação para usuário '.*' de IP <HOST>\s*$

# Linhas que devem ser ignoradas (opcional — útil para excluir falsos positivos)
ignoreregex =

# Exemplo de journalmatch se estiver usando o backend systemd
# journalmatch = _SYSTEMD_UNIT=minhaapp.service

Agora, adicionamos a jail correspondente no jail.local:

[minhaapp]
enabled = true
filter = minhaapp
port = 8080
logpath = /var/log/minhaapp/login.log
maxretry = 5
bantime = 3600
findtime = 600
action = iptables-multiport[name=minhaapp, port="8080", protocol=tcp]

A criação de expressões regulares para o fail2ban exige atenção a alguns detalhes. Primeiro, sempre começamos a regex com ^\s* para consumir espaços em branco opcionais no início da linha. O <HOST> é substituído internamente pelo padrão (?:::f{4,6}:)?(?P<host>[\w\-.^_]*\w) (ou similar, dependendo da versão), que captura IPs em diversos formatos. Após criar um filtro customizado, utilize o comando fail2ban-regex para testá-lo contra um arquivo de log real antes de colocá-lo em produção. Isso evita surpresas desagradáveis com regex que não capturam o esperado.

# Testa o filtro contra o arquivo de log
# Sintaxe: fail2ban-regex <arquivo_log> <arquivo_filtro>
sudo fail2ban-regex /var/log/minhaapp/login.log /etc/fail2ban/filter.d/minhaapp.conf

# Para testar com uma linha específica (modo interativo)
echo "2026-07-19 11:23:45 ERRO: Falha de autenticação para usuário 'admin' de IP 203.0.113.42" | sudo fail2ban-regex - /etc/fail2ban/filter.d/minhaapp.conf
Running tests
=============

Use   failregex filter : minhaapp
Use      log file      : /var/log/minhaapp/login.log
Use         encoding    : UTF-8


Results
=======

Failregex: 3 total
|-  #) [# of hits] regular expression
|   1) [3] ^\s*<DATE> ERRO: Falha de autenticação para usuário '.*' de IP <HOST>\s*$
`-

Ignoreregex: 0 total

Date template hits:
|- [# of hits] date format
|  [3] {^LN-BEG}ExYear(?P<_sep>-)Month(?P=_sep)Day(?:T|  ?)24hour:Minute:Second(?:[.,]Microseconds)?(?:\s*Zone offset)?
`-

Lines: 3 lines, 0 ignored, 3 matched, 0 missed
[processed in 0.02 sec]

|- Matched line(s):
|  203.0.113.42  Sun Jul 19 11:23:45 2026
|  203.0.113.42  Sun Jul 19 11:23:50 2026
|  198.51.100.17  Sun Jul 19 11:23:55 2026
`-

O relatório mostra que 3 linhas foram processadas e 3 corresponderam ao padrão. Os IPs capturados são exibidos no final — exatamente o que esperávamos. Se você encontrar “0 matched”, revise sua regex. É uma boa prática testar sempre antes de ativar a jail, especialmente em produção.

Verificando a Instalação e Testando a Configuração

Esta é uma das seções mais importantes da aula. Não basta configurar; é preciso validar que o fail2ban está efetivamente bloqueando ataques. Vamos realizar três tipos de verificação: validação estrutural da configuração, teste funcional com simulação de ataque e inspeção das regras de firewall aplicadas. Comece verificando se não há erros de sintaxe nos arquivos de configuração. O próprio fail2ban-client oferece um modo de teste que valida tudo sem aplicar mudanças.

# Testa a configuração sem iniciar o serviço — útil para detectar erros de sintaxe
sudo fail2ban-client -t

# Força o reload da configuração e observa mensagens de erro
sudo fail2ban-client reload
OK: configuration test is successful

Agora, vamos simular um ataque de força bruta contra o SSH para ver o banimento em ação. De uma máquina externa (ou de outro terminal no mesmo servidor, usando IP de loopback — mas lembre-se de que 127.0.0.1 está em ignoreip, então use um container ou máquina virtual), tente conectar via SSH com senhas incorretas repetidamente. Aqui está uma sequência de comandos que você pode executar a partir de uma segunda máquina Linux na mesma rede.

# Na máquina atacante (substitua pelo IP real do servidor com fail2ban)
ssh usuarioinexistente@192.168.1.100
# Digite senha incorreta 3 vezes
ssh usuarioinexistente@192.168.1.100
# Digite senha incorreta novamente
ssh usuarioinexistente@192.168.1.100
# Digite senha incorreta mais uma vez — na terceira tentativa, o IP será banido
ssh usuarioinexistente@192.168.1.100
# Agora a conexão deve ficar travada ou recusada

Após as tentativas, volte ao servidor e verifique o status da jail:

# Verifica o status da jail sshd após as tentativas
sudo fail2ban-client status sshd

# Exibe todas as jails ativas e seus status resumidos
sudo fail2ban-client status

# Consulta o log do fail2ban para ver os banimentos ocorridos
sudo tail -f /var/log/fail2ban.log
Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     3
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 1
   |- Total banned:     1
   `- Banned IP list:   192.168.1.200

Status
|- Number of jail:      7
`- Jail list:           apache-auth, apache-badbots, apache-noscript, apache-overflows, nginx-http-auth, nginx-botsearch, sshd

2026-07-19 11:35:22,123 fail2ban.actions        [1242]: NOTICE  [sshd] Ban 192.168.1.200
2026-07-19 11:35:22,345 fail2ban.filter         [1242]: INFO    [sshd] Found 192.168.1.200 - 2026-07-19 11:35:21

O IP 192.168.1.200 aparece na lista de banidos. Para confirmar que o bloqueio está efetivo no firewall, inspecione as regras de iptables. O fail2ban cria uma cadeia própria chamada f2b-sshd (ou similar, seguindo o padrão f2b-<nome_da_jail>) e insere regras de bloqueio nela.

# Lista as regras de iptables e filtra pelas cadeias do fail2ban
sudo iptables -L -n -v | grep -A 5 "f2b"

# Ou, se estiver usando ufw no Ubuntu:
sudo ufw status verbose

# No Rocky Linux com firewalld:
sudo firewall-cmd --direct --get-all-rules
Chain f2b-sshd (1 references)
 pkts bytes target     prot opt in     out     source               destination
    5   300 REJECT     all  --  *      *       192.168.1.200        0.0.0.0/0            reject-with icmp-port-unreachable
    0     0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0

Observe a regra REJECT para o IP 192.168.1.200. O contador pkts mostra 5 pacotes bloqueados — ou seja, o atacante tentou continuar a comunicação após o banimento e foi barrado imediatamente. Essa verificação tripla (status do cliente, log do fail2ban e regras de firewall) é o procedimento padrão que adotamos na JRT Technology Solutions para auditorias de segurança em nossos ambientes gerenciados.

Erros Comuns e Como Resolver

Mesmo administradores experientes encontram problemas com o fail2ban. Separamos os quatro erros mais frequentes que vemos em campo, junto com diagnóstico e solução detalhada para cada um. Guarde esta seção como referência — ela vai lhe poupar horas de troubleshooting.

  • Erro 1: Serviço inicia, mas nenhuma jail fica ativa.
    Sintoma: Após a instalação, systemctl status fail2ban mostra o serviço rodando, mas fail2ban-client status retorna “0 jails”.
    Causa: O arquivo jail.local não foi criado ou as jails estão com enabled = false. O fail2ban não ativa jails por padrão; é necessário habilitá-las explicitamente.
    Solução: Verifique o conteúdo de /etc/fail2ban/jail.local e confirme que cada jail possui enabled = true. Execute fail2ban-client reload e confira novamente com fail2ban-client status. Se ainda estiver zerado, examine /var/log/fail2ban.log em busca de mensagens de erro como “Unable to open log file”.
  • Erro 2: “WARNING Unable to find a log file” ou jail não monitora logs.
    Sintoma: O log do fail2ban exibe a mensagem WARNING [sshd] Unable to find a log file e a jail nunca bane IPs.
    Causa: O caminho do arquivo de log especificado em logpath está incorreto ou o arquivo não existe. Isso é comum ao migrar configurações de distribuições diferentes (ex: no Ubuntu, o log SSH fica em /var/log/auth.log, enquanto no Rocky Linux fica em /var/log/secure).
    Solução: Use a variável %(sshd_log)s sempre que possível. Se precisar de caminho fixo, localize o arquivo correto com ls -la /var/log/auth.log /var/log/secure. Ajuste o logpath e recarregue o serviço. Em sistemas com systemd-journald puro, altere backend = systemd no jail.local e remova a diretiva logpath, pois o sistema lerá diretamente do journal.
  • Erro 3: IPs legítimos sendo banidos (lockout do administrador).
    Sintoma: Você mesmo ou outros usuários autorizados perdem acesso ao servidor após algumas tentativas de login mal-sucedidas.
    Causa: O parâmetro ignoreip não foi configurado corretamente no jail.local, ou os IPs de origem não estão incluídos na lista de exceções.
    Solução: Se você está lockado, acesse o servidor via console (out-of-band) ou use outro IP não banido. Edite /etc/fail2ban/jail.local e adicione seu IP ou rede à diretiva ignoreip. Para desbanir manualmente um IP específico: sudo fail2ban-client set sshd unbanip SEU_IP. Considere aumentar o maxretry para 5 temporariamente enquanto ajusta a política.
  • Erro 4: “fail2ban-regex” não captura linhas de log esperadas.
    Sintoma: O teste com fail2ban-regex retorna “0 matched”, mas você tem certeza de que as linhas existem no arquivo de log.
    Causa: A expressão regular está incorreta — muitas vezes por não escapar caracteres especiais (., *, [), por não considerar espaços em branco, ou porque a data no log não é reconhecida pelo detector automático de timestamps.
    Solução: Use o flag -v (verbose) ou –print-all-missed no comando fail2ban-regex para ver quais linhas não foram capturadas. Construa a regex incrementalmente, testando com uma linha de cada vez. Para problemas de data, adicione manualmente um padrão de timestamp no filtro usando datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S. Consulte man fail2ban-regex para sintaxe detalhada dos padrões de data.
  • Erro 5: Banimentos não persistem após reboot do servidor.
    Sintoma: Todos os IPs banidos são liberados quando o servidor é reiniciado, e ataques recomeçam do zero.
    Causa: Por padrão, o fail2ban armazena banimentos apenas em memória RAM. Ao reiniciar, o estado é perdido.
    Solução: Configure o banco de dados persistente. No jail.local, na seção [DEFAULT], adicione dbfile = /var/lib/fail2ban/fail2ban.sqlite3 e dbpurgeage = 86400 (mantém banimentos por 24 horas no banco após expirarem). O fail2ban salvará e restaurará automaticamente os banimentos entre reinicializações usando esse arquivo SQLite.

Boas Práticas e Dicas Avançadas

Com o fail2ban em funcionamento, é hora de refinar a configuração com práticas que aplicamos em ambientes de produção de alto tráfego. A primeira recomendação é implementar recidive — uma jail especial que monitora o próprio log do fail2ban e bane IPs que são pegos repetidamente. Isso cria uma política de “reincidência”: se um IP foi banido três vezes em 24 horas, ele recebe um banimento muito mais longo (por exemplo, uma semana). Configure-a no jail.local:

[recidive]
enabled = true
logpath = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime = 604800
findtime = 86400
maxretry = 3

Outra prática essencial é a configuração de alertas por e-mail. Embora tenhamos configurado o parâmetro action global com %(action_mw)s, você pode personalizar ainda mais. O action_mw envia um e-mail com o IP banido e informações de whois. Para ambientes com alto volume de banimentos, recomendamos usar action = %(action_)s (sem notificação) e complementar com um script externo que agrega e envia relatórios diários — assim você não sobrecarrega sua caixa de entrada com centenas de alertas. Na JRT Technology Solutions, integramos o fail2ban com ferramentas de SIEM via syslog, centralizando todos os eventos de segurança em um painel único.

A tabela abaixo resume as principais ações disponíveis e quando utilizá-las:

Ação Descrição Quando usar
action_ Apenas bloqueia (iptables/firewalld), sem notificações Ambientes com alto volume, SIEM externo coleta logs
action_mw Bloqueia e envia e-mail com whois Servidores com tráfego moderado, admin único
action_mwl Bloqueia, envia e-mail com whois e trechos do log Depuração, análise de incidentes
action_cf_mwl Integra com Cloudflare via API Sites protegidos por Cloudflare WAF

Quer aprender na prática com especialistas?

A JRT Technology Solutions oferece treinamentos e implementação de Linux para equipes corporativas.



Falar no WhatsApp

Thiago Paes Rodrigues

Com mais de 22 anos de experiência em Tecnologia da Informação, este profissional construiu uma trajetória sólida como empresário, atuando de forma estratégica na implementação de soluções tecnológicas que otimizam processos e impulsionam resultados em diferentes setores.