Brute-force attacks on SSH services are one of the most common threats facing Linux servers exposed to the internet. Automated scripts scan for open SSH ports and attempt login combinations continuously, hoping to guess weak credentials. Fail2Ban provides an effective layer of defence against these attacks by monitoring authentication logs and automatically blocking IP addresses that show malicious behaviour.
This guide covers how to install and configure Fail2Ban to protect SSH and web services on Ubuntu servers. It walks through the setup process, common configuration mistakes that reduce effectiveness, and the steps needed to harden the configuration for production use.
How Fail2Ban Works
Fail2Ban monitors log files for patterns that match known attack signatures. When a pattern is detected repeatedly, Fail2Ban updates the server firewall to block the offending IP address. The blocking is temporary by default but can be extended for persistent attackers.
The tool operates on a concept of jails. Each jail monitors a specific log file for specific patterns and applies an action when those patterns match. The default installation includes sensible jail configurations for SSH, Apache, and Nginx out of the box. You can enable, disable, or customise these jails based on your server setup.
When Fail2Ban identifies an IP that exceeds the failure threshold within the defined time window, it adds that IP to the firewall rules. Connections from the blocked IP are refused at the network level, preventing the attacker from consuming server resources or attempting further logins.
Installing Fail2Ban on Ubuntu
The installation process is straightforward on Ubuntu. Update your package list and install the package from the default repository.
apt update && apt install fail2ban
After installation, the Fail2Ban service starts automatically. You can verify the service is running with this command.
systemctl status fail2ban
If the service is not running, start it manually.
systemctl start fail2ban
Understanding the Configuration Structure
Fail2Ban stores its configuration in /etc/fail2ban/. The main configuration file is jail.conf, which contains default settings for all available jails. You should never edit this file directly because package updates can overwrite your changes.
Instead, create a jail.local file in the same directory. Settings in this file override the defaults in jail.conf. This approach keeps your customisations separate and ensures they survive system updates.
The jail.local file uses the same syntax as jail.conf. Here is a basic structure for overriding SSH jail settings.
[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
The settings control how Fail2Ban behaves. maxretry defines the number of failures allowed within the findtime window before a ban is issued. bantime specifies how long the ban lasts in seconds. The values above represent a reasonable starting point for SSH protection.
Configuring HTTP Protection for Nginx or Apache
Fail2Ban can also protect web servers against abuse, including aggressive scrapers, vulnerability scanners, and denial-of-service attempts. HTTP attacks typically happen faster than SSH attacks because a single machine can make hundreds of requests per minute without triggering account lockouts.
A normal user loading a web page generates fewer than 10 requests per minute. An automated script can easily exceed 100 requests in the same timeframe. Monitoring access logs for this behaviour lets you block malicious traffic before it affects server performance.
The findtime for HTTP bans should be shorter than for SSH because HTTP attacks unfold more quickly. A five-minute window and a threshold of 100 requests will catch aggressive scrapers and scripted attacks without affecting normal users who refresh pages frequently.
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 100
bantime = 600
findtime = 300
For Apache, replace the log path with the corresponding Apache access log location. Adjust the maxretry value based on your observed traffic patterns. Servers with API endpoints or heavy legitimate traffic may require higher thresholds to avoid blocking real users.
Application-level rate limiting can supplement Fail2Ban for protecting specific endpoints that require finer control than network-level blocking provides. This approach works particularly well for API services where you need to limit requests per user or per token rather than per IP address.
Enabling and Testing Your Jails
After creating your jail.local configuration, restart the Fail2Ban service to apply the changes.
systemctl restart fail2ban
List all configured jails and their status using the Fail2Ban client.
fail2ban-client status
This command shows which jails are active and how many IPs are currently banned. To check the status of a specific jail, such as the SSH jail, run this.
fail2ban-client status sshd
The output shows the total number of failures that triggered bans and lists the currently banned IP addresses. Reviewing this information regularly helps you understand what kind of traffic your server receives.
Setting Up Email Notifications
Configuring Fail2Ban to send email notifications confirms that the service is working and alerts you to active attacks. Without notifications, you may not realise Fail2Ban is blocking traffic unless you check the logs manually.
The default action for most jails bans the IP silently. To receive notifications, change the action to include whois information and log excerpts.
action = %(action_mwl)s
This configuration bans the offending IP and sends an email containing the whois record and relevant log lines. For this to work, your server must be able to send email. Configuring Postfix or another mail transfer agent ensures ban notifications reach your inbox rather than failing silently.
If your server does not have email configured, logs are still written to /var/log/fail2ban.log. You can monitor this file directly or set up log rotation to manage file sizes over time.
Common Configuration Mistakes That Reduce Effectiveness
Several configuration errors commonly reduce Fail2Ban's effectiveness or cause unexpected blocking of legitimate users.
- Setting maxretry too low: Legitimate users on unstable network connections may trigger multiple connection attempts, especially when an SSH client retries automatically. Setting
maxretryto at least 3 for SSH prevents accidental bans of real users. - Forgetting to whitelist your own IP: Accidentally banning yourself while testing or during normal work is frustrating. Add your office or home IP address to the
ignoreipdirective in yourjail.localfile to prevent self-blocks. - Incorrect log file paths: Fail2Ban silently takes no action if the log path in your jail configuration does not match the actual log file location. Verify paths carefully, especially if your distribution or web server uses non-standard logging locations.
- Editing jail.conf directly: Changes made to
jail.confare overwritten during package updates. Always usejail.localfor customisations to preserve settings across updates. - Ignoring ban repeat offenders: Some attackers cycle through IP addresses or wait for bans to expire before resuming. Persistent attackers should be added to a permanent block list at the firewall level rather than relying on Fail2Ban to re-ban them repeatedly.
Hardening Fail2Ban for Production Servers
The default Fail2Ban configuration is a useful starting point, but production servers benefit from more restrictive settings. Adjusting the thresholds based on your actual traffic patterns and security requirements strengthens your defence.
For SSH access exposed to the internet, set maxretry to 3 and bantime to at least 3600 seconds, which is one hour. For servers that experience repeated targeted attacks, consider extending the ban time to 86400 seconds, which is 24 hours, for repeat offenders. The findtime of 600 seconds (10 minutes) remains appropriate for most SSH configurations.
Use the Fail2Ban client to manually ban IP addresses that you observe scanning your server, even if they have not yet triggered an automatic ban.
fail2ban-client set sshd banip 192.0.2.100
Replace 192.0.2.100 with the IP address you want to block. After manually banning an IP, review your logs to understand why it was flagged and whether it represents a genuine threat or a misconfiguration.
Combine Fail2Ban with a properly configured firewall for layered protection. UFW (Uncomplicated Firewall) on Ubuntu works well alongside Fail2Ban, providing baseline firewall rules while Fail2Ban dynamically blocks IP addresses based on observed behaviour. Reviewing both configurations together gives you a clearer picture of your server's exposure.
Integrating Fail2Ban with Server Hardening Practices
Fail2Ban is most effective as part of a broader server hardening strategy rather than as a standalone solution. Combining it with SSH key authentication, secure SSH configuration, and regular system updates creates multiple layers of defence.
Disabling password authentication for SSH and using key-based login eliminates the primary attack vector that Fail2Ban protects against. Even with Fail2Ban in place, strong SSH configuration reduces your attack surface significantly. You should also restrict SSH access to specific IP addresses or ranges where possible, limiting who can attempt authentication at all.
Regularly reviewing /var/log/fail2ban.log helps identify patterns in attack traffic and refine your configuration over time. Look for IP ranges that appear repeatedly, scan schedules that suggest automated tools, and any new attack signatures that may require additional jail configurations.
When you identify persistent attackers, consider adding their IP ranges to a permanent block list in your firewall rather than relying on Fail2Ban to re-block them each time. This approach reduces processing overhead and ensures these IPs remain blocked even during Fail2Ban restarts or temporary outages.
When Fail2Ban Is Not Enough
Fail2Ban works well against attacks where a single IP address generates many requests. However, it cannot stop distributed attacks where each IP address makes only a small number of requests. In a distributed brute-force attack, thousands of different IP addresses might each attempt just one or two logins, staying below the maxretry threshold.
For protection against distributed attacks, consider cloud-based DDoS mitigation services that can absorb and filter malicious traffic before it reaches your server. These services operate at the network edge and can distinguish between legitimate traffic and attack traffic based on characteristics that individual server logs do not reveal.
Fail2Ban also cannot protect against attacks that exploit application vulnerabilities or credential stuffing where attackers use username and password combinations stolen from other services. Application-level security measures, proper input validation, and multi-factor authentication address these threat vectors more effectively.
Maintaining Your Fail2Ban Setup Over Time
Fail2Ban configuration requires occasional review as your server usage evolves. Traffic patterns change, new services are added, and attack techniques shift. A configuration that works well initially may need adjustment as your server grows.
When adding new services to your server, consider whether they expose new attack surfaces. A new web application, API endpoint, or management interface may benefit from its own Fail2Ban jail. Review the available jail templates and adapt them for your specific log formats and access patterns.
Keep your Fail2Ban installation updated through regular system package updates. New jail definitions and pattern matching improvements are included in updates that address emerging threats. Always test configuration changes in a staging environment before applying them to production servers.
Back up your jail.local file before performing major system upgrades. While the file should be preserved, having a backup ensures you can restore your configuration quickly if anything goes wrong during the upgrade process.