Skip to content

Cookie settings

Choose which kinds of cookies you allow. You can change your choice at any time via Cookie settings at the bottom of every page. Privacy statement

Necessary

Always on

Needed to log in, keep your cart and remember your choices. These are always on.

Which cookies?
  • weboke_sessie, weboke_klant, weboke_remember: your session and staying logged in
  • weboke_basket: your cart
  • wkBtwIncl: showing prices with or without VAT
  • wok_cookies: your cookie choice, 12 months

Measures which ad leads to a visit or order, so we do not pay for ads that do nothing.

Which cookies?
  • Google Ads via Google Tag Manager (_gcl_au, _gcl_aw), up to 90 days
  • OpenAI Ads, ads in ChatGPT
  • wok_aff: which partner referred you to us (affiliate programme), 60 days

4.6

Country and language

Each country has its own site, with prices and VAT for that country.

View

Module 5 · 5.3

fail2ban against guessing attempts

Module 5: Shielding the network · Reading time: 10 minutes

Learning objective: After this lesson you can set up fail2ban for SSH, read its status and log, unban an address, and explain what fail2ban does and does not protect.

Key concepts: brute force, jail, filter, action (banaction), maxretry, findtime, bantime, bantime.increment, ignoreip, backend systemd, jail.conf, jail.local and jail.d, fail2ban-client, recidive

The door-handle rattlers of the internet

Look at the SSH log one day after installation and you will see it: hundreds of login attempts, with names such as root, admin, test, ubuntu and oracle, from addresses all over the world.

anna@web1:~$ sudo journalctl -u ssh --since today | grep -c "Invalid user"
412
anna@web1:~$ sudo journalctl -u ssh --since today | grep "Invalid user" | tail -n 3
nov 09 14:02:11 web1 sshd-session[20311]: Invalid user admin from 203.0.113.77 port 51522
nov 09 14:02:13 web1 sshd-session[20313]: Invalid user oracle from 203.0.113.77 port 51544
nov 09 14:02:16 web1 sshd-session[20315]: Invalid user test from 203.0.113.77 port 51560

(On Ubuntu the service is called ssh, on AlmaLinux and Rocky sshd. The process name can be sshd or sshd-session, depending on the OpenSSH version.) This is a brute-force attack: bots simply trying every common name and password. If you log in with keys and have password login switched off (lesson 2.2), they will never get in, however often they try. But they fill up your log, cost some computing power, and on services that do require a password (webmail, an admin panel) they can be genuinely dangerous.

fail2ban reads logs, recognises failed attempts, and locks an address that fails too often out of the firewall for a while.

How fail2ban thinks: jails

fail2ban works with jails. A jail combines three things:

  • a filter: regular expressions that recognise failed attempts in a log (for example the line "Invalid user ... from <adres>");
  • an action: what happens after too many attempts, usually a firewall rule that blocks the address (banaction, on Debian 13 via nftables by default);
  • thresholds: how many failed attempts (maxretry) within what time (findtime) lead to a ban of how long (bantime).

The default values from jail.conf are: maxretry = 5, findtime = 10m, bantime = 10m. So: five failed attempts within ten minutes results in a ten-minute ban.

Installing, and the Debian 12 pitfall

anna@web1:~$ sudo apt install fail2ban
anna@web1:~$ sudo systemctl status fail2ban

On Debian and Ubuntu the jail for SSH ([sshd]) is already switched on after installation, through the file /etc/fail2ban/jail.d/defaults-debian.conf.

There is one well-known pitfall. fail2ban needs to know where the log is. That used to be the text file /var/log/auth.log. Since Debian 12, rsyslog is no longer installed by default and the messages are only in the systemd journal (lesson 6.2). The Debian 12 fail2ban package still looked for /var/log/auth.log, could not find it and did not start. Debian 13 fixed this by setting backend = systemd for SSH in defaults-debian.conf. On Debian 12 you fix it yourself (see below) by doing the same. Ubuntu still has rsyslog by default, but backend = systemd works there too.

Your own settings: jail.local

Never edit jail.conf: that file is replaced during an update. Put your own settings in /etc/fail2ban/jail.local or in a file in /etc/fail2ban/jail.d/ with the extension .local:

# /etc/fail2ban/jail.d/eigen.local
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
bantime.increment = true
ignoreip = 127.0.0.1/8 ::1 198.51.100.20

[sshd]
enabled = true
backend = systemd

What the lines do:

  • bantime = 1h: a one-hour ban instead of ten minutes.
  • bantime.increment = true: anyone who comes back after a ban gets a longer ban each time.
  • ignoreip: addresses that are never banned. Put your office's fixed IP address here, if you have one. Do not add large ranges here "just to be safe".
  • backend = systemd: read the journal instead of a text file (required on Debian 12, recommended everywhere). This needs the package python3-systemd to be installed; it usually already is, as a dependency.

