Skip to main content

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

bash
apt install -y fail2ban          # Debian/Ubuntu
# dnf install -y fail2ban        # RHEL-family
systemctl enable --now fail2ban
systemctl status fail2ban --no-pager

How 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:

ini
[sshd]
enabled  = true
backend  = systemd
maxretry = 3
findtime = 10m
bantime  = 1h

Debian 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:

ini
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 10.75.0.0/16 203.0.113.0/24

Add 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

bash
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.7

Audit every jail at once:

bash
for j in $(fail2ban-client status | grep -oP 'Jail list:\s*\K.*' | tr ',' ' '); do
  echo "== $j =="; fail2ban-client status "$j" | grep -iE 'banned|total'
done

If 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