Fail2ban watches log files for repeated failed authentication attempts, then automatically blocks the offending IP via firewall rules. It's the difference between a compromised server and one that stays yours.
This tutorial walks the install, configuration, and verification on Ubuntu 20.04+. No assumptions about firewall flavor—we cover both UFW and raw iptables.
Why Fail2ban Matters
SSH brute-force is the default attack on any internet-facing Linux box. Bots iterate through common usernames and passwords at machine speed. Fail2ban stops this by:
- Reading authentication logs in real time
- Counting failures per IP within a time window
- Banning the IP with a firewall rule when the threshold is crossed
- Unbanning after a cooldown period
Without it, your auth logs fill with thousands of failed attempts daily. With it, attackers get blocked after 5–6 tries. The asymmetry is stark.
Installation on Ubuntu
Fail2ban ships in the default repos. Install it cleanly:
sudo apt update
sudo apt install fail2ban
Start and enable the service:
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
Verify it's running:
sudo systemctl status fail2ban
You should see active (running). If the service fails to start, check /var/log/fail2ban.log for configuration errors—we'll address those next.
Core Configuration: Jails and Filters
Fail2ban uses two concepts:
- Filter: a regex pattern that matches failed login attempts in a log file.
- Jail: a rule that applies a filter to a log, counts failures, and triggers a ban.
The main config file is /etc/fail2ban/jail.conf. Do not edit it directly. Instead, create /etc/fail2ban/jail.local to override defaults:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Edit /etc/fail2ban/jail.local and set these parameters at the top:
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
destemail = your-email@example.com
sender = fail2ban@your-domain.com
action = %(action_mwl)s
Breakdown:
bantime: seconds to ban an IP (3600 = 1 hour). Use-1for permanent bans in production.findtime: seconds in which failures are counted (600 = 10 minutes). If 5 failures occur within 10 minutes, ban triggers.maxretry: number of failures before a ban.action: what happens when a ban triggers.action_mwlsends an email with whois data and log lines.
SSH Jail Setup
Look for the [sshd] section in /etc/fail2ban/jail.local. It should exist but may be disabled. Enable and tune it:
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 5
bantime = 3600
findtime = 600
If you run SSH on a non-standard port (e.g., 2222), change port = 2222.
The logpath variable points to your auth log. On Ubuntu, this is /var/log/auth.log. Fail2ban resolves %(sshd_log)s automatically from its paths config.
Gotcha: If you use journald exclusively and don't log to /var/log/auth.log, add this line:
logpath = /var/log/auth.log
Or configure fail2ban to read from journald. For most setups, /var/log/auth.log works out of the box.
Reload and Verify
After editing jail.local, reload fail2ban:
sudo systemctl reload fail2ban
Check that the sshd jail is active:
sudo fail2ban-client status sshd
Output should resemble:
Status for the jail: sshd
|- Filter file list: /etc/fail2ban/filter.d/sshd.conf
|- Address space: ipv4
|- Ban list:
|- Currently banned: 0
|- Total banned: 0
|- Jail 'sshd' has not been started yet
If sshd doesn't appear, check /var/log/fail2ban.log for parse errors in your config.
Firewall Integration
Fail2ban works with UFW, iptables, and nftables. It auto-detects your firewall on most Ubuntu systems.
For UFW users (recommended for beginners):
Fail2ban's default action uses iptables. UFW sits on top, so bans may not take effect visibly in ufw status. They still work—fail2ban writes to the underlying iptables chains. To confirm:
sudo iptables -L -n | grep -i fail2ban
You'll see chains like f2b-sshd if bans are active.
For raw iptables users:
Fail2ban's default action inserts rules into the INPUT chain. No extra setup needed.
Testing the Setup
Do not test by actually failing SSH logins from your own IP—you'll ban yourself. Instead, simulate a ban:
sudo fail2ban-client set sshd banip 192.0.2.1
Verify the ban:
sudo fail2ban-client status sshd
You should see the IP in the ban list. Unban it:
sudo fail2ban-client set sshd unbanip 192.0.2.1
For a real test, use a second machine or cloud instance to attempt logins. Watch the logs in real time:
sudo tail -f /var/log/fail2ban.log
After 5 failed attempts within 10 minutes, you'll see:
2024-01-15 14:32:10,123 fail2ban.actions [12345]: NOTICE [sshd] Ban 203.0.113.42
Monitoring and Maintenance
Check active bans regularly:
sudo fail2ban-client status
This lists all jails. For a specific jail:
sudo fail2ban-client status sshd
View the fail2ban log for issues:
sudo journalctl -u fail2ban -n 50
If email alerts are enabled, verify they arrive. If they don't, check that sendmail or postfix is installed and running.
When This Breaks
Legitimate IPs get banned: This happens if a user types the wrong password repeatedly. Whitelist trusted IPs by adding them to ignoreip in jail.local:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 10.0.0.5
Separate IPs with spaces. CIDR notation is supported.
Bans don't trigger: Check that fail2ban can read the log file. Run:
sudo tail /var/log/auth.log
If it's empty, SSH isn't logging there. Check /var/log/syslog or enable verbose SSH logging.
Service won't start: Syntax error in jail.local. Validate with:
sudo fail2ban-client -d
This performs a dry-run and prints parse errors.
High CPU usage: Large log files can slow regex matching. Archive old logs:
sudo logrotate -f /etc/logrotate.d/rsyslog
Trade-offs
Fail2ban is reactive, not preventive. It blocks after the attack starts. For true prevention, disable SSH password auth entirely and use key-based auth only. Fail2ban then protects against key-guessing (unlikely but possible).
Bans are also temporary. A permanent ban requires bantime = -1, but this risks locking out legitimate users behind shared IPs (e.g., corporate NAT). Balance security and usability by setting bantime to 1–4 hours. If your server also hosts a web storefront, keep in mind that headless storefront architectures introduce additional public-facing endpoints that benefit from the same IP-banning discipline.
One-Line Takeaway
Fail2ban stops SSH brute-force by banning IPs after repeated failed logins—install it, enable the sshd jail, and reload the service.