Fail2ban: Proteger SSH e Serviços Linux contra Ataques de Força Bruta em 2026

Linux · Segurança · Fail2ban · SSH · Brute Force · Nftables | ✎ Duarte Spínola | 10 de Julho de 2026

O fail2ban é a ferramenta padrão para protecção automática contra ataques de força bruta em servidores Linux. Monitoriza ficheiros de log, detecta padrões de autenticação falhada, e bloqueia automaticamente os IPs ofensivos no firewall. Este guia cobre a instalação, configuração de jails para SSH, Nginx, Apache, e serviços custom, tuning de thresholds para PME, integração com nftables/iptables, e diagnóstico de problemas. Inclui comandos testados em Ubuntu 24.04, Debian 12, RHEL 9 e CentOS Stream 9.

Conteúdos

1. O que Está a Acontecer — Ameaças de Força Bruta em 2026

Ataques de força bruta contra serviços expostos — especialmente SSH — são constantes e automatizados. Bots percorrem ranges inteiros de IPs à procura de servidores com a porta 22 aberta, e tentam milhares de combinações de utilizador/password por minuto. O MITRE ATT&CK cataloga este vector como T1110.004 (Brute Force: SSH) (MITRE ATT&CK T1110.004).

Num servidor cloud típico (Hetzner, DigitalOcean, AWS) recém-criado com SSH na porta 22 e autenticação por password activa, as primeiras tentativas de brute-force aparecem em menos de 5 minutos. Um servidor sem protecção pode receber 10.000+ tentativas por dia.

O fail2ban resolve este problema de forma automática: lê os logs de autenticação em tempo real, identifica IPs com múltiplas falhas, e adiciona regras de firewall para os bloquear temporariamente. É a primeira linha de defesa, complementar a chaves SSH e hardening do sshd_config (fail2ban GitHub, Arch Wiki — Fail2ban).

Fail2ban não substitui hardening. Desactivar PasswordAuthentication no sshd_config continua a ser a medida mais eficaz. O fail2ban é uma camada adicional — reduz ruído nos logs, bloqueia bots ruidosos, e protege serviços que não suportam chaves (web apps, painéis de controlo, FTP).

2. Cenários em que o Fail2ban é Essencial

Cenário Risco Jail recomendada
Servidor cloud com SSH na porta 22 Brute force SSH constante sshd (jail padrão)
Servidor web com Nginx/Apache Ataques a wp-login.php, /admin, .htaccess nginx-limit-req, nginx-botsearch
Servidor de email (Postfix/Dovecot) Brute force SMTP/IMAP postfix, dovecot
FTP/SFTP server (vsftpd, proftpd) Brute force FTP vsftpd, proftpd
Painel de controlo (cPanel, Plesk) Ataques ao login do painel Jail custom com regex específica
VPN server (OpenVPN, WireGuard) Tentativas de ligação inválidas Jail custom para OpenVPN log
Servidor com múltiplos serviços Vários vectores de ataque Múltiplas jails + recidive para reincidentes
PME com IPs fixos de funcionários Bloquear IPs legítimos por engano Whitelist de IPs da empresa

3. Instalar e Configurar o Fail2ban

Instalação

# Ubuntu / Debian
sudo apt update
sudo apt install fail2ban

# RHEL / CentOS Stream / Rocky / Alma
sudo dnf install fail2ban
# O EPEL é necessário em RHEL:
# sudo dnf install epel-release && sudo dnf install fail2ban

# Verificar versão
fail2ban-client --version
# Fail2Ban v1.1.0

Estrutura de ficheiros

# NUNCA editar jail.conf directamente — criar jail.local
/etc/fail2ban/
├── jail.conf          # Configuração padrão (NÃO EDITAR — perde-se em updates)
├── jail.local         # Override local (criar aqui)
├── fail2ban.local     # Override do daemon
├── filter.d/          # Filtros regex por serviço
│   ├── sshd.conf      # Filtro para SSH
│   ├── nginx-http-auth.conf
│   ├── apache-auth.conf
│   └── ...
├── action.d/          # Acções (iptables, nftables, etc.)
│   ├── iptables-multiport.conf
│   ├── nftables-multiport.conf
│   └── ...
└── paths/
    └── debian.conf    # Caminhos de logs por distro

Configuração base — jail.local

# Criar jail.local
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# OU criar do zero (mais limpo):
sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
# Tempo de banimento (segundos) — 1 hora por defeito
bantime = 3600

