SSH allows you to log in to a remote computer or server and control it through a command-line interface. However, because SSH is exposed to the internet; attackers can try to log in by trying various username and password combinations.
A good way to protect SSH would be to ban an IP address from logging in if there are too many failed login attempts. You can use a package called “fail2ban” for this purpose, and it works with minimal configuration. In addition, you can even configure Fail2ban to protect other applications, like web servers.
We’ll cover how to protect SSH with Fail2ban in this post. These instructions should work on modern versions of Ubuntu (16.04 and later), Debian and CentOS 7.
On Debian and Ubuntu, you can install Fail2ban with:
sudo apt update sudo apt install fail2ban
On CentOS, you should first enable the EPEL repository and install Fail2ban.
sudo yum -y install epel-release sudo yum -y install fail2ban
/etc/fail2ban/jail.local with a text editor such as nano:
sudo nano /etc/fail2ban/jail.local
In the text editor, type in the following text:
[sshd] enabled = true banaction = iptables-multiport
Save the file contents and exit the text editor. Then, enable the Fail2ban service and start it up with:
sudo systemctl enable fail2ban sudo systemctl restart fail2ban
That’s it! With this minimal configuration, Fail2ban will block an IP for 10 minutes if it notices five failed logins occurring in a 10-minute period.
The basics of Fail2ban
So how does that work? Put simply, Fail2ban is a daemon that monitors logs and takes actions based on their contents. It is driven by three types of configuration:
- Filters specify certain patterns of text that Fail2ban should recognize in log files.
- Actions are things Fail2ban can do.
- Jails tell Fail2ban to match a filter on some logs. When the number of matches goes beyond a certain limit specified in the jail, Fail2ban takes an action specified in the jail.
Fail2ban comes with a jail instructing it to look at system logs and take actions against attacks on SSH. The default action (which is discussed later in detail) adds iptables rules to block out attackers.
In the previous section, we set the “enabled” setting of the “sshd” jail to true, so that Fail2ban can block these attacks. In the next section, we will see how you can configure some aspects of Fail2ban.
Configuring the jail
While the defaults will suffice for most, sometimes you may need to change things so that it suits your needs better.
Say, you want to block IP addresses for a day, if they have tried to make 10 login attempts within 12 hours. In order to do this, open the
/etc/fail2ban/jail.local file. Previously, we had set the “enabled” setting in this file. Now, we will add the following settings to the end of the file:
maxretry = 10 findtime = 43200 bantime = 86400
The file would end up looking like this:
[sshd] enabled = true banaction = iptables-multiport maxretry = 10 findtime = 43200 bantime = 86400
maxretrycontrols the maximum number of allowed retries.
findtimespecifies the time window (in seconds) which should be considered for banning an IP. (43200 seconds is 12 hours)
bantimespecifies the time window (in seconds) for which the IP address will be banned (86400 seconds is 24 hours).
If you have moved SSH to a different port, you can also specify the port number with
port = <port_number> in this file.
Restart Fail2ban for these changes to come into effect with the following command:
sudo systemctl restart fail2ban
Configuring the action
In the “Installing Fail2ban” section, we set up Fail2ban to use the “iptables-multiport” action. By default, it rejects packets with a “port unreachable” message. If you want to drop packets instead of rejecting them, create
/etc/fail2ban/action.d/iptables-common.local and type in the following:
[Init] blocktype = DROP
If you want to take an entirely custom action, you have to first define an action file in
/etc/fail2ban/action.d/my-action.local. (You can name the file anything you want, as long as it has the
.local extension.) A typical action file looks like this:
[Definition] actionstart = ... actionstop = ... actioncheck = ... actionban = ... actionunban = ...
As their names suggest, the
actionstop lets you specify initialization commands that will be run when Fail2ban starts up and shuts down.
actionunban specify the commands which will be run when Fail2ban wants to block an IP. In addition, Fail2ban may also need to verify whether an IP is already blocked, and the command specified in
actioncheck helps it to do so.
To see a practical example of this, have a look at the
When specifying these commands, you can use the
<ip> placeholder to get the IP address of the attacker.
After you’ve written your action file, in the SSH jail defined in
/etc/fail2ban/jail.local, you can set the action by setting the following:
banaction = my-action
Blocking repeat offenders with multiple jails
As a server administrator, you may have noticed that there are some IPs that keep brute forcing over and over, despite the bans. You could work around this by setting a low value of
findtime and a high value of
bantime, but this risks locking out legitimate users. Fortunately, you can block these attacks by setting up multiple jails.
For our example, we’re going to define a jail named
sshlongterm. It will block an IP for a week if there are 35 failed attempts login attempts over 3 days.
However, before we get started, we need the configuration of the default jail. Open the
/etc/fail2ban/jail.conf and look for a line containing only
[sshd]. The default configuration is mentioned below this line. On Ubuntu 16.04, the default configuration looks like this:
[sshd] port = ssh logpath = %(sshd_log)s
Copy these rules over to
/etc/fail2ban/jail.local, under the definition of the
sshlongtermjail. The file should end up looking like this:
[sshd] ; rules for the default SSH jail [sshlongterm] port = ssh logpath = %(sshd_log)s
Now, you need to set up the primary rules for the jail. This involves setting up the filter type, as well as the values for maxretry, findtime and bantime. Add the following lines:
banaction = iptables-multiport maxretry = 35 findtime = 259200 bantime = 608400 enabled = true filter = sshd
These settings allow up to 35 failed logins over 3 days before blocking the IP for a week.
The complete jail would end up looking like this:
[sshlongterm] port = ssh logpath = %(sshd_log)s banaction = iptables-multiport maxretry = 35 findtime = 259200 bantime = 608400 enabled = true filter = sshd
Restart fail2ban for these changes to take effect.
The default jail as well as the
sshlongterm jail should now work in conjunction. Short term attacks will be handled by the default jail, and the long term attacks handled by our own jail.
As we’ve seen, Fail2ban protects SSH right away. With a little bit of configuration, it can make massive brute force attacks a trivial problem.