Limiting failed ssh login attempts with fail2ban

Bartłomiej Patrzyk
Software Mansion
Published in
5 min readMar 22, 2019

--

SSH is quite secure, especially if you take reasonable precautions, such as requiring key pair based authentication. However, there are still a lot of bots out there in the wild that tries to find vulnerable hosts by attempting to log in with common compromised usernames and passwords such as root/root or admin/admin. While it is unlikely they will succeed, they will still consume your bandwidth and generate massive amounts of logs.

One approach to minimize the number of brute-force login attempts is to change the default port SSH listens on. However, it’s not considered good practice — first of all, one has to remember to set a proper non-default port each time they connect to the server. Moreover, it might create another security vulnerability if the chosen port is greater than 1024. Typically, only root can bind to port numbers lower than 1024. However, if a higher port number is used for SSH, under certain circumstances users with no root access can substitute SSH daemon with another, possibly malicious, service.

A better way to solve the problem at hand is to use a tool that will block the attacker from accessing SSH server. One of such widely-adopted tools is fail2ban (https://www.fail2ban.org). By analyzing logs, fail2ban discovers repeated failed authentication attempts and automatically sets firewall rules to drop traffic originating from the offender’s IP address.

Installing fail2ban on Ubuntu

Manual installation

Installing fail2ban on Ubuntu (and other Debian-based distributions) is straightforward:

$ sudo apt install fail2ban

That’s it! fail2ban should be up and running with the default configuration, and it should be configured to start automatically on system startup.

Installing with Ansible

On Debian-based distributions you can install fail2ban with Ansible by adding apt task in the playbook file:

tasks:
- name: install fail2ban
apt:
name: fail2ban
state: present
update_cache: yes

Checking if it works

You can check if the service is up with the following command:

$ sudo systemctl status fail2ban

The output should be similar to the following — the service status should be active:

fail2ban.service - Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2019-02-24 10:21:18 UTC; 2h 42min ago
(... omitted for brevity ...)

Let’s see how did fail2ban alter iptables rules:

$ sudo iptables -L -n -v

You should also see that there is a new chain f2b-sshd in iptables config that is referenced in the INPUT chain rule:

Chain INPUT (policy ACCEPT 777 packets, 80681 bytes)pkts bytes target     prot opt in     out     source               destination
1250 93157 f2b-sshd tcp -- * * 0.0.0.0/0 0.0.0.0/0 multiport dports 22
(... omitted for brevity ...)Chain f2b-sshd (1 references)
pkts bytes target prot opt in out source destination
1223 90505 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0

fail2ban package contains a tool called fail2ban-client. It allows you to check the status of the service and interact with it (e.g., it lets you manually ban and unban IP addresses, enable and disable jails, etc.)

Let’s see which jails are active:

$ sudo fail2ban-client statusStatus
|- Number of jail: 1
`- Jail list: sshd

There is only a single jail — sshd — which is responsible for monitoring SSH server logs for failed login event and setting firewall rules to block further attempts.

Now we can check the statistics for sshd jail:

$ sudo fail2ban-client status sshdStatus for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:

As we can see, there were no failed logins so far. Now let’s try to log in several times with incorrect credentials. The jail status is now as follows:

Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 5
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 192.168.33.1

192.168.33.1 IP address is banned from accessing SSH server. fail2ban does this by adding an entry in f2b-sshd iptables chain:

Chain f2b-sshd (1 references)
pkts bytes target prot opt in out source destination
12 696 REJECT all -- * * 192.168.33.1 0.0.0.0/0 reject-with icmp-port-unreachable
1279 97855 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0

Configuring fail2ban

The default configuration should be sufficient in most cases. However, it’s good to understand what the defaults are and how you can adjust them to your needs.

With the standard configuration fail2ban will protect SSH server and will block the malicious party for 10 minutes after 5 failed login attempts within 10 minutes timeframe. The default config file can be found at /etc/fail2ban/jail.conf. The file is well documented and mostly self-explanatory. Keep in mind that you should not make any changes to that file as it might be overwritten during fail2ban upgrade.

In case you need to adjust the configuration, create /etc/fail2ban/jail.local config file with the desired changes. It is advised not to add default values to that file, but only the values you want to customize.

For example, if you want to change the default ban duration (bantime) and the number of failed attempts (maxretry) you should add the following config to /etc/fail2ban/jail.local:

[sshd]
#Set ban time to 5 minutes
bantime = 300
#Decrease the number of failed login attempts before banning to 3
maxretry=3

After changing the configuration remember to restart the service:

$ sudo systemctl restart fail2ban

For more config options see comments in /etc/fail2ban/jail.conf or read the manual (man jail.conf).

Using fail2ban with ufw

ufw (Uncomplicated Firewall) is another tool for managing firewall that has recently became a standard across different Linux distributions. With the default configuration fail2ban uses iptables to block traffic; however, it is also possible to configure fail2ban to use ufw to manage rules.

It requires adding banaction=ufw entry in /etc/fail2ban/jail.local config file.

It can be set either globally (for all jails) in the DEFAULT section:

[DEFAULT]
banaction=ufw

Or for a specific jail:

[sshd]
banaction=ufw

Configuration with Ansible

The simplest way to configure fail2ban with Ansible is to upload jail configuration file (jail.local) using the template task:

- name: upload fail2ban configuration
template:
src: jail.local
dest: /etc/fail2ban/jail.local
- name: restart fail2ban
service:
name: fail2ban
state: restarted

Service restart is required for the changes to take effect.

Conclusion

Fail2ban is a reasonable countermeasure against brute-force attacks targeting SSH (as well as other services that are out of the scope of this tutorial). It is easy to install, and in most cases, it doesn’t require additional configuration. We highly recommend to use it on all Internet-facing servers.

(Background vector created by freepik — www.freepik.com)

--

--