How To Protect Against Brute Force Logins With Fail2Ban

How To Protect Against Brute Force Logins With Fail2Ban

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.


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:

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 fail2ban reporting service API
action_badips Report ban via, 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.


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
|- 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:
   `- Total banned:	1

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


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

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.

You might also be interested in these tutorials too...

How Do You Start In The Tech Sector?
Thu, Aug 9, 2018

How Do You Start In The Tech Sector?

The tech sector, if you know what you’re doing, is easier than most fields to get started in. However, you do have to know what you’re doing. In this post, I’m going to step through a series of ways to get started, in case you’re not sure.

Google Chrome 69 Now Flags HTTP Websites
Mon, Sep 10, 2018

Google Chrome 69 Now Flags HTTP Websites

Earlier today, I upgraded my installation of Google Chrome from version 68 to version 69. While not a major upgrade, there’s a key security update that I want to draw your attention to. Here’s a quick look at it.

Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus