Skip to main content

Fail2ban: Protecting SSH and Nginx from Password Guessing

Security · 20.04.2025 · 6 min read
Illustration for “Fail2ban: Protecting SSH and Nginx from Password Guessing”

Every public VPS faces thousands of SSH password guessing attempts every day. Fail2ban analyses logs and automatically blocks attacking IP addresses.

Installing Fail2ban

sudo apt update
sudo apt install -y fail2ban

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Setting up SSH protection

Create a local config (do not edit jail.conf — it gets overwritten on updates):

sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime  = 3600
findtime = 600
maxretry = 5
ignoreip = 127.0.0.1/8 YOUR_HOME_IP

[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 3
bantime  = 86400
sudo systemctl restart fail2ban

What each parameter means

ParameterValue in the exampleMeaning
bantime3600 (86400 for sshd)How many seconds an IP is blocked after exceeding the limit
findtime600The time window in seconds over which failed attempts are counted
maxretry5 (3 for sshd)How many failed attempts are allowed within findtime
ignoreip127.0.0.1/8 and your IPAddresses that are never blocked

The [sshd] section overrides maxretry and bantime from [DEFAULT] — for SSH they are usually made stricter (fewer attempts, a longer ban) than the general default for other services.

Common configuration mistakes

Wrong logpath for the distribution

The path /var/log/auth.log is correct for Debian and Ubuntu. On CentOS, AlmaLinux and other RHEL-like distributions the authentication log lives at /var/log/secure — with the wrong path Fail2ban starts without an error, but the jail never triggers because it simply cannot find a file to read.

# Check that the jail is actually reading the log file:
sudo fail2ban-client status sshd
# The "File list" line should show a real log path with data in it,
# not the path to an empty or nonexistent file

ignoreip without your own IP

If you do not add your working IP to ignoreip and the maxretry threshold is low, ordinary password typos during a normal login can ban you for the bantime period. This is especially dangerous with a dynamic home IP — check the current address every time your provider reconnects you.

On systems where the authentication log is kept only through the systemd journal, with no /var/log/auth.log or /var/log/secure file (some minimal images and container environments), a regular logpath will not work at all. For that case the jail has a separate backend parameter that reads entries directly from the journal instead of a file on disk:

[sshd]
enabled = true
backend = systemd
maxretry = 3

Basic commands

# Status of all jails
sudo fail2ban-client status

# SSH jail status
sudo fail2ban-client status sshd

# Manually unban an IP
sudo fail2ban-client set sshd unbanip 203.0.113.42

# View blocked IPs
sudo iptables -L f2b-sshd -n --line-numbers

Protecting Nginx from DDoS and scanning

# Add to jail.local:
[nginx-http-auth]
enabled  = true
logpath  = /var/log/nginx/error.log

[nginx-botsearch]
enabled  = true
logpath  = /var/log/nginx/access.log
maxretry = 2
✅ Change the SSH port from 22 to a non-standard one (for example, 2222) in /etc/ssh/sshd_config — this cuts the number of attacks by 95%.
⚠️ Before changing the SSH port, be sure to open the new port in UFW: sudo ufw allow 2222/tcp

When Fail2ban will not help

  • A distributed brute force from thousands of different IPs, one or two attempts from each — the maxretry threshold for an individual address never triggers because no single IP exceeds it.
  • An attack through a vulnerability in the application itself rather than password guessing — Fail2ban reacts to patterns in failed-authentication logs, not to arbitrary HTTP requests with no matching filter signature.
  • Logging in with a compromised but valid password or key on the first try — there is nothing to block based on repeated failures here; you need two-factor authentication and monitoring for suspicious logins.

Checking that the protection works

  1. Run sudo fail2ban-client status sshd — the output should show a correct log path and a growing Currently failed counter after failed login attempts.
  2. From another IP (not in ignoreip), deliberately enter a wrong SSH password more than maxretry times in a row — the next attempt should be rejected by the firewall, not the login form.
  3. Check the ban list: sudo fail2ban-client status sshd should show that IP in the Banned IP list.
  4. Unban the test IP with fail2ban-client set sshd unbanip and confirm the connection goes through again — this confirms the ban lifts normally, not only by hand through iptables.

Setting up the firewall that Fail2ban places its blocking rules on is covered in UFW for Ubuntu and Debian, and logging in with keys instead of a password removes the need for guessing entirely — see hardening SSH on a server.

Installation

sudo apt update
sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban

Creating the jail.local Config

Never edit jail.conf — it gets overwritten on updates. Create jail.local:

># /etc/fail2ban/jail.local
[DEFAULT]
# Ban time in seconds (3600 = 1 hour, -1 = forever)
bantime  = 3600
# Time window to look for attempts (10 minutes)
findtime = 600
# Max attempts before a ban
maxretry = 5
# Exclude your own IP from bans!
ignoreip = 127.0.0.1/8 YOUR.TRUSTED.IP

[sshd]
enabled  = true
port     = 22
logpath  = %(sshd_log)s
maxretry = 3

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

[postfix-sasl]
enabled  = true
port     = smtp,submission
logpath  = /var/log/mail.log
maxretry = 5
sudo systemctl restart fail2ban

Management and Monitoring

# General status
sudo fail2ban-client status

# Status of a specific jail
sudo fail2ban-client status sshd

# Manually ban an IP
sudo fail2ban-client set sshd banip 192.168.1.100

# Unban an IP
sudo fail2ban-client set sshd unbanip 192.168.1.100

# View banned IPs in iptables
sudo iptables -L f2b-sshd -n -v

# Fail2Ban logs
sudo journalctl -u fail2ban -f

Real-Time Attack Monitoring

# Watch SSH bans in real time
sudo tail -f /var/log/auth.log | grep "Failed password"

# Top attacking IPs
sudo grep "Failed password" /var/log/auth.log |     awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -20
💡 ignoreip is MANDATORY: Add your home/office IP to ignoreip BEFORE the first run. Otherwise you risk banning yourself while testing. Always double-check the rule.
⚠️ Fail2Ban ≠ full protection: A brute force from thousands of different IPs (a botnet) will bypass Fail2Ban. Additionally restrict SSH access with an IP whitelist via UFW, or use a VPN to reach the server.
Was this article helpful?
← Back to Knowledge Base Ask Support