Fail2ban monitors your service logs for repeated failures and blocks offending IPs using your firewall. This guide explains the concept and gives copy paste jails for SSH on 22, HTTP on 80 or 443, and MySQL or MariaDB on 3306.
How Fail2ban works
- Reads logs from services (for example: /var/log/auth.log, web server error or access logs, MySQL error log).
- Filters match suspicious lines.
- If failures exceed maxretry within findtime, Fail2ban bans the source IP for bantime via nftables on Debian 12.
Key terms:
- jail: the rule that ties a filter and an action to specific logs and ports
- filter: regex rules that match bad events in logs
- action: how to ban (we use nftables-multiport)
Install Fail2ban
sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban
Install rsyslog
Fail2ban only works if it can see login failures in your logs. On Debian 12, the system journal (journald) already captures them, but many Fail2ban jails still expect a traditional log file like /var/log/auth.log. To keep things simple, install rsyslog so those logs are written out automatically:
sudo apt install rsyslog
sudo systemctl enable --now rsyslog
Base configuration
Do not edit jail.conf. Create your own overrides.
/etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1
banaction = nftables-multiport
logtarget = /var/log/fail2ban.log
Reload after changes:
sudo fail2ban-client reload
Jail 1, protect SSH on port 22
/etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
port = 22
backend = auto
logpath = /var/log/auth.log
maxretry = 5
findtime = 10m
bantime = 1h
Check status:
sudo fail2ban-client status sshd
sudo tail -f /var/log/fail2ban.log
Jail 2, protect HTTP on ports 80 and 443
Fail2ban reacts to patterns in logs. Two common web auth cases are below. Use only the one that applies to your stack.
Nginx basic auth:/etc/fail2ban/jail.d/nginx-http-auth.local
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
findtime = 10m
bantime = 1h
Apache basic auth:/etc/fail2ban/jail.d/apache-auth.local
[apache-auth]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/apache2/error.log
maxretry = 3
findtime = 10m
bantime = 1h
Tip: run ls /etc/fail2ban/filter.d to see other web filters such as nginx-noscript or apache-badbots, then point a jail at the filter and the correct log path.
Jail 3, watch MySQL or MariaDB on port 3306
Best practice is not to expose 3306 publicly. Bind to localhost or a private network and restrict with your firewall. If you must watch for brute force on 3306:
/etc/fail2ban/jail.d/mysqld-auth.local
[mysqld-auth]
enabled = true
port = 3306
filter = mysqld-auth
logpath = /var/log/mysql/error.log
maxretry = 3
findtime = 10m
bantime = 6h
Confirm the error log path inside MariaDB or MySQL:
SHOW VARIABLES LIKE 'log_error';
If your DB logs only to the journal, you can switch a jail to use systemd:
[mysqld-auth]
enabled = true
port = 3306
filter = mysqld-auth
backend = systemd
journalmatch = _SYSTEMD_UNIT=mariadb.service + _COMM=mysqld
maxretry = 3
findtime = 10m
bantime = 6h
Optional, recidive jail for repeat offenders
/etc/fail2ban/jail.d/recidive.local
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
bantime = 1d
findtime = 1d
maxretry = 5
Apply and verify
Check configuration syntax:
sudo fail2ban-client -d
Reload Fail2ban:
sudo fail2ban-client reload
List jails and show bans:
sudo fail2ban-client status
sudo fail2ban-client status sshd
Unban a specific IP:
sudo fail2ban-client set sshd unbanip 203.0.113.45
Check nftables sets for banned IPs:
sudo nft list ruleset | grep -A3 'f2b'
Troubleshooting
- Filters available:
ls /etc/fail2ban/filter.d
- Test a filter against a log:
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
- Reverse proxy or CDN:
Ensure your web server logs the real client IP, not the proxy, otherwise you will ban the proxy’s IP. - Dockerized services:
Make sure containers write logs to files on the host or to journald so Fail2ban can read them.