What Fail2Ban Does

Fail2Ban monitors log files for repeated authentication failures and temporarily bans the offending IP addresses using firewall rules. It is reactive, not preventive — it stops an attacker who is hammering a service after a configurable number of failed attempts.

It is not a substitute for proper access control: key-based SSH, strong passwords, network segmentation. But it is a practical layer that reduces noise and closes the window on credential stuffing attacks.

Installing Fail2Ban

On Ubuntu:

sudo apt update
sudo apt install fail2ban

Fail2Ban installs with a default configuration that protects SSH out of the box. Verify it is running:

sudo systemctl status fail2ban

Configuration Structure

Fail2Ban uses two configuration layers:

Create the override file:

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

Or, better, create a minimal jail.local that only contains what you change:

sudo nano /etc/fail2ban/jail.local

Global Defaults

Start with sensible global defaults in jail.local:

[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
banaction = nftables-multiport
banaction_allports = nftables-allports
ignoreip = 127.0.0.1/8 ::1

Protecting SSH

The SSH jail is enabled by default. Harden it:

[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 3
bantime  = 2h

Three failed SSH attempts in ten minutes earns a two-hour ban. If you run SSH on a non-standard port:

[sshd]
port = 2222

Aggressive Mode

For SSH, Fail2Ban has an aggressive filter that also catches connection attempts before authentication:

[sshd]
mode = aggressive

This catches port scanners that connect and disconnect without attempting a login.

Protecting Apache

Authentication Failures

[apache-auth]
enabled  = true
port     = http,https
filter   = apache-auth
logpath  = /var/log/apache2/error.log
maxretry = 5
bantime  = 1h

Bad Bots and Scanners

[apache-badbots]
enabled  = true
port     = http,https
filter   = apache-badbots
logpath  = /var/log/apache2/access.log
maxretry = 2
bantime  = 48h

Overflows and Exploits

[apache-overflows]
enabled  = true
port     = http,https
filter   = apache-overflows
logpath  = /var/log/apache2/error.log
maxretry = 2
bantime  = 1h

PHP URL Injection

[php-url-fopen]
enabled  = true
port     = http,https
filter   = php-url-fopen
logpath  = /var/log/apache2/access.log
maxretry = 1
bantime  = 48h

Progressive Banning

Fail2Ban supports incremental ban times. Repeat offenders get longer bans:

[DEFAULT]
bantime.increment = true
bantime.factor    = 2
bantime.maxtime   = 4w
bantime.overalljails = true

First ban: 1 hour. Second: 2 hours. Third: 4 hours. And so on, up to four weeks. The overalljails flag means an IP banned in the SSH jail counts toward escalation in the Apache jail too.

Writing Custom Filters

Fail2Ban filters live in /etc/fail2ban/filter.d/. Each filter defines a failregex — a regular expression that matches a log line indicating a failure.

Example: a custom filter for a web application that logs failed logins to /var/log/myapp/auth.log:

Create /etc/fail2ban/filter.d/myapp.conf:

[Definition]
failregex = ^<HOST> - - \[.*\] "POST /login HTTP/.*" 401
ignoreregex =

Then add the jail:

[myapp]
enabled  = true
port     = http,https
filter   = myapp
logpath  = /var/log/myapp/auth.log
maxretry = 5
bantime  = 1h

Test your filter before enabling it:

sudo fail2ban-regex /var/log/myapp/auth.log /etc/fail2ban/filter.d/myapp.conf

This shows how many lines match without actually banning anything.

Managing Bans

Check Status

sudo fail2ban-client status
sudo fail2ban-client status sshd

The second command shows currently banned IPs and the total number of bans.

Unban an IP

sudo fail2ban-client set sshd unbanip 192.0.2.100

Ban an IP Manually

sudo fail2ban-client set sshd banip 192.0.2.100

Check the Firewall Rules

sudo nft list ruleset | grep fail2ban

Or with iptables:

sudo iptables -L f2b-sshd -n

Email Notifications

Fail2Ban can send email on ban events. Configure the action:

[DEFAULT]
destemail  = admin@example.com
sender     = fail2ban@example.com
mta        = sendmail
action     = %(action_mwl)s

The action_mwl action bans the IP, sends an email with whois data and the relevant log lines. Ensure your MTA is configured. For low-volume servers this is useful; for high-traffic servers the email volume can become overwhelming — use action_ (ban only) and review logs periodically instead.

Hardening Fail2Ban Itself

Use nftables

On Ubuntu 22.04+, prefer nftables over iptables:

[DEFAULT]
banaction = nftables-multiport

Set the Database Purge Age

Fail2Ban stores ban history in /var/lib/fail2ban/fail2ban.sqlite3. Purge old records:

[DEFAULT]
dbpurgeage = 7d

Restrict the Fail2Ban Socket

sudo chmod 0700 /var/run/fail2ban

Log Rotation

Ensure Fail2Ban's own logs rotate. Check /etc/logrotate.d/fail2ban exists and contains sensible settings. If not:

/var/log/fail2ban.log {
    weekly
    rotate 12
    compress
    delaycompress
    missingok
    notifempty
    postrotate
        fail2ban-client flushlogs >/dev/null 2>&1 || true
    endscript
}

Testing Your Configuration

After editing jail.local, always test the configuration before restarting:

sudo fail2ban-client -t

If the test passes, restart:

sudo systemctl restart fail2ban

Then test a ban by deliberately failing SSH authentication from a test IP (not your management IP):

ssh -o PasswordAuthentication=yes baduser@your-server

Repeat until the ban triggers, then check:

sudo fail2ban-client status sshd

Summary

Install Fail2Ban, create jail.local with your overrides, enable jails for SSH and Apache at minimum, use progressive banning for repeat offenders, and test your filters before deploying them. It does not replace proper access control, but it is a practical, low-overhead layer that meaningfully reduces brute-force attack surface.