Setting up fail2ban

Installing fail2ban, the sshd jail, reading fail2ban-client status, unbanning an address including your own, and what it cannot stop.

Linux5 min read

On this page
  1. What it does, and what it does not
  2. Installing it
  3. Configuring the sshd jail
  4. If your server has no /var/log/auth.log
  5. Check that it is actually running
  6. Reading the status
  7. Unbanning an address
  8. If you banned yourself
  9. Writing a jail for another service
  10. Related

fail2ban watches log files, and when one source produces the same failure too many times in a row it adds a firewall rule that drops that address for a while. It is a good answer to somebody grinding through passwords on your SSH port, and it is not an answer to anything larger than that.

What it does, and what it does not

It reads what a service has already written to a log. Everything follows from that:

  • The attempt reached your service before fail2ban knew about it. Banning is after the fact, by design.
  • It counts per source address. Credential attempts spread across thousands of addresses never reach the threshold from any one of them, and nothing is banned.
  • A packet flood aimed at a game port writes nothing to any log, so fail2ban never sees it. That traffic is handled before it reaches your operating system; see How protection works, and use Blocking and allowing IPs when you want one source gone.
One address guessing SSHpasswordsWhat arrivesFailed sign-ins from198.51.100.23 reach sshdWhat the log getsauth.log gets a failure linefor eachfail2banCounted per address: bannedat the thresholdGuesses spread acrossthousands of addressesWhat arrivesOne or two attempts from eachaddressWhat the log getsauth.log gets a line for eachfail2banNo single address reaches thethreshold: nothing bannedA packet flood at a gameportWhat arrivesPackets aimed at UDP 30120What the log getsNothing is written to any logfail2banfail2ban never sees it. Thattraffic is handled before itreaches your operating systemOne address guessing SSH passwordsWhat arrivesFailed sign-ins from 198.51.100.23 reach sshdWhat the log getsauth.log gets a failure line for eachfail2banCounted per address: banned at thethresholdGuesses spread across thousands ofaddressesWhat arrivesOne or two attempts from each addressWhat the log getsauth.log gets a line for eachfail2banNo single address reaches the threshold:nothing bannedA packet flood at a game portWhat arrivesPackets aimed at UDP 30120What the log getsNothing is written to any logfail2banfail2ban never sees it. That traffic ishandled before it reaches your operatingsystem
fail2ban only reads logs, so it acts only on what a log records against one address. A single guesser is banned; guesses spread thin never add up at any one address; and a flood that writes no log line is never seen at all. Illustration.

If your concern is specifically people guessing SSH passwords, turning password sign-in off removes the problem outright rather than rate-limiting it. See Hardening SSH on Linux. fail2ban is still worth running after that, mostly for services that must accept a password and for keeping the logs readable.

Installing it

On Debian and Ubuntu:

Shell
apt update
apt install -y fail2ban

On RHEL, Alma, and Rocky, fail2ban comes from EPEL:

Shell
dnf install -y epel-release
dnf install -y fail2ban

Then, on either family:

Shell
systemctl enable --now fail2ban

Configuring the sshd jail

Never edit /etc/fail2ban/jail.conf. It is replaced when the package updates and your changes go with it. Put your settings in /etc/fail2ban/jail.local, which overrides it and is left alone:

Config
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 203.0.113.10
 
[sshd]
enabled = true
port    = 22

What those four values mean together: an address that fails maxretry times within findtime is banned for bantime.

maxretry = 5, findtime = 10m, bantime = 1ha failed sign-in in the log198.51.100.23Banned for 1h5 in 10m198.51.100.77Never 5 in 10m: not banned010m20m30m40m50m60m70m80mMinutesmaxretry = 5, findtime = 10m, bantime = 1ha failed sign-in in the log198.51.100.23Banned for 1h5 in 10m198.51.100.77Never 5 in 10m: not banned020m40m60m80mMinutes
The jail.local above, on a timeline. Five failures inside ten minutes from one address earn it a one-hour ban; another address failing just as often, but spread out, never has five inside any ten minutes. Derived from the jail.local example on this page; the failure times are an example.

