Do you want to see Fail2Ban in action? We have it running on this website. Check out how many bans have been executed in the last few days—click here!
What is Fail2Ban?
Fail2Ban is an open-source intrusion prevention tool that protects servers from brute-force attacks and unauthorized access. It monitors log files (e.g., SSH, Nginx) for suspicious activity and bans offending IP addresses using firewall rules.
How Fail2Ban Works
- Monitor Logs: Scans service-specific logs (e.g.,
/var/log/auth.log
for SSH). - Detect Patterns: Uses regex to identify repeated failed login attempts.
- Ban IPs: Blocks IPs exceeding predefined failure thresholds via firewall rules.
- Unban (If you want) After Time: Automatically unblocks IPs after a configurable period.
Why It’s Effective:
- Lightweight, flexible, and automated.
- Customizable rules (called "jails") for specific services.
Installation Guide
Debian/Ubuntu:
sudo apt update && sudo apt install fail2ban
RHEL/CentOS:
sudo dnf install fail2ban # or sudo yum install fail2ban
Basic Configuration
Before using it, we need to understand some basic concepts of Fail2Ban: Jails, Filters, and Actions.
Jails
A jail is a configuration that defines how Fail2Ban should monitor and protect a specific service (e.g., SSH, Nginx, Apache). Each jail specifies:
- Which log file to monitor.
- How many failed attempts are allowed (
maxretry
). - The time window for counting failures (
findtime
). - How long to ban an IP (
bantime
). - The action to take (e.g., block via firewall).
Jails link together filters (regex patterns to detect attacks) and actions (what to do when an attack is detected)
you can create your own jails or use some just configured ones:
/etc/fail2ban/jail.conf # Base configuration (do NOT edit directly)
/etc/fail2ban/jail.local # Custom overrides (safe to edit)
/etc/fail2ban/jail.d/ # Additional jail configs (e.g., *.conf files)
Do not edit /etc/fail2ban/jail.conf
directly, as it may be overwritten during updates. Instead, use /etc/fail2ban/jail.local
or create custom jail configurations inside /etc/fail2ban/jail.d/
for persistent settings
Let's check a custom jail that we could create in jail.local
[my-nginx-404]
enabled = true
filter = my-nginx-404
logpath = /var/log/nginx/access.log
# Path to Nginx access log
maxretry = 5
# 5 errors = ban
findtime = 5m
bantime = 24h
action = iptables-block send_notification
Parameter | Description |
---|---|
enabled |
Activates the jail. Set to true to enable it. |
port |
Specifies the ports to block (http = 80, https = 443). |
filter |
The filter file (nginx-404 ) used to detect malicious activity. |
logpath |
Path to the Nginx access log (/var/log/nginx/access.log ). |
maxretry |
Number of failed attempts (404/403 errors) allowed before banning. |
findtime |
Time window (5 minutes) to count failures. |
bantime |
Duration to ban an IP (24 hours). |
action |
What to do when banning (default: block via firewall). (Note: you can execute more than one action) |
Filters
A filter is a set of regex patterns that Fail2Ban uses to identify malicious activity in log files. Filters parse logs to detect patterns like:
- Failed SSH login attempts.
- Unauthorized access to a web app (e.g., Nginx 403 errors).
Filters determine what constitutes an attack. Fail2Ban compares log entries against these regex rules to trigger bans.
Default filters are located in:
/etc/fail2ban/filter.d/ # Contains all filter definitions (*.conf files)
Example of a filter that catches all the 404 errors from nginx log file:
[Definition]
failregex = ^<HOST> - - \[.*\] "(GET|POST|HEAD) .* HTTP/.*" 404
ignoreregex =
Actions
Actions define what Fail2Ban does when the something triggers the jail configuration. Common actions include:
- Adding a firewall rule to block an IP.
- Sending an email alert.
- Logging the ban.
The most used action is to enforce security policies (e.g., blocking IPs via iptables
or firewalld
).
Default actions:
/etc/fail2ban/action.d/ # Contains action definitions (*.conf files)
- Example actions:
action.d/iptables-allports.conf
(block IP via iptables).action.d/sendmail.conf
(send email alerts).
# /etc/fail2ban/action.d/iptables-allports.conf
[Definition]
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
This action blocks an IP using iptables
.
In Every Action file you could have 4 different components:
- actionban: is executed when the condition to do the ban is met (the action could be to block traffic from the IP that is doing something bad )
- actionunban: is executed when the condition to do the unban is met (the action could be to remove the firewall configuration that blocked the Ip on the ban)
- actionstart: is executed when the jail starts (for example, here you can do some action to configure your firewall, if needed)
- actionstop: is executed when the jail stop (for example, you could send a notification about the fact that the jail is not operating anymore)
Note: there is also a component called actioncheck that is executed when the jail starts, and before each ban. If you need, you can use actioncheck to be sure that you can execute the action in the action ban; for example, you could have to check a database connection or other stuff.
To summarize:
Concept | Role | File Location |
---|---|---|
Jails | Define monitoring rules | /etc/fail2ban/jail.local or jail.d/ |
Filters | Detect attacks via regex | /etc/fail2ban/filter.d/ |
Actions | Enforce bans/notifications | /etc/fail2ban/action.d/ |
Let's Try
Now that we have installed Fail2Ban and we know a little bit about how to configure it let's try it, we will do a basic configuration to protect the ssh. First of all we have to create a local configuration for our jail
sudo nano /etc/fail2ban/jail.local
[sshd]
enabled = true
maxretry = 3
# Allow 3 failed attempts
findtime = 10m
# Within 10 minutes
bantime = 1h
# Ban for 1 hour
logpath = /var/log/auth.log
# Path to SSH logs (usually /var/log/auth.log)
filter = sshd
#we use the filter that we just have
action = iptables-allports[name=sshd]
#we use an action that will block the ip of the "attacker" on every port
For the Filter and the Action, we will use some that ship with fail2ban; if we want, we can also write them by ourselves, but for our purpose, it is not needed.
Starting
After saving the jail we can try to start:
sudo systemctl start fail2ban # start fail2ban
sudo systemctl status fail2ban # check if fail2ban is running
If, checking the status, you see something like:
Active: failed (Result: exit-code)
You can check the details of the error with:
sudo journalctl -u fail2ban
Testing
If we have fail2ban running correctly we can check the active jails and if there are some active bans in the jails that we have:
sudo fail2ban-client status # List active jails
sudo fail2ban-client status sshd # View banned IPs for SSH
This will be the output of the command:
debian@mojalab:/home/debian$ sudo fail2ban-client status # List active jails
Status
|- Number of jail: 1
`- Jail list: sshd
debian@mojalab:/home/debian$ sudo fail2ban-client status sshd # View banned IPs for SSH
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
you could also check the log of fail2ban, usually you can find it in /var/log/fail2ban, with tail -f you could observe the log file ,for example, while doing a test
sudo tail -f /var/log/fail2ban.log
In the following example I made more than 3 login attempts with the wrong credential and then I got banned
2025-02-02 00:35:24,825 fail2ban.filter [298523]: INFO [sshd] Found 45.80.184.201 - 2025-02-02 00:35:24
2025-02-02 00:35:31,582 fail2ban.filter [298523]: INFO [sshd] Found 45.80.184.201 - 2025-02-02 00:35:31
2025-02-02 00:35:32,050 fail2ban.filter [298523]: INFO [sshd] Found 45.80.184.201 - 2025-02-02 00:35:32
2025-02-02 00:35:32,132 fail2ban.actions [298523]: NOTICE [sshd] Ban 45.80.184.201
checking again I got:
debian@mojalab:/home/debian$ sudo fail2ban-client status sshd # View banned IPs for SSH
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 6
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 45.80.184.201
Unban an IP
To manually unban an IP address from a specific jail (e.g., sshd
), use the following command:
sudo fail2ban-client set <jail-name> unbanip <IP-address>
In my example it will be
debian@kasper:~$ sudo fail2ban-client set sshd unbanip 45.80.184.201
1
As you see it will return 1 if the unban was successful 0 otherwise
Whitelisting
If needed (for example you have a lot of users that connect to your server using the same Ip Address and you don't want to block everyone for just one user that keeps trying to login with the wrong credentials) you could put an Ip in Whitelist, this Ip will be excluded from the ban process.
To add the ip to the whitelist just edit the jail.local file:
sudo nano /etc/fail2ban/jail.local
and add:
[DEFAULT]
ignoreip = 45.80.184.201
and then restart and check if everything is ok:
debian@mojalab:~$ sudo systemctl restart fail2ban
debian@mojalab:~$ sudo systemctl status fail2ban
● fail2ban.service - Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2025-02-02 01:06:10 UTC; 5s ag
now even you receive a lot of failed login attempt from this IP it will not be banned.
After whitelisting if you check the log you will see something like:
2025-02-02 01:08:45,622 fail2ban.filter [298645]: INFO [sshd] Ignore 45.80.184.201 by ip
2025-02-02 01:08:48,327 fail2ban.filter [298645]: INFO [sshd] Ignore 45.80.184.201 by ip
2025-02-02 01:08:50,019 fail2ban.filter [298645]: INFO [sshd] Ignore 45.80.184.201 by ip
2025-02-02 01:08:50,696 fail2ban.filter [298645]: INFO [sshd] Ignore 45.80.184.201 by ip
2025-02-02 01:08:50,854 fail2ban.filter [298645]: INFO [sshd] Ignore 45.80.184.201 by ip
Final Notes
Fail2Ban is a powerful first line of defense against brute-force attacks. However, it should be part of a multi-layered security approach rather than the sole protection mechanism. Keep your server updated, minimize exposed ports, implement strong authentication policies, and always have a backup plan.
Disclaimer: At MojaLab, we aim to provide accurate and useful content, but hey, we’re human (well, mostly)! If you spot an error, have questions, or think something could be improved, feel free to reach out—we’d love to hear from you. Use the tutorials and tips here with care, and always test in a safe environment. Happy learning!
No AI was mistreated in the making of this tutorial—every LLM was used with the respect it deserves.