The Problem: Brute Force Attacks#
After exposing my Ubuntu server to the internet, I spent a night reviewing logs. SSH received failed login attempts every second. Nginx also had suspicious requests to common routes. I needed something to block these attempts automatically. Fail2ban was my solution.
Installation#
sudo apt update
sudo apt install fail2ban
sudo systemctl start fail2ban
sudo systemctl enable fail2banVerify that it’s running:
sudo systemctl status fail2banFail2ban Structure#
Fail2ban works like this: it monitors logs, detects failure patterns, and creates firewall rules to block IPs. It has three key components:
- Jails: define which service to protect
- Filters: regex patterns to detect failed attempts
- Actions: what to do when an attack is detected (ban, email, etc)
SSH Configuration#
The SSH jail comes preconfigured, but I customized it. Create file /etc/fail2ban/jail.local:
sudo nano /etc/fail2ban/jail.local[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3Explanation:
bantime: seconds the block lasts (1 hour)findtime: window in seconds to count attempts (10 min)maxretry: failed attempts before banning (3 in SSH, more restrictive)
Nginx Configuration#
For Nginx I needed to create a custom jail. First, the filter in /etc/fail2ban/filter.d/nginx-http-auth.conf:
sudo nano /etc/fail2ban/filter.d/nginx-http-auth.conf[Definition]
failregex = ^<HOST> - \S+ \[\S+ \S+\] ".*?" (401|403) .*$
ignoreregex =Then, add the jail to the local file:
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 600
bantime = 3600Apply Changes#
sudo systemctl restart fail2banVerify that the jails are active:
sudo fail2ban-client statusSee detailed SSH status:
sudo fail2ban-client status sshdMonitoring#
After a few hours, I checked the activity:
sudo fail2ban-client status sshdThe output shows banned IPs. To see jail details:
sudo tail -f /var/log/fail2ban.logUnblock an IP (just in case)#
If I made a mistake and blocked my own IP:
sudo fail2ban-client set sshd unbanip <IP>Important Notes#
- Fail2ban doesn’t replace SSH keys. I continued using key authentication, not passwords.
- I increased
maxretryin SSH to 3 because it’s more restrictive than 5 in web applications. - Nginx logs must be in combined format. Check
/etc/nginx/nginx.conf. - For filter changes, restart:
sudo systemctl restart fail2ban.
Result#
After implementing this, brute force attempts disappeared. The logs stopped being a mess. The server feels more at ease.
Fail2ban isn’t a silver bullet, but it’s an effective shield against basic automation. It’s worth the time spent configuring it correctly.
Recommended Equipment#
- YubiKey 5 NFC — Physical security key for 2FA and secure SSH access
- Raspberry Pi 3 B+ — Lightweight, low-power server to start your homelab
Affiliate links. No extra cost to you.