# Tempo de busca de falhas (segundos) — 10 minutos
findtime = 600

# Número de falhas antes de banir
maxretry = 3

# Whitelist — IPs que nunca são banidos
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 10.0.0.0/8

# Acção padrão: banir no firewall + email
# Para PME sem email: usar apenas firewall
banaction = nftables-multiport
# banaction = iptables-multiport (alternativa)

# Email (opcional)
# destemail = [email protected]
# sender = [email protected]
# action = %(action_)s

# Backend de logs
backend = systemd

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
maxretry = 3
bantime = 3600
EOF

# Reiniciar
sudo systemctl restart fail2ban
sudo systemctl enable fail2ban

# Verificar
sudo fail2ban-client status
# Status
# |- Number of jail:      1
# `- Jail list:           sshd

4. Jail SSH — Configuração Detalhada

Configuração recomendada para PME

# /etc/fail2ban/jail.local — secção [sshd]

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
backend = systemd

# 3 tentativas falhadas em 10 minutos = ban
maxretry = 3
findtime = 600

# Ban de 1 hora para primeira infração
bantime = 3600

# Ban progressivo para reincidentes
# Usa a jail recidive (ver secção 8)
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 604800  # máximo 1 semana

Verificar a jail SSH

# Status da jail
sudo fail2ban-client status sshd

# Output:
# Status for the jail: sshd
# |- Filter
# |  |- Currently failed: 0
# |  |- Total failed:     47
# |  `- File list:        /var/log/auth.log
# `- Actions
#    |- Currently banned: 2
#    |- Total banned:     15
#    `- Banned IP list:   1.2.3.4 5.6.7.8

# Ver IPs banidos
sudo fail2ban-client status sshd | grep "Banned IP"

# Ver logs do fail2ban
sudo journalctl -u fail2ban -f
# ou
sudo tail -f /var/log/fail2ban.log

Desbanir um IP manualmente

# Desbanir IP específico
sudo fail2ban-client set sshd unbanip 1.2.3.4

# Adicionar IP à whitelist permanentemente
sudo fail2ban-client set sshd addignoreip 192.168.1.50

# Verificar se IP está banido
sudo fail2ban-client status sshd | grep 1.2.3.4

5. Jails para Nginx, Apache e Outros Serviços

Nginx — Proteger wp-login.php e .htaccess

# /etc/fail2ban/jail.local — adicionar:

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

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

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

Apache — Proteger login e directory traversal

# /etc/fail2ban/jail.local — adicionar:

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

[apache-botsearch]
enabled = true
port = http,https
filter = apache-botsearch
logpath = /var/log/apache2/access.log
maxretry = 2
bantime = 7200

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

Postfix / Dovecot — Email

# /etc/fail2ban/jail.local — adicionar:

[postfix]
enabled = true
port = smtp,submissions
filter = postfix
logpath = /var/log/mail.log
maxretry = 3
bantime = 3600

