Fail2ban basics: automatic brute-force blocking
Install, configure and audit fail2ban, and avoid the classic self-ban trap.
fail2ban watches your logs for signs of an attack and, when it sees enough failures from one address, adds a temporary firewall block.
Install
apt install -y fail2ban # Debian/Ubuntu
# dnf install -y fail2ban # RHEL-family
systemctl enable --now fail2ban
systemctl status fail2ban --no-pagerHow it works
Each jail binds a log source to a filter and a set of thresholds:
filter — the pattern that counts as a failure.
maxretry — failures allowed before a ban.
findtime — the window in which those failures must occur.
bantime — how long the ban lasts.
A block is a firewall rule (an iptables/nftables entry) that drops the offending address.
A sane SSH jail
Create /etc/fail2ban/jail.d/sshd.local:
[sshd]
enabled = true
backend = systemd
maxretry = 3
findtime = 10m
bantime = 1hDebian 12 and later: there is no /var/log/auth.log — journald owns the logs. Set backend = systemd or the jail will fail to start with "have not found any log file for sshd jail". This one line has cost more people more confusion than any other fail2ban setting.
Reload after editing: systemctl reload fail2ban.
Always whitelist your own infrastructure
ignoreip keeps trusted sources from ever being banned. If you run monitoring, a control host, or backups from known addresses, list them:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 10.75.0.0/16 203.0.113.0/24Add it under [DEFAULT] so every jail inherits it. Forgetting this is how a fleet of servers ends up banning its own monitoring host: a single failed connection plus a long bantime equals a host that "is down" while actually being perfectly healthy and simply refusing your SSH. If you ever see instant connection refused on port 22 from one host while others reach it fine, check fail2ban before anything else.
Check and manage bans
fail2ban-client status # list jails
fail2ban-client status sshd # banned IPs and counters for a jail
fail2ban-client set sshd unbanip 198.51.100.7
fail2ban-client set sshd banip 198.51.100.7Audit every jail at once:
for j in $(fail2ban-client status | grep -oP 'Jail list:\s*\K.*' | tr ',' ' '); do
echo "== $j =="; fail2ban-client status "$j" | grep -iE 'banned|total'
doneIf you spot one of your own addresses, fix ignoreip first — unbanning alone does not stick, because the jail re-bans on its next tick.
Beyond SSH
The same machinery protects web apps, mail and more. Common jails: nginx-http-auth, nginx-botsearch, postfix, dovecot, and recidive (a long ban for repeat offenders across all jails).
For crowdsourced blocking at a higher level, CrowdSec is worth a look.
Related reading