Protecting SSH with Fail2ban

SSH bruteforce log

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.

Installing Fail2ban

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

Now, create /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

Here:

  • maxretry controls the maximum number of allowed retries.
  • findtime specifies the time window (in seconds) which should be considered for banning an IP. (43200 seconds is 12 hours)
  • bantime specifies 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 actionstart and actionstop lets you specify initialization commands that will be run when Fail2ban starts up and shuts down. actionban and 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 /etc/fail2ban/action.d/iptables-multiport.conf file.

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 maxretry and 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 sshlongterm jail. 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.

Conclusion

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.

If you liked this post, please share it 🙂

You may also like...