Fail2ban : Block WordPress brute force attacks

The Internet is a dangerous place so you might find yourself on the receiving end of a brute force attack. WordPress’ popularity virtually guarantees that will happen to your (WordPress) site, sooner or later.

My sites get hit at least once a week and I have clients who are constantly under attack.

Once you are getting brute forced, you could easily block the offender by IP address, with iptables, like this

iptables -A INPUT -s 111.222.111.222 -j DROP

That will work, however doing this manually every time gets quite tricky, next to impossible.

A good solution is to automate this, using fail2ban (there are other options of course, but outside of the scope of this article).

fail2ban monitors your log files and when it matches certain rules (like too many login attempts, scanning directories etc.) it can take action by creating firewall rules (like the one above). That will block the offending IP from accessing your server in any way (ftp, http, ssh, etc.).

Installing fail2ban

If you don’t already have fail2ban installed, you can do it easily from command line:

sudo apt-get install fail2ban

or if you are using CentOS (or similar flavour) , first add the repo

rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

Then install

yum install fail2ban

This is not a complete guide on installing fail2ban, but rather configuring a few rules specifically to protect a WordPress site.

Resources abound on the topic of installing fail2ban. Also, after setting up, make sure your install monitors your logs as they can vary depending on setup (in jail.local, in your rule files [filter.d directory]).
I run servers with individual logs per domain, shared logs and so on, so your mileage may vary. If you need some help setting it up, drop me a line.

Creating a fail2ban rule

After we have (if not already) installed fail2ban, we need to create rules (it comes with default ones too).
The rules are made up of regular expressions, called failregex that match the logs we are monitoring.

Protecting wp-login

Most of the attempts to brute force go to the wp-login page, so we are protecting this first.
The rule I have for this is saved in the file (on my setup) /etc/fail2ban/filter.d/wp-login.conf
It contains

# WP brute force attacks filter
 [Definition]
 failregex = ^<HOST> .* "POST .*wp-login.php
 ignoreregex =

This matches POST requests to wp-login.php
If there are 5 or more matches (my jail.local config is 5, defaults are found in jail.conf), it automatically blocks the offending IP.

Protecting xmlrpc.php attempts
This file is another source of trouble. Same as the wp-login rule, I create a new rule under /etc/fail2ban/filter.d/wp-xmlrpc.conf
It contains:

# WP brute force attacks filter
[Definition]
failregex = ^<HOST> .* "POST .*xmlrpc.php
ignoreregex =

As you can see, both rules are quite basic but do the job.

We created the rules, but we haven’t tested them. So let’s test before we enable. Testing uses the following syntax:

fail2ban-regex <logfile> <fail2ban rule to test>

For example:

fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/wp-login.conf

If there are any matches in the logfile, the test should output some matches and IP addresses.

We can clearly see the bots have been quite busy hammering this server. All quiet after adding the rule.

90146 hit(s): Day/MONTH/Year:Hour:Minute:Second
Success, the total number of match is 6532

I always test the rules before enabling them. Having rules that don’t work is not very useful.

After creating and testing these new rules, we need to enforce them.

Edit /etc/fail2ban/jail.local (if file doesn’t exist, create it. Do not do your local changes in jail.conf) and add the following:

[wp-login]
enabled = true
port = http,https
action = iptables-multiport[name=WP, port=http, protocol=tcp]
filter = wp-login
logpath = /var/log/apache2/access.log
maxretry = 5

[wp-xmlrpc]
enabled = true
port = http,https
action = iptables-multiport[name=WP-xmlrpc, port=http, protocol=tcp]
filter = wp-xmlrpc
logpath = /var/log/apache2/access.log
maxretry = 5
bantime=3600

What we are doing: enabling rules, specifying which logs to check and what action to take. If we do not specify things like bantime,
that setting will default to what ever your default it. As you can see, for one I did specify, for the other one, I left to default.

Ok then, so we have created and enabled our rules so now we need to restart fail2ban (fingers crossed).

Restart:

service fail2ban restart

Then let’s check it is now runing

service fail2ban status

I tend to do this, just to double check there wasn’t a silent fail start.

Checking rules work

With the fail2ban rule now active, login attempts to wp-login.php should now get blocked in the iptables firewall. Same for attempts
on xmlrpc.php

You can check the list of blocked ip adresses in iptables with this command:

iptables -L

What if fail2ban doesn’t start

Sometimes it happens that we misconfigured something. We will see no error logs when starting (and failing) so we need to diagnose:

fail2ban-client -x start

This will show us logs of what’s happening. Also we can use this to deal with any warnings, though not necessary.

Hopefully this has given you some extra information on dealing with all the nasty people out there wanting your site logins…