Skip to main content

Fail2ban to protect SSH and Nginx: practical configuration on Ubuntu

Rogelio Guerra Riverón
Author
Rogelio Guerra Riverón
Building my own web infrastructure from scratch. Here I document each step: servers, networks, containers and everything that comes along.

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 fail2ban

Verify that it’s running:

sudo systemctl status fail2ban

Fail2ban 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 = 3

Explanation:

  • 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 = 3600

Apply Changes
#

sudo systemctl restart fail2ban

Verify that the jails are active:

sudo fail2ban-client status

See detailed SSH status:

sudo fail2ban-client status sshd

Monitoring
#

After a few hours, I checked the activity:

sudo fail2ban-client status sshd

The output shows banned IPs. To see jail details:

sudo tail -f /var/log/fail2ban.log

Unblock 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 maxretry in 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#

Affiliate links. No extra cost to you.