Fail2ban: Setup, Configure, Customize, Test

Hello world! There are a number of tools that I find absolutely indispensable when working on system administration tasks. Some of these tools assist with securing/hardening web servers to prevent DDoS or other types of hacks. I’m going to be delving deeper into Fail2ban and how it can be setup and configured to help with just about any type of issue you and your server may be facing.

Firstly, I’m a huge proponent of Ubuntu and their server operating systems, as you’ve probably seen from my other blogs, so I’ll be assuming you’re operating on the same. Those of you operating on different versions of Linux, you’ll likely recognize when to use another command. To those of you operating on Windows servers, you have my condolences (just kidding ;)).

The Basics

This part of the process is simple enough. Update and upgrade your installation of Ubuntu, for sanity’s sake. Install Fail2ban.

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

Configuring Fail2ban

We’ll be operating in the /etc/fail2ban/ folder for the most part. Though, later on, we’ll be referring to log files to give Fail2ban more information.

If you open the jail.conf file, you’ll notice pretty early on that they recommend you not touch that file. It’ll be overwritten quite commonly, so you should enter all of your configurations in a new jail.local file.

sudo vim jail.local

Enter the following informaton into this file to override defaults set in the jail.conf file.

ignoreip = ::1
bantime = 3600
findtime = 600
maxretry = 5
enabled = true

This will set the default ban time to 3600 seconds (how long they’re locked out), the find time to 600 seconds (how long of a period to search for instances to ban), and maxretry to 5 (the number of times before triggering the rule. You may choose to increase or decrease these rules based on your own preferences. Be careful when using -1 bantime, as it is a permanent ban which can lock you out of your server. If you’re a responsible adult and are using ssh keys to log in, you should be fine. Always have a backup entryway in case of failures.

Setting [sshd] enabled = true enables the basic sshd rule to ban anyone failing to access ssh five times within 600 seconds. This will ban the offending individual for 3600 seconds. This is often more than enough time to prevent your server from being overloaded with false requests.

Creating Custom Rules

As you may have noticed from my other blogs, I’m often running open-source web applications on my servers. This means that I’m often open to common attacks and DDoS techniques used by script kiddies across the globe.

Looking through your web-server logs, you may notice many different types of attacks. You can create rules for these attacks using regular expressions in combination with Fail2ban rules.

I’ve previously written about XML-RPC attacks against WordPress and how to prevent them, but I’ll be outlining another option here using fail2ban, which may actually be a more elegant solution, though potentially not as secure.

Firstly, new rules should be placed in the /etc/fail2ban/filter.d/ folder, which you’ll notice has a number of already configured rules you may want to enable in your /etc/fail2ban/jail.local file.

Create a new file called wordpress.conf in the filter.d folder:

vim /etc/fail2ban/filter.d/wordpress.conf

I run Apache2 as my web-server of choice, so my log files are located in /var/log/apache2/ and specifically the newest access logs would be in access.log, though you can see an archive of older logs in the numbered files. Two specific lines that cause me grief are the following:

XX.XXX.XXX.XXX - - [13/Jun/2019:16:07:58 -0400] "POST /wp-login.php HTTP/1.1" 200 5382 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
XX.XXX.XXX.XXX - - [13/Jun/2019:16:07:58 -0400] "POST /xmlrpc.php HTTP/1.1" 200 3588 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"

The first bit of the line item is the IP address (redacted), then we have a couple dashes and the date. Following, we have the type of request, the URI, the type, and the HTTP code type response. In the case 200 – OK. That’s all we really need to create our regular expressions, as this is all the common information we need. You’ll see numerous requests like this one coming in on a daily basis.

Add the following configuration to the newly created wordpress.conf file:

failregex = <HOST>.*POST.*(wp-login\.php|xmlrpc\.php).* 200

As you can see, we specify where we would find the host in the regular expression. Then we specify that it is followed by any characters leading up to POST. Then we are looking for any characters leading up to wp-login.php or xmlrpc.php. Following that, any characters leading up to the 200 code.

This regex calls out two different attacks. Firstly, POST calls to wp-login.php with a 200 error are failures, as a successful login will return a redirect. Secondly, and similarly, POST calls to xmlrpc.php are treated the same way.

Once we’ve saved this configuration file, we’ll need to enable it and set some configuration parameters in our jail.local file. This is what our file should look like after the addition:

ignoreip = ::1
bantime = 3600
findtime = 600
maxretry = 5
enabled = true
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/apache2/access.log
maxretry = 5
bantime = 3600

As you can see, we specify that our requests may be coming over the http or https ports. They should be filtered based on the wordpress.conf filter (excluding the .conf). The rule should find and filter bad actors based on our log file which would be /var/log/apache2/access.log or /var/log/nginx/access.log/ or something similar based on your own configurations.

Testing Your New Configuration

Fail2ban gives us a number of commands that we can use to test our setup.

You can check if your server is running with: sudo fail2ban-client status
You can reload your fail2ban configuration with: sudo fail2ban-client reload
You can even see all commands just by running: sudo fail2ban-client

However, we’re looking to see how effective our fail2ban configuration is at spotting issues in our existing logs. We’ll run fail2ban-regex specifying our access.log input file, and our filter.d/wordpress.conf custom configuration.

sudo fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/wordpress.conf

For me, it takes a moment and then spits out the following:

Running tests

Use   failregex filter file : wordpress, basedir: /etc/fail2ban
Use         log file : /var/log/apache2/access.log
Use         encoding : UTF-8


Failregex: 4584 total
|-  #) [# of hits] regular expression
|   1) [4584] <HOST>.*POST.*(wp-login\.php|xmlrpc\.php).* 200

Ignoreregex: 0 total

Date template hits:
|- [# of hits] date format
|  [13957] Day(?P<_sep>[-/])MON(?P=_sep)Year[ :]?24hour:Minute:Second(?:\.Microseconds)?(?: Zone offset)?

Lines: 13957 lines, 0 ignored, 4584 matched, 9373 missed [processed in 4.45 sec]
Missed line(s): too many to print.  Use --print-all-missed to print all 9373 lines

As you can see, we successfully run our configuration failregex against the log retrieving 4584 offending lines. It then lets me know that using the “Date template hint” I could have found 13957 lines. Since this is a log file where every single line includes the date, you can see that it coincides with the number of lines at the bottom, and is effectively useless data. At the very bottom, we see that out of 13957 lines 0 were ignored and 4584 were matched. We don’t need to print or process the other lines, as they should have been legal access to our web-server.

At this point, we can look through additional lines in our access files for patterns in access, and see what we can limit through Fail2ban custom rules. You could even spend some time looking through the additional premade rules to see if any apply to you and enable them in your base configuration file – after all, script kiddies use some pretty common attack vectors.

Happy banning!