Brute-force attacks against SSH frequently target internet facing Linux systems, such as Debian, Ubuntu, Arch Linux, Linux Mint, Kali Linux, Raspberry Pi OS (Raspbian), Gentoo, etc. Fail2ban resolves this by monitoring authentication logs and dynamically updating firewall rules to block IP addresses that show malicious behavior such as repeated failed login attempts.
This article covers configuring custom jail parameters and applying the rules to protect SSH.
When does Fail2ban actually improve SSH security?
The real value of Fail2ban depends on the authentication model. If a server allows password authentication, Fail2ban reduces exposure to automated brute-force attacks by limiting how many authentication attempts an attacker can make from a given IP address.
If a server enforces key-only authentication (highly recommended) and disables root logins, password guessing is no longer an effective attack method. Even so, public-key authentication does not prevent resource exhaustion. Botnets frequently scan exposed SSH ports, and even with password authentication disabled, the SSH server still has to accept incoming TCP connections and process the initial SSH protocol handshake before rejecting the attempt.
Fail2ban addresses this by dynamically updating firewall rules to block any IP address that exceeds a configured threshold of failed attempts. Blocking repeat offenders at the network layer reduces the CPU load of handling malicious SSH handshakes and keeps authentication logs clean. It also generates explicit ban events, providing an audit trail of abusive IP addresses without cluttering logs with background noise.
Installing packages
The initial step requires installing the fail2ban, the daemon responsible for parsing logs and managing bans.
On Debian-based systems, fail2ban can be installed with:
apt-get install fail2ban
For other distributions, install fail2ban using the package manager.
Configuring jail.local
The fail2ban package ships with /etc/fail2ban/jail.conf by default. Do not edit this file directly. Instead, create /etc/fail2ban/jail.local. Using jail.local ensures package upgrades do not overwrite your configurations. (Fail2ban reads .conf files first, followed by .local files, which override the default values.)
Create the /etc/fail2ban/jail.local file with the following content:
[DEFAULT]
# Uncomment to use journalctl to find SSH authentication attempts
# backend = systemd
# The duration a host is banned (supports seconds or abbreviations like 1h)
bantime = 1h
# Number of failures (e.g., failed SSH login attempts) before a host is banned
maxretry = 5
# A host is banned if it has generated 'maxretry' attempts during the last 'findtime'
findtime = 10m
# Never ban these ip addresses
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = ssh
filter = sshd
To prevent accidental lockouts, add trusted IP addresses, CIDR ranges, or hostnames to ignoreip. Fail2ban skips rule enforcement for these addresses. Separate multiple entries with spaces (e.g., ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.40). Always include management workstations, VPN gateways, and jump hosts before enforcing bans.
Managing repeat offenders with the recidive jail
Configuring the recidive jail handles persistent attackers who return after their initial ban expires by monitoring the Fail2ban log itself and applying longer, stricter bans to IP addresses repeatedly banned by other jails.
Add the following to /etc/fail2ban/jail.local to extend the ban for persistent abusers:
[recidive]
enabled = true
# Monitor the fail2ban log file for repeated bans
backend = polling
logpath = /var/log/fail2ban.log
banaction = %(banaction_allports)s
# Ban repeat offenders for 1 week
bantime = 1w
# Look for repeat bans over the last 1 day
findtime = 1d
# Trigger after 5 previous bans
maxretry = 5
Applying the configuration
After modifying the /etc/fail2ban/jail.local file, restart the daemon to apply the new rules:
systemctl restart fail2ban
Verifying the status
This reports the status of the sshd jail, including the current number of banned IP addresses:
fail2ban-client status sshd
How to unblock an IP address?
To unblock an IP address managed by Fail2bans ssh jail, use:
fail2ban-client set sshd unbanip 8.8.8.8
Alternatively, you can use the following to remove an IP address from all active jails simultaneously:
fail2ban-client unban 8.8.8.8
Beyond Fail2ban
While Fail2ban reduces automated SSH brute-force attacks, it is reactive and insufficient as a standalone security strategy. Since Fail2ban reacts to events after they have been recorded by the monitored service or log source, the underlying attack surface remains exposed to initial probes and attackers that rotate IP addresses or stay below the configured thresholds.
Rather than treating Fail2ban as a complete solution, It is recommended to implement a layered defense architecture that includes:
- Moving the actual SSH daemon off public-facing interfaces,
- enforcing key-only authentication,
- disabling root logins,
- setting up a default-deny firewall,
- implementing multi-factor authentication,
- using a VPN,
- hardening cryptographic algorithms,
- deploying a decoy honeypot (e.g., Cowrie),
- and configuring
sshdto listen on a non-standard port, such as 22000 instead of the default 22. (This is another method to reduce automated scanning. Many botnets are programmed to look exclusively for open port 22. While changing the port is security by obscurity and will not defeat a dedicated attacker, it drops the volume of opportunistic background traffic hitting your server.)