SSH rate limiting isn’t just about stopping brute-force attacks; it’s fundamentally about preventing denial-of-service conditions by limiting the rate at which any client can attempt to connect, regardless of intent.
Let’s see this in action. Imagine you have a server, sshd.example.com, and you want to limit new SSH connections from any single IP address to a maximum of 5 per minute.
# On the server where sshd is running:
sudo apt update && sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Now, we need to configure fail2ban to watch SSH login attempts. Create a new configuration file that overrides the defaults:
sudo nano /etc/fail2ban/jail.d/sshd-rate-limit.local
Paste the following into the file:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
findtime = 60
bantime = 300
action = %(action_mwl)s
Here’s what each part does:
[sshd]: This section targets SSH daemon logs.enabled = true: Activates this jail.port = ssh: Specifies the service name, whichfail2banmaps to port 22 by default.filter = sshd: Tellsfail2banto use the built-insshdfilter to parse log entries.logpath = /var/log/auth.log: The path to the authentication log file on most Debian/Ubuntu systems. (On RHEL/CentOS, this is typically/var/log/secure).maxretry = 5: This is the core of rate limiting. After 5 failed login attempts.findtime = 60: …within a 60-second window.bantime = 300: …the IP address will be banned for 300 seconds (5 minutes).action = %(action_mwl)s: This defines what to do when an IP is banned.action_mwlmeans "monitor, with whois and mail", which typically involves banning the IP in the firewall and sending an email notification with the details.
Save the file and restart fail2ban for the changes to take effect:
sudo systemctl restart fail2ban
You can verify the status of your jails with:
sudo fail2ban-client status
And check the status of the sshd jail specifically:
sudo fail2ban-client status sshd
This will show you how many IPs are currently banned and in the fail2ban log (/var/log/fail2ban.log), you’ll see the actions being taken.
The mental model is that fail2ban continuously scans specified log files for patterns matching failed login attempts. When the configured maxretry count is reached within the findtime window from a single IP, fail2ban executes the defined action, which usually involves modifying firewall rules to block further connections from that IP for the duration of bantime. It’s a reactive system, but with precise thresholds for reaction.
The findtime parameter is crucial for setting the rate. If maxretry is 5 and findtime is 60, you’re effectively limiting connections to 5 per minute. If you set findtime to 3600 (an hour), you’re allowing up to 5 attempts within an hour, which is much less restrictive.
A common pitfall is assuming maxretry alone controls the rate. It’s the combination of maxretry and findtime that defines the rate limit. The bantime dictates how long an IP is silenced, and longer ban times can be more effective for persistent attackers but also more disruptive if an innocent user gets locked out.
If you’ve set up fail2ban correctly and are still seeing SSH login attempts exceeding your configured rate, the next problem you’ll encounter is that fail2ban isn’t actually applying the firewall rules, often due to incorrect banaction configuration or missing firewall dependencies.