Sizing them is a judgement about your own users, not a fixed answer. A few notes on the edges:

  • bantime = -1 bans permanently. The list only grows, so prefer a finite ban unless you are curating the list by hand.
  • bantime.increment = true in [DEFAULT] makes each repeat ban for the same address longer than the last, which is usually better than one long ban.
  • ignoreip is never banned. Put your own fixed address there, space separated. It is worth doing before you have to, not after.
  • port accepts a number or a name from /etc/services. If you moved SSH, set the number here as well, or the jail keeps watching port 22.

Apply the file:

Shell
fail2ban-client reload

If your server has no /var/log/auth.log

Some images log only to the journal, and the jail then has nothing to read. Check:

Shell
ls -l /var/log/auth.log

If the file is not there, tell the jail to read the journal instead by adding one line to the [sshd] section of jail.local:

Config
[sshd]
enabled = true
backend = systemd

Then reload.

Check that it is actually running

A jail that cannot find its log takes the whole service down at startup, and the message that explains why is only in the service status:

Shell
systemctl status fail2ban

A failure reads like "Failed during configuration: Have not found any log file for sshd jail". The full history is in /var/log/fail2ban.log, or:

Shell
journalctl -u fail2ban -n 50 --no-pager

Reading the status

List the jails that are up:

Shell
fail2ban-client status

Then look at one:

Shell
fail2ban-client status sshd

That prints two blocks. The filter block gives currently failed and total failed attempts, and names the log file it is reading, which is the quickest way to confirm the jail found its source. The actions block gives currently banned, total banned, and the list of banned addresses. A jail that is up but shows zero of everything after a day on a public address is usually reading the wrong log.

Example output. The filter block ends with the file the jail reads, and the actions block with the addresses banned now. Example output.

Unbanning an address

Remove one address from one jail:

Shell
fail2ban-client set sshd unbanip 203.0.113.10

Remove it from every jail, or clear the lot:

Shell
fail2ban-client unban 203.0.113.10
fail2ban-client unban --all

You can also ban by hand, which is useful for testing the mechanism:

Shell
fail2ban-client set sshd banip 203.0.113.10

If you banned yourself

You will not be able to SSH in to fix it, because the ban is what is refusing you. Open Console access in the panel, which reaches the machine without going through the ban, then:

Shell
fail2ban-client set sshd unbanip 203.0.113.10

Add the address to ignoreip in jail.local and reload, so the same mistake cannot repeat. If you connect from an address that changes, ignore a fixed one you control instead, and rely on the console for the rest.

Writing a jail for another service

A jail needs a filter, which is a pattern that matches one failure line, and a few settings that say where to read and when to act. fail2ban ships filters for common software in /etc/fail2ban/filter.d/, so check there before writing one. Enabling a shipped filter takes only the jail:

Config
[nginx-http-auth]
enabled = true

For your own application, write the filter first. Create /etc/fail2ban/filter.d/myapp.conf:

Config
[Definition]
failregex = ^.*authentication failed for user .* from <HOST>.*$
ignoreregex =

<HOST> is the placeholder for the address to ban, and every failregex needs exactly one. Test the pattern against real log lines before enabling anything:

Shell
fail2ban-regex /var/log/myapp/app.log /etc/fail2ban/filter.d/myapp.conf

It reports how many lines matched. Zero matches means the jail would do nothing, which is the failure mode you want to find now rather than during an attack. For a service that logs to the journal, point the same command at it:

Shell
fail2ban-regex systemd-journal /etc/fail2ban/filter.d/myapp.conf

Then add the jail to jail.local:

Config
[myapp]
enabled  = true
port     = 8080
filter   = myapp
logpath  = /var/log/myapp/app.log
maxretry = 5
bantime  = 1h

Reload, confirm the service is still up, and check the new jail appears:

Shell
fail2ban-client reload
systemctl status fail2ban
fail2ban-client status myapp

Need a server, or a hand with one?

Pick a plan to deploy a server, or open a ticket in the panel. Support runs 24/7/365.