Overview
Fail2Ban is an intrusion prevention framework written in Python. It scans system logs (e.g., auth logs, mail server logs, web server logs) for patterns matching malicious activities—such as brute-force authentication attacks, port scanning, and vulnerability probing—and dynamically adjusts firewall rules (using iptables, nftables, or UFW) to ban the offending IP addresses for a specified duration.
When to Use
- Hardening SSH access endpoints on public cloud instances.
- Protecting web applications (e.g., Nginx HTTP Basic Auth, WordPress login portals) against dictionary brute-force attacks.
- Mitigating bot probing and search engine crawler spam on public web apps.
- Securing SMTP, IMAP, or database ports from automated connection floods.
Prerequisites
- Linux server (Debian/Ubuntu systems used in examples).
sudo or root access.
- Firewall backend installed (
iptables, nftables, or UFW).
Procedure
Install Fail2Ban
sudo apt update
sudo apt install fail2ban -y
Enable and start the systemd daemon
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Configure Jails
Never modify jail.conf directly. Always write overriding rules in /etc/fail2ban/jail.local.
# /etc/fail2ban/jail.local - Custom server override rules
[DEFAULT]
bantime = 1h # Duration of the ban (m=minutes, h=hours, d=days)
findtime = 10m # Period within which failed retries trigger a ban
maxretry = 5 # Number of failures allowed within findtime
banaction = nftables-multiport # Firewall backend choice
ignoreip = 127.0.0.1/8 192.168.1.0/24 # Trust local subnets
[sshd]
enabled = true
port = ssh
maxretry = 3 # Stricter retry limits for SSH
bantime = 24h # Ban SSH attackers for a full day
logpath = /var/log/auth.log
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
[nginx-limit-req]
enabled = true
port = http,https
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
bantime = 2h
Create Custom Filters (Optional)
For custom applications, write a filter pattern file in /etc/fail2ban/filter.d/my-app.conf:
# /etc/fail2ban/filter.d/my-app.conf
[Definition]
failregex = ^<HOST> - - \[.*\] "POST /api/login HTTP/.*" 401 .*$
ignoreregex =
Validate Custom Filters
Before activating the filter, validate your regular expression against target log lines:
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/my-app.conf
Manage Active Bans via Client CLI
# Get overall status
sudo fail2ban-client status
# Get detailed statistics for a specific jail
sudo fail2ban-client status sshd
# Manually ban an offending IP
sudo fail2ban-client set sshd banip 198.51.100.42
# Unban a mistakenly blocked IP
sudo fail2ban-client set sshd unbanip 198.51.100.42
Pitfalls
- Systemd Journal Logs: Modern Linux installations (like Debian 12 or Ubuntu 24.04) log auth attempts via
systemd-journald instead of writing raw text files to /var/log/auth.log. If Fail2Ban is not monitoring attempts, configure the backend parameter: backend = systemd in jail.local.
- Log Rotation Delays: If log rotation utilities compress logs (e.g.,
auth.log.1.gz) too frequently, Fail2Ban can lose track of the open file descriptors, missing failed attempts during rotation windows.
- Lockout Risk: Never configure Fail2Ban without white-listing your local IP ranges or corporate VPN subnets in the
ignoreip parameter. Doing so runs the risk of locking yourself out of your remote server if you mistype your password a few times.
- Proxy Load Balancers: If your application is deployed behind a proxy load balancer (like Cloudflare, AWS ALB, or HAProxy), you must configure your web servers to parse
X-Forwarded-For headers so Fail2Ban logs the real client IP rather than the proxy's IP. Banning the proxy IP will block access for all users.
Verification
- Verify service runtime status
sudo systemctl status fail2ban
- Verify jail status and banned IPs
sudo fail2ban-client status
sudo fail2ban-client status sshd
- Verify custom regex matches
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/my-app.conf
Related skills
ufw - Uncomplicated Firewall configuration
nftables - Modern Linux firewalling
ssh-hardening - Securing SSH daemon configurations
1---2name: fail2ban3description: Installs Linux Fail2Ban with jail.local overrides so sshd, nginx, and mail log matches become nftables, iptables, or UFW bans, managed through fail2ban-client. Trigger when SSH, HTTP basic-auth, WordPress logins, or SMTP/IMAP face brute-force and bot probes. Not a WAF, IDS, or CDN bot-management product. Do not use on Windows hosts or behind a load balancer without real client IPs in logs.4---5
6## Overview
7Fail2Ban is an intrusion prevention framework written in Python. It scans system logs (e.g., auth logs, mail server logs, web server logs) for patterns matching malicious activities—such as brute-force authentication attacks, port scanning, and vulnerability probing—and dynamically adjusts firewall rules (using `iptables`, `nftables`, or `UFW`) to ban the offending IP addresses for a specified duration.
8
9## When to Use
10- Hardening SSH access endpoints on public cloud instances.
11- Protecting web applications (e.g., Nginx HTTP Basic Auth, WordPress login portals) against dictionary brute-force attacks.
12- Mitigating bot probing and search engine crawler spam on public web apps.
13- Securing SMTP, IMAP, or database ports from automated connection floods.
14
15## Prerequisites
16- Linux server (Debian/Ubuntu systems used in examples).
17- `sudo` or root access.
18- Firewall backend installed (`iptables`, `nftables`, or `UFW`).
19
20## Procedure
21
221. **Install Fail2Ban**
23 ```bash
24 sudo apt update
25 sudo apt install fail2ban -y
26 ```
27
282. **Enable and start the systemd daemon**
29 ```bash
30 sudo systemctl enable fail2ban
31 sudo systemctl start fail2ban
32 ```
33
343. **Configure Jails**
35 Never modify `jail.conf` directly. Always write overriding rules in `/etc/fail2ban/jail.local`.
36 ```ini
37 # /etc/fail2ban/jail.local - Custom server override rules
38
39 [DEFAULT]
40 bantime = 1h # Duration of the ban (m=minutes, h=hours, d=days)
41 findtime = 10m # Period within which failed retries trigger a ban
42 maxretry = 5 # Number of failures allowed within findtime
43 banaction = nftables-multiport # Firewall backend choice
44 ignoreip = 127.0.0.1/8 192.168.1.0/24 # Trust local subnets
45
46 [sshd]
47 enabled = true
48 port = ssh
49 maxretry = 3 # Stricter retry limits for SSH
50 bantime = 24h # Ban SSH attackers for a full day
51 logpath = /var/log/auth.log
52
53 [nginx-http-auth]
54 enabled = true
55 port = http,https
56 filter = nginx-http-auth
57 logpath = /var/log/nginx/error.log
58 maxretry = 5
59
60 [nginx-limit-req]
61 enabled = true
62 port = http,https
63 filter = nginx-limit-req
64 logpath = /var/log/nginx/error.log
65 maxretry = 10
66 bantime = 2h
67 ```
68
694. **Create Custom Filters (Optional)**
70 For custom applications, write a filter pattern file in `/etc/fail2ban/filter.d/my-app.conf`:
71 ```ini
72 # /etc/fail2ban/filter.d/my-app.conf
73 [Definition]
74 failregex = ^<HOST> - - \[.*\] "POST /api/login HTTP/.*" 401 .*$
75 ignoreregex =
76 ```
77
785. **Validate Custom Filters**
79 Before activating the filter, validate your regular expression against target log lines:
80 ```bash
81 fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/my-app.conf
82 ```
83
846. **Manage Active Bans via Client CLI**
85 ```bash
86 # Get overall status
87 sudo fail2ban-client status
88
89 # Get detailed statistics for a specific jail
90 sudo fail2ban-client status sshd
91
92 # Manually ban an offending IP
93 sudo fail2ban-client set sshd banip 198.51.100.42
94
95 # Unban a mistakenly blocked IP
96 sudo fail2ban-client set sshd unbanip 198.51.100.42
97 ```
98
99## Pitfalls
100- **Systemd Journal Logs**: Modern Linux installations (like Debian 12 or Ubuntu 24.04) log auth attempts via `systemd-journald` instead of writing raw text files to `/var/log/auth.log`. If Fail2Ban is not monitoring attempts, configure the backend parameter: `backend = systemd` in `jail.local`.
101- **Log Rotation Delays**: If log rotation utilities compress logs (e.g., `auth.log.1.gz`) too frequently, Fail2Ban can lose track of the open file descriptors, missing failed attempts during rotation windows.
102- **Lockout Risk**: Never configure Fail2Ban without white-listing your local IP ranges or corporate VPN subnets in the `ignoreip` parameter. Doing so runs the risk of locking yourself out of your remote server if you mistype your password a few times.
103- **Proxy Load Balancers**: If your application is deployed behind a proxy load balancer (like Cloudflare, AWS ALB, or HAProxy), you must configure your web servers to parse `X-Forwarded-For` headers so Fail2Ban logs the real client IP rather than the proxy's IP. Banning the proxy IP will block access for all users.
104
105## Verification
1061. **Verify service runtime status**
107 ```bash
108 sudo systemctl status fail2ban
109 ```
1102. **Verify jail status and banned IPs**
111 ```bash
112 sudo fail2ban-client status
113 sudo fail2ban-client status sshd
114 ```
1153. **Verify custom regex matches**
116 ```bash
117 fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/my-app.conf
118 ```
119
120## Related skills
121- `ufw` - Uncomplicated Firewall configuration
122- `nftables` - Modern Linux firewalling
123- `ssh-hardening` - Securing SSH daemon configurations