Fail2ban for SSH
Every public server gets a steady stream of SSH password guesses. Fail2ban watches for them and blocks the addresses that keep failing. It's a useful filter, but it isn't a lock: key-only login is what actually keeps people out.
Published . Written for the fail2ban package on current Debian and Ubuntu releases (fail2ban 1.x) with systemd.
- What fail2ban does
- Install it
- Use jail.local, not jail.conf
- The settings that matter
- Read the journal, not auth.log
- Check bans and unban someone
- What it doesn't protect against
What fail2ban does
Fail2ban reads log messages, counts failures per IP address, and when one address fails too often in a short window, it adds a firewall rule that drops that address for a while. For SSH, that means bots guessing passwords get blocked after a few tries instead of being able to try thousands of times.
That's all it does. It doesn't make a weak password safe, and it can't see attackers who spread their guesses across many addresses. Treat it as noise reduction on top of key-only auth, not instead of it.
Install it
sudo apt update
sudo apt install fail2ban
sudo systemctl enable --now fail2ban
On Debian and Ubuntu the package already turns on the sshd jail in /etc/fail2ban/jail.d/defaults-debian.conf. Read that file before you add your own settings, so you know what's already set:
cat /etc/fail2ban/jail.d/defaults-debian.conf
On Debian 13 it enables the sshd jail, sets backend = systemd for it, and uses nftables to ban. Older or Ubuntu packages may differ, which is why you look first.
Use jail.local, not jail.conf
/etc/fail2ban/jail.conf holds the defaults and says at the top that you should not modify it. Package upgrades can replace it. Put your changes in /etc/fail2ban/jail.local (or a .local file in jail.d/) instead. Fail2ban reads the .conf files first and lets the .local files override them, so you only write the settings you change.
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
# never ban these: loopback and your own fixed address
ignoreip = 127.0.0.1/8 ::1 203.0.113.7
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
backend = systemd
# port = 2222 # only if sshd listens on a non-standard port
The settings that matter
maxretry: how many failures an address may have before it's banned. The default is 5.findtime: the window those failures have to fall in. The default is10m. Five failures spread over a day don't trigger a ban.bantime: how long the ban lasts. The default is10m, which bots simply wait out. An hour or a day is a common choice.ignoreip: addresses that are never banned. It takes single addresses or CIDR ranges, separated by spaces. Add the fixed address you connect from, if you have one, so a few typos don't lock you out. Fail2ban also ignores the server's own addresses by default (ignoreself).port: the jail bans the port named here. It defaults tossh(22). If sshd listens elsewhere, set it, or the ban blocks a port nobody is using.
Read the journal, not auth.log
Fail2ban needs to find sshd's log messages. Older setups wrote them to /var/log/auth.log through rsyslog. Since Debian 12, rsyslog isn't installed by default, so that file may not exist and a jail pointed at it fails to start. sshd's messages are always in the systemd journal.
Setting backend = systemd in the [sshd] section tells fail2ban to read the journal directly. On Debian 13 the package does this already. If your system still has rsyslog and auth.log, either backend works; pick one and stay consistent.
Check the configuration before you reload, then reload:
sudo fail2ban-client -t
sudo fail2ban-client reload
Add your own address to ignoreip before testing. If you test bans by failing logins from your own machine, you will be banned too. Keep a second SSH session open while you change anything, and know how to reach your provider's web console.
Check bans and unban someone
# which jails are running
sudo fail2ban-client status
# failures and banned addresses for the SSH jail
sudo fail2ban-client status sshd
# unban one address from the sshd jail
sudo fail2ban-client set sshd unbanip 198.51.100.23
# or unban it from every jail
sudo fail2ban-client unban 198.51.100.23
status sshd shows "Currently failed", "Total failed", "Currently banned" and the banned IP list. If "Total failed" stays at zero while the journal shows failed logins, fail2ban isn't reading the right log. That usually means the backend setting is wrong.
What it doesn't protect against
- Weak or leaked passwords. If password login is on, an attacker gets five tries per address per window, and botnets have plenty of addresses. Turn password auth off: see disable SSH password authentication.
- Stolen keys. A login with a valid key isn't a failure, so fail2ban never sees it.
- Holes in the software itself. A vulnerability in sshd is exploited without failed logins. Keeping packages updated is what covers that.
- Other services. The
sshdjail only watches SSH. A database open to the internet is a separate problem.
With key-only auth in place, fail2ban mostly keeps your logs quieter. That's still worth having. To see what the bots are actually trying, read who tried to log in over SSH.
Stenvakt starts with a read-only Review. It runs a security and health check of every server, and every fix it suggests is shown in a Preview before it runs. Changes to SSH run in safe mode, with a snapshot, a rollback timer and a test over a fresh connection. It's a Mac app and isn't out yet: get early access.
Sources
- fail2ban on GitHub: config/jail.conf (defaults and option comments)
- Debian fail2ban package: jail.d/defaults-debian.conf
- Debian manpages: fail2ban-client(1)
- Debian 12 release notes: rsyslog is no longer installed by default