Reload and check:

anna@web1:~$ sudo fail2ban-client -t            # test the config
OK: configuration test is successful
anna@web1:~$ sudo systemctl reload fail2ban
anna@web1:~$ sudo fail2ban-client status
Status
|- Number of jail:      1
`- Jail list:   sshd

Reading the status, banning and unbanning

anna@web1:~$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 3
|  |- Total failed:     1288
|  `- Journal matches:  _SYSTEMD_UNIT=ssh.service + _COMM=sshd
`- Actions
   |- Currently banned: 2
   |- Total banned:     117
   `- Banned IP list:   203.0.113.77 198.51.100.140

"Currently failed" are addresses that have made attempts but are not yet over the threshold. "Currently banned" are the addresses that are blocked right now.

Have you locked yourself out (for example by trying a few times with the wrong key or username)? Then log in from another network or via the console and unban your address:

anna@web1:~$ sudo fail2ban-client set sshd unbanip 198.51.100.20

fail2ban's own log is in /var/log/fail2ban.log and shows when each address was banned and unbanned:

2026-11-09 14:02:16,903 fail2ban.actions [811]: NOTICE  [sshd] Ban 203.0.113.77
2026-11-09 15:02:17,120 fail2ban.actions [811]: NOTICE  [sshd] Unban 203.0.113.77

Repeat offenders: recidive

Some bots simply come back after every ban. There are two tools for this. bantime.increment (above) makes the ban longer each time. On top of that there is the recidive jail: it reads fail2ban's own log and bans an address that has been banned often within a day for a week, on all ports. Switch it on in your .local file:

[recidive]
enabled = true

What fail2ban does and does not do

fail2ban is useful, but it is not the lock on the door. The lock is logging in with a key, with passwords switched off (lesson 2.2). So also know what fail2ban cannot do:

  • A distributed attack from thousands of addresses, each making one or two attempts, never goes over maxretry.
  • A weak password stays weak. fail2ban slows down the guessing, nothing more.
  • Only what is in a log: a service that does not log failed attempts, or a log that fail2ban does not read, goes unseen.
  • Docker: bans in the normal firewall chain do not apply to published container ports (lesson 9.3). Services in containers need a separate setting (an action on the DOCKER-USER chain).

What fail2ban does do well: keep your log quiet, reduce the load from bots, and protect services where you (still) use passwords, such as webmail or an admin panel. For services like these there are filters for nginx, Postfix, Dovecot and many others.

On AlmaLinux and Rocky Linux

There, fail2ban comes from EPEL: dnf install epel-release and then dnf install fail2ban. The package fail2ban also installs the link with firewalld. The SSH service is called sshd. Switch the jail on yourself in a .local file; unlike on Debian, it is not switched on automatically.

Summary

  • Bots try to log in all day long; with keys and password login switched off they cannot get in, and fail2ban also keeps them at a distance.
  • A jail is filter + action + thresholds (maxretry, findtime, bantime); by default 5 attempts in 10 minutes, a 10-minute ban.
  • Your own settings go in jail.local or jail.d/*.local, never in jail.conf; use backend = systemd (required on Debian 12).
  • fail2ban-client status sshd shows banned addresses, set sshd unbanip unbans; recidive and bantime.increment deal with repeat offenders.
  • fail2ban does not stop distributed attacks or weak passwords, and does not see Docker ports by itself.

Quiz

1. You have installed fail2ban on Debian 12 and it will not start, with a message that there is no log file for the sshd jail. What is the cause?



2. Why do you put your settings in jail.local and not in jail.conf?



3. The default settings are maxretry = 5, findtime = 10m, bantime = 10m. An address makes a failed attempt at 14:00, 14:03, 14:05, 14:08 and 14:09. What happens?



4. Your server gets thousands of login attempts every day, spread over tens of thousands of addresses, one attempt each. What does fail2ban do about it?



5. You have locked yourself out by typing the wrong username a few times. What is the proper fix?



Exercise

Exercise Z5.3: Reading an intrusion log. The sample file ssh-journal-1.txt contains a piece of the journal of an SSH server running fail2ban, plus the output of fail2ban-client status sshd. Answer: which address tried the most usernames, which address managed to log in with a key and as which user, which address was banned and at what time, and would that address also have been banned with the default settings (maxretry = 5, findtime = 10m)? The program compares your answers with the fixed answer key. If you have your own server with fail2ban, look at fail2ban-client status sshd there as well.

Exercise Z5.3: Reading an intrusion log

File for this exercise: ssh-journal-1.txt








What you enter and what we find isn't stored. Your browser remembers your answers and whether you've passed the exercise. If you're signed in, we only save that you got the exercise (almost) right in My courses; you can delete that there too.

Further reading

One moment…

Set up your domains

For each domain, arrange the nameservers, the link to your hosting and who becomes the owner.

Loading your domains…