Advanced Fail2ban Configuration
This guide covers advanced Fail2ban configurations to protect your services beyond SSH.
Installation
sudo apt update
sudo apt install fail2ban
File Structure
/etc/fail2ban/
├── fail2ban.conf # Global configuration
├── jail.conf # Default jails (do not modify)
├── jail.local # Your customizations
├── jail.d/ # Additional jails
├── filter.d/ # Filters (regex)
└── action.d/ # Actions (ban, notifications)
Important
Never modify jail.conf directly. Create jail.local instead which takes priority.
Global Configuration
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
# Ignore these IPs (your fixed IP, localhost)
ignoreip = 127.0.0.1/8 ::1 YOUR_FIXED_IP
# Default ban duration (24h)
bantime = 86400
# Observation window
findtime = 600
# Attempts before ban
maxretry = 3
# Detection backend
backend = systemd
# Default action (ban + log)
banaction = iptables-multiport
# Email for notifications (optional)
destemail = admin@yourdomain.com
sender = fail2ban@yourdomain.com
mta = sendmail
action = %(action_mwl)s
Jails for Common Services
SSH (enhanced)
[sshd]
enabled = true
port = ssh,2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
findtime = 300
bantime = 86400
Nginx - Authentication
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600
Nginx - Anti-bot/scanner
[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 86400
Nginx - Request Limiting
First create the filter:
sudo nano /etc/fail2ban/filter.d/nginx-limit-req.conf
[Definition]
failregex = limiting requests, excess:.* by zone.*client: <HOST>
ignoreregex =
Then the jail:
[nginx-limit-req]
enabled = true
port = http,https
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 60
bantime = 7200
Apache
[apache-auth]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/apache2/error.log
maxretry = 3
bantime = 3600
[apache-overflows]
enabled = true
port = http,https
filter = apache-overflows
logpath = /var/log/apache2/error.log
maxretry = 2
bantime = 86400
MySQL/MariaDB
[mysqld-auth]
enabled = true
port = 3306
filter = mysqld-auth
logpath = /var/log/mysql/error.log
maxretry = 3
bantime = 86400
Postfix (Email)
[postfix]
enabled = true
port = smtp,465,587
filter = postfix
logpath = /var/log/mail.log
maxretry = 3
bantime = 86400
[postfix-sasl]
enabled = true
port = smtp,465,587,imap,imaps
filter = postfix-sasl
logpath = /var/log/mail.log
maxretry = 3
bantime = 86400
Dovecot (IMAP)
[dovecot]
enabled = true
port = imap,imaps,pop3,pop3s
filter = dovecot
logpath = /var/log/mail.log
maxretry = 3
bantime = 86400
Progressive Banning
To increase ban duration for repeat offenders:
[recidive]
enabled = true
filter = recidive
logpath = /var/log/fail2ban.log
action = iptables-allports[name=recidive]
bantime = 604800 # 1 week
findtime = 86400 # Look in the last 24h
maxretry = 3 # 3 bans = recidive
Creating a Custom Filter
Example to protect an API against brute force:
sudo nano /etc/fail2ban/filter.d/api-auth.conf
[Definition]
# Detect 401/403 errors in logs
failregex = ^<HOST> .* "(GET|POST) /api/auth.*" (401|403)
^<HOST> .* "POST /api/login.*" (401|403)
ignoreregex =
datepattern = %%d/%%b/%%Y:%%H:%%M:%%S %%z
Test the filter:
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/api-auth.conf
Then enable the jail:
[api-auth]
enabled = true
port = http,https
filter = api-auth
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 300
bantime = 3600
Email Notifications
Configuration with Postfix:
[DEFAULT]
destemail = admin@yourdomain.com
sender = fail2ban@yourdomain.com
mta = sendmail
# Available actions:
# action_ : ban only
# action_mw : ban + email with whois
# action_mwl : ban + email with whois + logs
action = %(action_mwl)s
Discord/Slack Notifications
Create a custom action:
sudo nano /etc/fail2ban/action.d/discord.conf
[Definition]
actionban = curl -X POST -H "Content-Type: application/json" \
-d '{"content": "**Fail2ban** - IP `<ip>` banned on `<name>` (<failures> attempts)"}' \
"YOUR_DISCORD_WEBHOOK"
actionunban = curl -X POST -H "Content-Type: application/json" \
-d '{"content": "**Fail2ban** - IP `<ip>` unbanned from `<name>`"}' \
"YOUR_DISCORD_WEBHOOK"
Use it in a jail:
[sshd]
enabled = true
action = iptables-multiport[name=sshd, port="ssh", protocol=tcp]
discord
Useful Commands
# Global status
sudo fail2ban-client status
# Jail status
sudo fail2ban-client status sshd
# List banned IPs
sudo fail2ban-client status sshd | grep -E "Banned IP"
# 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
# Reload configuration
sudo fail2ban-client reload
# View logs in real-time
sudo tail -f /var/log/fail2ban.log
# Test a filter
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
Integration with UFW
By default, Fail2ban uses iptables. To use UFW:
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
banaction = ufw
Monitoring with fail2ban-client
Daily report script:
#!/bin/bash
# /usr/local/bin/fail2ban-report.sh
echo "=== Fail2ban Report $(date) ==="
echo ""
for jail in $(fail2ban-client status | grep "Jail list" | sed 's/.*://;s/,//g'); do
echo "--- $jail ---"
fail2ban-client status $jail
echo ""
done
Add to cron:
0 8 * * * /usr/local/bin/fail2ban-report.sh | mail -s "Fail2ban Report" admin@yourdomain.com
Troubleshooting
Fail2ban won't start
# Check syntax
sudo fail2ban-client -d
# View errors
sudo journalctl -u fail2ban -f
Bans not working
# Check iptables rules
sudo iptables -L -n | grep fail2ban
# Check that filter matches
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf --print-all-matched
Performance
For high-traffic servers, use backend = systemd and avoid monitoring very large logs without precise filters.