Fail2ban Setup: Protecting SSH and Web Services from Brute Force

Fail2ban is one of those tools I install on every server within the first 30 minutes. It’s not a silver bullet — a determined attacker with a botnet of millions of IPs won’t be slowed down much — but it handles opportunistic attacks effectively and keeps your auth logs from filling up with garbage.

The basic idea: Fail2ban reads log files, looks for patterns that indicate failed authentication attempts, and uses iptables (or nftables, or firewalld) to temporarily ban the offending IP.

Installation

# Debian/Ubuntu
apt install fail2ban

# CentOS/RHEL
dnf install fail2ban

The default installation includes a set of “jails” — configurations for various services. SSH is included and often enabled by default.

Configuration Approach

Never edit /etc/fail2ban/jail.conf directly — it gets overwritten on upgrades. Instead, create /etc/fail2ban/jail.local which overrides the defaults:

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

Or create targeted override files in /etc/fail2ban/jail.d/:

touch /etc/fail2ban/jail.d/custom.conf

I prefer the jail.d approach — cleaner, easier to manage.

SSH Protection

Create /etc/fail2ban/jail.d/sshd.conf:

[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
findtime = 600
bantime  = 3600

maxretry = 3 — ban after 3 failed attempts.
findtime = 600 — count failures within a 10-minute window.
bantime = 3600 — ban for 1 hour.

If you’re using a non-default SSH port:

port = 2222

Start and enable:

systemctl enable fail2ban
systemctl start fail2ban

Check SSH jail status:

fail2ban-client status sshd

Output shows: currently failed IPs, total failures, currently banned IPs, total bans.

Nginx Protection

Fail2ban can watch Nginx access logs too. For HTTP auth failures:

[nginx-http-auth]
enabled  = true
filter   = nginx-http-auth
port     = http,https
logpath  = /var/log/nginx/error.log
maxretry = 5
findtime = 300
bantime  = 600

For blocking rapid 404 scanning (common for WordPress installations):

[nginx-404]
enabled  = true
filter   = nginx-404
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 20
findtime = 60
bantime  = 3600

You need a filter for nginx-404. Create /etc/fail2ban/filter.d/nginx-404.conf:

[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD) .* HTTP/.*" 404
ignoreregex =

Test your filter before relying on it:

fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-404.conf

This shows how many lines match the filter pattern. If it’s 0, something’s wrong with the regex or the log format.

Nginx Bad Bot Protection

I also block common bad bot User-Agents:

[nginx-badbots]
enabled  = true
filter   = nginx-badbots
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 1
bantime  = 86400

The nginx-badbots filter usually comes with Fail2ban. If not, create /etc/fail2ban/filter.d/nginx-badbots.conf with patterns matching known malicious bots.

Persistent Bans

By default, bans are in-memory and lost on Fail2ban restart. Enable persistent banning with a database:

In /etc/fail2ban/jail.d/custom.conf:

[DEFAULT]
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 86400  # Keep ban history for 24 hours

Or set bantime = -1 to make bans permanent until manually removed.

For repeat offenders, use bantime.increment:

[DEFAULT]
bantime.increment = true
bantime.factor = 1
bantime.formula = ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor
bantime.overalljails = false

This doubles the ban time for each subsequent offense. First ban: 1 hour, second: 2 hours, third: 4 hours, and so on.

Whitelisting IPs

Some IPs should never be banned — your office IP, monitoring servers, load balancers:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 192.168.0.0/16 203.0.113.42

This is global. You can also set ignoreip per jail.

Be careful: if your deployment server has the same IP as where you administer from, whitelist it. Nothing like locking yourself out of a server because your CI/CD pipeline triggered too many requests.

Manual Ban and Unban

Sometimes you want to manually ban an IP:

fail2ban-client set sshd banip 1.2.3.4

Unban:

fail2ban-client set sshd unbanip 1.2.3.4

Check if a specific IP is currently banned:

fail2ban-client get sshd banlist | grep 1.2.3.4

Viewing Banned IPs

See all currently banned IPs across all jails:

fail2ban-client status
fail2ban-client status sshd
fail2ban-client status nginx-http-auth

Or check iptables directly to see the actual firewall rules Fail2ban created:

iptables -n -L f2b-sshd

Actions: What Happens When an IP Gets Banned

By default, Fail2ban adds iptables rules to drop traffic from banned IPs. The default action is iptables-multiport — it adds the IP to a chain and drops matching traffic.

Other actions are available:

  • iptables-allports — ban on all ports
  • mail — send email notification
  • mail-whois — send email with whois info about the banned IP

Configure in jail:

[sshd]
enabled = true
action = iptables-multiport[name=sshd, port="ssh", protocol=tcp]
         mail[name=sshd, dest=you@example.com, sender=fail2ban@server.com]

I send email notifications for SSH bans on important servers — a wave of SSH attempts can be an early indicator of a targeted attack.

Fail2ban with firewalld or nftables

On systems using firewalld (CentOS/RHEL default), configure accordingly:

[DEFAULT]
banaction = firewallcmd-ipset
banaction_allports = firewallcmd-allports

For nftables:

[DEFAULT]
banaction = nftables-multiport

Log Files to Watch

After running for a few hours:

tail -f /var/log/fail2ban.log

You’ll see entries like:

2024-01-15 03:42:11,000 fail2ban.filter[1234]: INFO    [sshd] Found 1.2.3.4 - 2024-01-15 03:42:10
2024-01-15 03:42:15,000 fail2ban.actions[1234]: NOTICE  [sshd] Ban 1.2.3.4

Watching this for a day on a fresh server is educational — you’ll see just how much automated scanning is hitting your ports constantly.

The combination of Fail2ban and properly configured SSH (key-only auth, sensible MaxAuthTries) makes brute-force attacks practically ineffective. As part of an overall hardening approach, the Linux server hardening checklist covers the full set of steps for securing a fresh server.

Scroll to Top