How To Protect Against Brute Force Logins With Fail2Ban

security December 6th, 2017

One of the most common attack vectors against servers is bruteforce login attempts. This is where attackers attempt to access your server, by trying endless combinations of usernames and passwords. So how do you defend yourself against this kind of attack?

If you're a web application developer, the most common response might be to refactor some aspect of your application. There's nothing wrong with this approach, but it’s likely not the right solution. This is because there are specifically-designed, open source applications for defending against this very type of attack.

In today's post, I'm going to introduce you to one, and show you how to use it; it’s called Fail2Ban. In the setup we’re going to walk through, you’ll see how to defend against brute force SSH logins.

What Is Fail2Ban?

Originally developed by Cyril Jaquier back in 2004, Fail2Ban is:

An intrusion prevention software framework that protects computer servers from brute-force attacks.

More specifically, quoting from the Fail2Ban website:

Fail2ban scans log files (e.g., /var/log/apache/error_log) and bans IPs that show malicious activity, such as too many password failures, and seeking for exploits, etc. Generally, Fail2Ban is then used to update firewall rules to reject the IP addresses for a specified amount of time, although any other arbitrary action (e.g., sending an email) could also be configured.

Now you might be asking, if your website's the target of malicious activity, why wouldn't you just ban the host permanently? Good question. And on the surface of it, that might sound like the logical thing to do.

But what if the seemingly malicious activity wasn't malicious. What if it was a genuine user — perhaps even you — who fat-fingered the keyboard, whether because of a lack of sleep, stress, or some other valid cause? What if the user just plain forgot their password?

Would you want them to be permanently locked out of their own server, with no way of logging back in? I'd suggest the answer would be a resounding no! That's why the ban isn't permanent, at least not by default.

What's more, assuming that the activity is genuinely malicious, ban's don't need to be permanent. Often, all you need to do is to discourage attackers from attacking you, not block them altogether. Discouraging them normally makes you a target not worth attacking, because it requires too much effort, or too much time.

To be fair, this isn't always the case, as you may be a particularly inviting target. But if you can make it a royal PITA to attack you, the majority of attackers are likely to just go elsewhere and find lower-hanging fruit. That's why Fail2Ban is an excellent tool for helping to protect your application from brute force login attempts.

Let's Install It.

For the purposes of this post, I'll assume that you're using a Ubuntu-based Linux server to host your website. The server that I used as the foundation for the information in this post was Ubuntu 14.04.5 LTS.

Yes, it's a little old now, but fundamentally you configure Fail2Ban the same way and use the same command-line tools in later releases.

If you're using a different distribution, adjust the commands and package names to suit. To install Fail2Ban, first update your Apt cache, and then install Fail2Ban as shown in the following example.

sudo apt-get update && sudo apt-get install -y fail2ban

When installed, you'll find Fail2Ban's configuration files in /etc/fail2ban. If you run ls -l on that directory, you should see output similar to the following:

drwxr-xr-x 2 root root  4096 Jan 24 16:09 action.d
-rw-r--r-- 1 root root  2328 Aug  1  2015 fail2ban.conf
drwxr-xr-x 2 root root  4096 Aug  2  2015 fail2ban.d
drwxr-xr-x 3 root root  4096 Jan 24 16:09 filter.d
-rw-r--r-- 1 root root 18562 Aug  1  2015 jail.conf
drwxr-xr-x 2 root root  4096 Jan 24 16:09 jail.d

Fail2Ban's configuration is split, primarily, across two key files; these are fail2ban.conf and jail.conf. fail2ban.conf is Fail2Ban's broader configuration file, where you can configure such settings as:

  • The logging level.
  • The file to log to.
  • The process socket file.
  • The pid file.

jail.conf is where you configure such options as:

  • The configuration for the services to defend.
  • How long to ban for should they be being attacked.
  • The email address to send reports to.
  • The action to take when an attack is detected.
  • A pre-defined set of jail configurations, such as SSH.

When setting up Fail2Ban, you should never alter jail.conf, as it's part of the core distribution and may be overwritten when you upgrade the package. Instead, copy it to a new file called jail.local, and make changes there instead.

What Is a Jail?

Before we dive into configuring Fail2Ban, we need to cover one key concept, and that’s a jail. You’ll hear the term as you dig through the Fail2Ban documentation and configuration files.

The term is pretty apt as, in a figurative sense, we want to jail offenders that try to maliciously access our servers. A jail is a configuration grouping or bucket. These can be configured both globally, in jail.conf, and in individual jail configuration files, stored in /etc/fail2ban/jail.d/. Local configuration directives take precedence over global configuration directives.

Regardless of where you store the configuration, in them you store all the details for a specific service that you want Fail2Ban to defend. They’re composed of two parts, the core configuration and a filter.

The Core Configuration

Here’s an example of the core configuration for ssh. What this does is check for malicious attempts to access the server on the default SSH port (22). It does this by parsing /var/log/auth.log looking for the pattern defined in the sshd filter config file. This filter file is stored in the filter.d directory, named: sshd.conf. Finally, if the pattern occurs six times, then it’s considered to be malicious activity.

[ssh]

enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 6

The Filter

There’s more to a filter file than I’m going to cover here. What I want to show you is the key configuration item: failregex. As Fail2Ban parses log files, it uses regular expressions to do so as efficiently as possible.

failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error) for .* from <HOST>( via \S+)?\s*$
            ^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
            ^%(__prefix_line)sFailed \S+ for .*? from <HOST>(?: port \d*)?(?: ssh\d*)?(: (ruser .*|(\S+ ID \S+ \(serial \d+\) CA )?\S+ %(__md5hex)s(, client user ".*", client host ".*")?))?\s*$
            ^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
            ^%(__prefix_line)s[iI](?:llegal|nvalid) user .* from <HOST>\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because not in any group\s*$
            ^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$

I know that regular expressions aren’t for everyone, and that this example may not mean a whole lot. So if you want to learn more about them, check out these two excellent sites:

  • https://www.regular-expressions.info/
  • https://regex101.com

Now Let's Configure Fail2Ban

Assuming that you've copied jail.conf to jail.local, open the new file in your editor of choice; I can’t help but use VIM. There are a number of key settings that you can configure to suit your particular needs; these are:

Setting Description
bantime This is the number of seconds that you want a malicious host to be banned for. By default, it's set to 600. If you want them banned longer, feel free to increase it. However, be careful. If you're attempting to login and get banned, you'll ban yourself for that long as well.
findtime (default: 600) The number of seconds to look for the failure pattern
maxretry (default: 3) The maximum number of times to look for the failure pattern
destemail The email address to send failure reports to. You could leave it set at its default value of root@localhost, but then you'd either have to SSH into the box, or run a remote SSH command to view the information. Having the information sent to an external email address is likely far more convenient.
sender The name of the account that sends the reports. Change it from its default value of root@localhost to something more meaningful.

After you've configured these settings, the next thing to do is to ensure action is set appropriately. This is the default action to take when an attack is detected. By default it's set to action_, which bans the IP address.

However, that doesn't help us a whole lot if we want to know what’s going on. So I suggest you change it to %(action_mwl)s. This will ban the IP and send an e-mail, along with a whois report, to the email address defined by destemail.

Here are all the available options that you can use.

Setting Description
action_ The simplest action to take: ban only
action_mw Ban & send an e-mail with whois report to the destemail.
action_mwl Ban & send an e-mail with whois report and relevant log lines to the destemail.
action_xarf Ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines to the destemail.
action_cf_mwl Ban IP on CloudFlare & send an e-mail with whois report and relevant log lines to the destemail.
action_blocklist_de Report block via blocklist.de fail2ban reporting service API
action_badips Report ban via badips.com, and use as blacklist

With the configuration set, we now need to check that the ssh jail is enabled, and configured, as in the example below.

[ssh]

enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 6

With that change made, you lastly need to reload Fail2Ban, by running service fail2ban reload, so that all the changes are enabled. When you do that, you should see the output below.

sudo service fail2ban reload
 * Reloading authentication failure monitor fail2ban

With that done, let's do a quick check, to see that Fail2Ban is running and that the ssh jail's active. To do that, we'll use fail2ban-client, as follows:

sudo fail2ban-client status
Status
|- Number of jail:  1
`- Jail list:       ssh

You can see that one jail is listed, and that it's SSH. So we're ready to test that our configuration will protect against malicious SSH logins.

Let's Test That It Works

To do this, attempt to login to your test server using ssh. Login with any username that comes to mind and supply any password. As the username and passwords are wrong, you'll be prompted to try again, twice. Do this, and then attempt to login again with another bogus username and password.

On the sixth attempt, the login attempt should hang, because it’s waiting for a timeout to occur. At this point, it's now time to use fail2ban-client, to check if our IP address has been banned. To do that, we'll use the status argument again, but this time specifying the ssh jail, as follows.

sudo fail2ban-client status ssh
Status for the jail: ssh
|- filter
|  |- File list:    /var/log/auth.log
|  |- Currently failed: 0
|  `- Total failed: 6
`- action
   |- Currently banned: 1
   |  `- IP list:   172.28.128.3
   `- Total banned: 1

You can see that there is one banned IP, which is our IP: 172.28.128.3. You should have received an email as well. So have a look at your email inbox.

via GIPHY

Unbanning an IP

Now that we've successfully banned an IP, what if we want to unban it? To do that, we can again use fail2ban-client, and tell it to unban a specific IP, as in the following example.

sudo fail2ban-client set ssh unbanip 172.28.128.3
172.28.128.3

After we do that, we need to check that it's been removed from the ban list, so we'll check the ssh jail's status again, as we did before. You can see in the following example that it's no longer banned.

sudo fail2ban-client status ssh
Status for the jail: ssh
|- filter
|  |- File list:    /var/log/auth.log
|  |- Currently failed: 0
|  `- Total failed: 6
`- action
   |- Currently banned: 0
   |  `- IP list:
   `- Total banned: 1

And That's a Wrap!

And that’s how to use Fail2Ban to protect your servers against malicious, brute force login attempts. That said, it’s only just scratched the surface of what Fail2Ban can do.

However, please be aware that Fail2Ban — especially with this minimalist configuration — doesn't provide 100% protection against attacks on your websites and servers. Yet, it's an excellent way to start! If nothing else, now you have far greater protection than you did before, and you're far more aware of just how many attacks are being made against your servers on an hour-by-hour basis.

If you like what you've learned so far, I strongly encourage you to keep learning. There's so much to uncover by reading them.

If you're experienced with Fail2Ban, I'd love to hear about your configurations and experience with it in the comments.

Tagged with: fail2ban / web application security / brute force protection / Debian / Linux / Systems Administration

Join the Email List

If you enjoyed this post, why not join the email list and get all future posts straight to your inbox? In addition, you'll get background information, extra research, and other content that's only available on the list. I promise I'll NEVER spam you. And you can unsubscribe at any time.