fail2ban: how to block brute force attacks on Linux step by step

fail2ban: how to block brute force attacks on Linux step by step

Open /var/log/auth.log on any SSH server exposed to the internet and you'll see hundreds — sometimes thousands — of login attempts a day. Bots scan the entire IPv4 space within hours, testing username and password combinations against every port 22 they find. fail2ban is the standard answer to this: it watches the logs in real time and automatically bans any IP that exceeds the attempt threshold you set. Simple, lightweight and effective.

What fail2ban does (and doesn't do)

fail2ban works in three stages: it reads a service's logs, applies a filter (a regular expression) to detect failed authentication attempts, and, once an IP crosses maxretry within the findtime window, triggers an action — by default, a block rule via iptables or nftables with a configurable duration.

What it doesn't do: it doesn't replace proper SSH hardening (disabling root login, using keys instead of passwords). The two work in complementary layers — fail2ban cuts down brute-force noise, but a server that still accepts weak passwords stays vulnerable if the attack comes from many different IPs (distributed, harder to ban).

Installing on Ubuntu and Debian

sudo apt update && sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

On Ubuntu 22.04+ and Debian 12, the package already ships with nftables/iptables support and the service starts automatically after installation.

Creating jail.local (never edit jail.conf)

/etc/fail2ban/jail.conf is the reference file — it gets overwritten on package updates. All custom configuration goes in jail.local, which takes precedence:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local and adjust the [DEFAULT] section:

[DEFAULT]
bantime  = 1h
findtime  = 10m
maxretry = 5
backend = systemd

What each parameter means:

  • bantime: how long the IP stays banned. Accepts suffixes: 10m, 1h, 1d, 1w.
  • findtime: the observation window. If the IP fails maxretry times within this period, it gets banned.
  • maxretry: number of failures tolerated before the ban.
  • backend: how fail2ban reads the logs. systemd uses journald (default on modern distros); use auto if you need to cover both.

Enabling the SSH jail

Still in jail.local, find the [sshd] section and configure:

[sshd]
enabled  = true
port     = ssh
maxretry = 3
bantime  = 2h
findtime = 5m

With these values, three wrong attempts within five minutes result in a two-hour ban. For a production server, more aggressive values make sense: maxretry = 2 and bantime = 24h.

After editing, reload:

sudo systemctl reload fail2ban

Essential management commands

How do I check whether fail2ban is working?

Run sudo fail2ban-client status — it lists the active jails. If sshd shows up, it's monitoring. To see how many IPs have been banned:

sudo fail2ban-client status sshd

Typical output:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 2
|  |- Total failed:     317
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 4
   |- Total banned:     51
   `- Banned IP list:   203.0.113.42 198.51.100.7 ...

To watch the logs in real time:

sudo journalctl -u fail2ban -f

To unban an IP manually (useful when you lock yourself out by mistyping your own password):

sudo fail2ban-client set sshd unbanip 203.0.113.42

Escalating bans for repeat offenders

By default, an IP is banned for bantime and, once it expires, starts back at zero. Enabling incremental bans makes the duration double with every repeat offense — persistent IPs end up banned for weeks:

[DEFAULT]
bantime.increment = true
bantime.multiplier = 2
bantime.maxtime = 1w

With this, the first ban lasts the configured bantime; the second, double that; the third, four times — up to a one-week cap. This feature is available from fail2ban 0.11 onward, which is the default on Ubuntu 22.04+ and Debian 12.

Protecting other services

fail2ban ships with ready-made jails for Nginx, Apache, Postfix, Dovecot and others. Just enable the matching section in jail.local. Example for Nginx (basic auth attempts):

[nginx-http-auth]
enabled = true
port    = http,https
maxretry = 4
bantime  = 1h

The same logic behind brute-force protection for RDP on Windows applies here: limiting attempts with automatic blocking is the first layer of defense for any service exposed to the network.

Allowlist — never banned

Add your IP (or range) to the allowlist so fail2ban never blocks it, even if you fumble the password several times:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 YOUR.IP.HERE

For multiple IPs, separate them with a space. If you always access the server from the same static IP, this line saves you the headache of locking yourself out.

Conclusion

Installing fail2ban takes less than two minutes and covers the most basic brute-force layer automatically. Combined with SSH key authentication and disabling root login, you close most of the attack vectors bots exploit against exposed servers. For anyone managing VPSs or home servers, that pair of measures is the equivalent of locking the door and changing the lock — not invulnerability, but it eliminates opportunistic attacks. If you want to take infrastructure hardening further, the guide on OPSEC and digital privacy covers the behavior and information-exposure layer, which is where many real attacks begin.

Comments