[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps
filter = dovecot
logpath = /var/log/mail.log
maxretry = 3
bantime = 3600

6. Configuração Avançada — Jails Custom e Regex

Criar um filtro customizado

Se tem uma aplicação custom que gera logs de autenticação, pode criar um filtro regex dedicado:

# /etc/fail2ban/filter.d/myapp.conf

[Definition]
# Regex para linhas de log com auth falhada
failregex = ^.*AUTH FAILED.*from <HOST>.*$

# Regex para ignorar (linhas legítimas)
ignoreregex =

# Testar o filtro contra um ficheiro de log
sudo fail2ban-regex /var/log/myapp.log /etc/fail2ban/filter.d/myapp.conf

# Output esperado:
# Lines: 1245 lines, 0 ignored, 47 matched, 1198 missed
# Success - 47 matches

Jail para a aplicação custom

# /etc/fail2ban/jail.local

[myapp]
enabled = true
port = 8080
filter = myapp
logpath = /var/log/myapp.log
maxretry = 5
findtime = 300
bantime = 3600
banaction = nftables-multiport

7. Integração com Nftables e IPTables

Nftables (recomendado em 2026)

Em Ubuntu 24.04, Debian 12, e RHEL 9, o nftables é o backend de firewall padrão, substituindo o iptables. O fail2ban suporta nftables nativamente:

# /etc/fail2ban/jail.local — usar nftables

[DEFAULT]
banaction = nftables-multiport
banaction_allports = nftables-allports

# Verificar que nftables está activo
sudo nft list ruleset | grep fail2ban
# Deve mostrar chains do fail2ban

# As regras criadas pelo fail2ban aparecem como:
# table inet f2b-table {
#     set addr-set-sshd { ... }
#     chain f2b-sshd { ... }
# }

IPTables (legacy)

# /etc/fail2ban/jail.local — usar iptables

[DEFAULT]
banaction = iptables-multiport
banaction_allports = iptables-allports

# Verificar regras criadas
sudo iptables -L -n | grep f2b
# Chain f2b-sshd (1 references)
# DROP     all  --  1.2.3.4    0.0.0.0/0

Não misture iptables e nftables. Se o seu sistema usa nftables por defeito (Ubuntu 24.04+), configure o fail2ban com banaction = nftables-multiport. Usar iptables pode criar conflitos ou regras que não são aplicadas.

8. Whitelist, Persistência e Recidive

Whitelist de IPs

# /etc/fail2ban/jail.local — secção [DEFAULT]

[DEFAULT]
# IPs que nunca são banidos (separados por espaço)
ignoreip = 127.0.0.1/8 ::1
    192.168.1.0/24       # rede local da empresa
    10.0.0.0/8           # VPN interna
    203.0.113.50         # IP fixo do admin remoto
    198.51.100.0/28      # range do escritório remoto

# Adicionar dinamicamente (sem reiniciar)
sudo fail2ban-client set sshd addignoreip 203.0.113.100
# Remover
sudo fail2ban-client set sshd delignoreip 203.0.113.100

Ban progressivo (recidive)

A jail recidive aumenta o tempo de banimento para IPs que são banidos repetidamente — um IP que foi banido 3 vezes recebe um banimento longo (por defeito, 1 semana):

# /etc/fail2ban/jail.local

[recidive]
enabled = true
filter = recidive
logpath = /var/log/fail2ban.log
maxretry = 3
findtime = 86400       # 24 horas
bantime = 604800       # 1 semana

# Ban progressivo na jail sshd
[sshd]
enabled = true
bantime.increment = true
bantime.factor = 2         # dobra a cada infração
bantime.maxtime = 604800   # máximo 1 semana

Persistência de bans

Por defeito, os bans do fail2ban são perdidos quando o serviço reinicia. Para manter bans entre reinícios:

# /etc/fail2ban/fail2ban.local

[Definition]
# Base de dados SQLite para persistir bans
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 604800  # purgar bans antigos após 1 semana

9. Diagnóstico e Troubleshooting

Verificar se o fail2ban está a funcionar

# Estado geral
sudo fail2ban-client status

# Estado de uma jail específica
sudo fail2ban-client status sshd

# Logs em tempo real
sudo journalctl -u fail2ban -f

# Verificar configuração
sudo fail2ban-client -t
# OK: configuration test succeeded

Testar um filtro contra um log real

# Testar filtro sshd contra o auth.log
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

# Output:
# Lines: 5234 lines, 0 ignored, 89 matched, 5145 missed
# Success - 89 matches (1.7% of lines)

# Testar com um log customizado
sudo fail2ban-regex /var/log/myapp.log /etc/fail2ban/filter.d/myapp.conf --print-all-matched

Forçar um ban manualmente (teste)

# Banir IP manualmente
sudo fail2ban-client set sshd banip 1.2.3.4

# Verificar
sudo fail2ban-client status sshd | grep 1.2.3.4

# Desbanir
sudo fail2ban-client set sshd unbanip 1.2.3.4

Problema: fail2ban não detecta falhas

# 1. Verificar que o ficheiro de log está correcto
sudo fail2ban-client get sshd logpath
# /var/log/auth.log

# 2. Verificar que o log tem entradas de falha
sudo grep "Failed password" /var/log/auth.log | tail -5

# 3. Verificar que o filtro faz match
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

# 4. Se backend=systemd, verificar journal
sudo journalctl -u ssh -n 20 | grep "Failed"

# 5. Verificar timezone (logs vs sistema)
date
sudo fail2ban-client get sshd datepattern

10. Alternativas — CrowdSec e SSHGuard

O fail2ban não é a única opção. Duas alternativas modernas:

Ferramenta Vantagens Desvantagens
fail2ban Standard, simples, milhares de filtros prontos, comunidade enorme Sem threat intelligence, bloqueia por IP individual apenas
CrowdSec Threat intelligence partilhada (community blocklist), arquitetura moderna, suporta remediação (captcha, MFA) Mais complexo, requer registo para blocklist, menor comunidade
SSHGuard Extremamente leve, foco em SSH, configuração minimalista Menos filtros prontos, sem interface de gestão, menos flexível

Quando usar cada um:

  • fail2ban — PME com 1-10 servidores, simplicidade, sem dependências externas
  • CrowdSec — PME com múltiplos servidores que beneficia de threat intelligence partilhada ( IPs maliciosos já detectados por outros são bloqueados automaticamente)
  • SSHGuard — Edge/IoT com recursos limitados, apenas SSH para proteger

11. FAQ para Sysadmins

1. Fail2ban protege contra ataques distribuídos (distributed brute force)?

Parcialmente. Se o atacante usa 1000 IPs diferentes com 1 tentativa cada, o maxretry=3 nunca é atingido por IP. Para este cenário, o CrowdSec é mais eficaz (blocklist partilhada detecta IPs maliciosos conhecidos). O fail2ban é optimizado para bots ruidosos de um único IP.

2. Quanto tempo de banimento usar?

1 hora para primeira infração é o padrão. Use ban progressivo (bantime.increment = true) para que reincidentes recebam bans cada vez mais longos. Para bots persistentes, a jail recidive com 1 semana é eficaz.

3. O fail2ban consome muitos recursos?

Não. Tipicamente < 1% CPU e < 50MB RAM. Usa Python + regex sobre logs, não captura tráfego de rede. Em Raspberry Pi com fail2ban a monitorizar SSH, o impacto é negligenciável.

4. Posso ter fail2ban e CrowdSec ao mesmo tempo?

Tecnicamente sim, mas não é recomendado — ambos vão tentar bloquear os mesmos IPs e podem criar conflitos no firewall. Escolha um. Se precisa de ambos, use fail2ban para detecção local e CrowdSec para blocklist global.

5. Como evitar bloquear IPs legítimos?

Whitelist (ignoreip) de todos os IPs fixos da empresa, VPN, e administradores remotos. Se um IP legítimo for bloqueado, desbane com sudo fail2ban-client set sshd unbanip IP e adicione à whitelist.

6. Fail2ban funciona com Docker?

Sim, mas precisa de configuração adicional. Se o SSH corre dentro de um container, o fail2ban no host precisa de aceder aos logs do container. Alternativa: instalar fail2ban dentro do container (não recomendado para containers efémeros) ou usar Docker logging driver para enviar logs para o host.

7. O que é backend = systemd?

Em vez de ler ficheiros de log (/var/log/auth.log), o fail2ban lê directamente do journalctl via systemd. É mais eficiente e funciona em sistemas que não mantêm ficheiros de log persistentes (journald apenas).

8. Como ver estatísticas de longo prazo?

# Total de bans por jail
sudo fail2ban-client status sshd | grep "Total banned"

# Histórico de bans no log
sudo grep "Ban" /var/log/fail2ban.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -20

# Top 20 IPs mais banidos
sudo grep "Ban" /var/log/fail2ban.log | grep -oP '\d+\.\d+\.\d+\.\d+' | sort | uniq -c | sort -rn | head -20

12. Erros Comuns e Resoluções

Sintoma Causa Solução
fail2ban-client: command not found Não instalado sudo apt install fail2ban
Number of jail: 0 Nenhuma jail activada Verificar enabled = true em jail.local
Total failed: 0 Logpath errado ou backend incorrecto Verificar logpath ou usar backend = systemd
Currently banned: 0 mas há falhas no log Filtro regex não faz match fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
Have not found any log file for sshd jail Caminho do log inexistente Verificar se /var/log/auth.log existe ou usar backend = systemd
IP banido mas ainda consegue ligar Firewall backend errado (iptables vs nftables) Verificar banaction corresponde ao firewall activo
Bans desaparecem após reiniciar Sem base de dados de persistência Verificar dbfile em fail2ban.local
Configuration test failed Erro de sintaxe em jail.local sudo fail2ban-client -t mostra o erro
SSHGuard vs Fail2ban conflito Ambos a gerir firewall Usar apenas um de cada vez
ignoreip não funciona Sintaxe incorrecta ou na secção errada Colocar ignoreip em [DEFAULT], separar IPs por espaço

Artigos Relacionados

Voltar ao Índice

Este artigo foi útil?

Duarte Spínola

Deixe um Comentário