When I analyzed the logs of a public healthcare organization with 2,000 endpoints, I counted over 15,000 failed SSH login attempts in a single night. Bots scan entire IP subnets, run through password dictionaries, and hammer port 22 until they find a weak credential. Without an automated system to block these brute-force attacks, you’re just hoping no one guesses the right password—and hope has never protected any server. Fail2ban is the tool that turns your authentication logs into a reactive firewall. It reads failed attempts, identifies malicious IPs, and updates your iptables or nftables rules to drop traffic in real time. In this guide, we’ll configure Fail2ban on Linux to protect SSH and web services, from prerequisites to operational testing.
Prerequisites and Test Environment
Before you begin, make sure you have root access or a user with sudo privileges. This guide is tested on Ubuntu 22.04/24.04 and CentOS Stream 9, but the logic applies to any modern Linux distribution. You must have an active firewall, such as UFW or firewalld, and an exposed SSH server. Do not apply restrictive configurations on a production server without testing them in an isolated environment first. Accidentally banning your own IP will lock you out of the system.
What is Fail2ban and Why Every Linux Server Needs It
Fail2ban is a Python daemon that monitors log files for specific patterns. When it detects an abnormal number of authentication errors from the same IP, it takes action by modifying firewall rules to block that node. It’s not a replacement for public key authentication or a VPN, but it serves as the first layer of defense against internet background noise. It reduces server load, cuts down log clutter, and prevents bots from burning resources. To comply with standards like NIS2 and ISO 27001:2022, securing remote access is mandatory. Fail2ban allows you to configure an automated, proportional response to attacks.
Installation on Ubuntu/Debian and CentOS/RHEL
Installation requires a single command. On Ubuntu and Debian, the package is available in the official repositories.
sudo apt install fail2ban
On CentOS Stream, RHEL, and derivatives, you’ll find it in EPEL.
sudo dnf install epel-release
sudo dnf install fail2ban
Once installed, enable the service at boot and start it.
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Verify the status. The daemon must be active and error-free.
sudo systemctl status fail2ban
Configuring jail.local: The Parameters That Matter
The package provides a default configuration file, jail.conf. You should never modify it directly; system updates will overwrite it. The best practice is to copy it to jail.local. Fail2ban reads the .local file first, giving your parameters precedence.
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Open jail.local in your text editor. The global parameters you need to tune are located in the [DEFAULT] section.
bantime: The duration of the ban. The default is 10 minutes, which is far too low. Set it to 3600 seconds (1 hour) or more for sustained attacks.findtime: The time window in which Fail2ban counts attempts. 600 seconds (10 minutes) is a solid baseline.maxretry: The number of failures before a ban is triggered. Set this to 3 for SSH, and 5 for web services.
These values define the aggressiveness of your defense. A bantime that’s too short allows bots to simply retry, while a maxretry that’s too high gives them too many attempts.
Protecting SSH: Recommended Production Settings
Now let’s activate the specific jail for SSH. Find the [sshd] section in the jail.local file and configure it as follows:
[sshd]
enabled = true
port = ssh
maxretry = 3
bantime = 3600
findtime = 600
With this configuration, after 3 failed login attempts within 10 minutes, the sender’s IP is banned for an hour. If your SSH port isn’t the standard port 22, update the port parameter with the correct number. After saving the file, restart the service to apply the rules.
sudo systemctl restart fail2ban
[Read also: Hardening SSH on Linux: 10 Essential Settings]
Protecting Nginx and Apache with Fail2ban
Fail2ban doesn’t just protect SSH. Web servers are constant targets for brute-force attacks on login pages or vulnerability scanners. You need to create custom jails for Nginx or Apache logs. For Nginx, enable the [nginx-http-auth] jail to protect Basic Auth authentication. For Apache, use [apache-auth]. Both read authentication error logs and apply the same principle: too many repeated errors trigger an IP ban. Add the filters to jail.local, making sure to specify the correct log path in the logpath parameter.
Monitoring Bans in Real Time
To verify that Fail2ban is working, check the status of active jails. The command displays banned IPs and the attempt count.
sudo fail2ban-client status sshd
The output returns the number of banned IPs and the list of addresses. If you see the list populating, it means the daemon is intercepting malicious traffic. Check the system logs for details on every action taken.
sudo tail -f /var/log/fail2ban.log
Unbanning an Accidentally Blocked IP
Sooner or later, you’ll ban a legitimate user—a colleague who forgot their password, or an internal automation script with expired credentials. You don’t need to restart the service. Use the client to remove the ban in real time.
sudo fail2ban-client set sshd unbanip 1.2.3.4
Replace 1.2.3.4 with the actual IP address. The action takes effect immediately. If you don’t remember the IP, use sudo fail2ban-client status sshd to view the list of active bans.
Testing Your Fail2ban Configuration
Don’t rely on hope. Test the configuration. From an external machine, try logging in via SSH with an incorrect password 3 times in a row. On the fourth attempt, the connection should be refused (Connection refused or Timeout). Check the server.
sudo fail2ban-client status sshd
The test IP must appear in the banned list. If it doesn’t, verify that the logpath parameter in the jail points to the correct log file for your system (e.g., /var/log/auth.log on Ubuntu, /var/log/secure on RHEL). An incorrect path is the most common cause of malfunction.
Common Errors and Troubleshooting
The most recurring issue is pointing to the wrong log file. If Fail2ban isn’t detecting attacks, verify the path in logpath. Another frequent error is a missing firewall backend. Fail2ban relies on iptables, nftables, or firewalld. If you’re using UFW on Ubuntu, make sure the jail has banaction = ufw. Without this directive, Fail2ban writes rules to iptables that UFW ignores, creating conflicts. Finally, the regular expressions in the default filters might not align with the log format of your specific service version. Check the filters in /etc/fail2ban/filter.d/ and adjust the regex if necessary.
Conclusion and Operational Takeaways
Fail2ban transforms passive logs into an active defense. I installed it as a first step at a government agency with 2,000 endpoints to stop bot background noise, and the impact on the logs was immediate. Remember three rules: always copy jail.conf to jail.local, lower the maxretry and increase the bantime for SSH, and always align the banaction with your system firewall. To dive deeper into directives and filters, check out the official documentation at fail2ban.readthedocs.io. Don’t leave your servers exposed to brute-force attacks.