Fail2ban: Protecting SSH from Password Brute-Force Attacks
Fail2ban reads service logs, spots repeated failed login attempts and temporarily blocks the attacker's IP address through the firewall. To protect SSH, it is enough to install the package, create /etc/fail2ban/jail.local with an [sshd] section (enabled = true, maxretry, findtime, bantime) and restart the service; the state is checked with fail2ban-client status sshd.
Fail2ban is a daemon that watches service logs, notices repeated failed login attempts and blocks the IP address they come from for a period of time. For SSH it is the simplest protection against password guessing: bots stop hammering the server after a few failures. Below is fail2ban setup from installation to checking bans and common mistakes.
Why you need fail2ban if you have SSH keys
Any server with port 22 open starts receiving login attempts from bots within minutes of launch. See for yourself:
$ sudo journalctl -u ssh --since "1 hour ago" | grep -c "Failed password"
412
If password login is disabled and only keys are used, guessing your way in is practically impossible. But fail2ban is still useful: it removes noise from the logs, reduces the load from thousands of connections and protects you from configuration mistakes, such as password login accidentally left enabled. It is a second layer of defence, not a replacement for keys.
How fail2ban works
The logic consists of three parts:
- Filter — a set of regular expressions that find failed-attempt lines in the log and extract the IP from them. Filters live in
/etc/fail2ban/filter.d/. - Action — what to do with the offender: add a rule to iptables, nftables, ufw or firewalld. Actions live in
/etc/fail2ban/action.d/. - Jail — the combination of a filter, an action, a log source and thresholds. For example, the
sshdjail watches the SSH log.
If maxretry matches are collected from one IP within the findtime window, the address is blocked for bantime. When the period expires, the ban is lifted automatically.
Installing fail2ban
On Debian and Ubuntu:
sudo apt update
sudo apt install fail2ban
On RHEL, Rocky and AlmaLinux the package is in the EPEL repository:
sudo dnf install epel-release
sudo dnf install fail2ban
Enable autostart and check the service status:
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban
On Debian and Ubuntu the SSH jail is already enabled by default through /etc/fail2ban/jail.d/defaults-debian.conf. In the RHEL family nothing is enabled after installation — jails must be activated manually.
Where settings live: jail.conf and jail.local
The main file is /etc/fail2ban/jail.conf, but you should not edit it: it will be overwritten when the package is updated. Your own settings go into /etc/fail2ban/jail.local or into separate files /etc/fail2ban/jail.d/*.local. Fail2ban reads the files in this order, and each subsequent one overrides the previous:
jail.confjail.d/*.confjail.localjail.d/*.local
Do not copy the whole jail.conf into jail.local — that freezes the old default values. Put only the settings you change into jail.local.
Fail2ban setup for SSH: a minimal jail.local
Create the file:
sudo nano /etc/fail2ban/jail.local
And add the following to it:
[DEFAULT]
# Your own addresses that must never be banned
ignoreip = 127.0.0.1/8 ::1 203.0.113.10
bantime = 1h
findtime = 10m
maxretry = 5
# Where to read logs from: the systemd journal or files
backend = systemd
[sshd]
enabled = true
port = ssh
maxretry = 3
Restart the service and make sure the jail has started:
$ sudo systemctl restart fail2ban
$ sudo fail2ban-client status
Status
|- Number of jail: 1
`- Jail list: sshd
What the parameters mean
| Parameter | Meaning | Example |
|---|---|---|
enabled | whether the jail is active | true |
port | port(s) to block for the offender | ssh, 2222, ssh,2222 |
maxretry | how many errors are allowed before a ban | 3 |
findtime | the window in which errors are counted | 10m |
bantime | length of the ban; -1 means forever | 1h, 1d |
ignoreip | addresses and subnets that are never banned | 10.0.0.0/8 |
backend | log source | systemd, auto |
logpath | path to the log file for the file backend | /var/log/auth.log |
banaction |
FAQ
Do I need fail2ban if password login is disabled?
It is not mandatory, but it is useful: it removes thousands of junk attempts from the logs, reduces the load, and acts as a safety net if password login is accidentally turned back on.
How do I see all blocked IPs across all jails?
Run sudo fail2ban-client banned — the command prints the lists of blocked addresses for each jail. For a single jail use fail2ban-client status sshd.
Do bans survive a server reboot?
Yes, fail2ban stores bans in its SQLite database (/var/lib/fail2ban/fail2ban.sqlite3) and restores active blocks on startup, unless the dbpurgeage parameter has already removed them.
Can fail2ban protect services other than SSH?
Yes, it ships with filters for nginx, Apache, Postfix, Dovecot and other services. Just enable the needed jail in jail.local, for example [nginx-http-auth] with enabled = true.
How is fail2ban different from a limit in the firewall?
A firewall statically allows or denies addresses, while fail2ban dynamically bans those who misbehave in the logs. They are often combined: a whitelist in the firewall and fail2ban for everything else.
This topic is covered in depth with practice in the course “Linux: с нуля до уверенного администрирования”.
Go to course