Fail2ban works by filtering a log file with a regular expression triggering a
ban action if the condition is met. After a preset time, it will trigger an
unban action. Without much effort, we can have WordPress log all authentication events and have fail2ban react on them.
Note: This guide is written for Ubuntu, but most of it should apply to other distributions as well.
It doesn’t get much easier than this:
$ apt-get update && apt-get install fail2ban
Now, fail2ban works right away detecting any failed login attempts on SSH. The default settings will make 5 failed login attempts within 600 seconds to cause an IP ban via the iptables firewall for 600 seconds.
Logging Authentication Events
So, first of all we will make WordPress log all authentication events to the system authentication log file (the authlog, on Ubunutu found in
/var/log/auth.log). You might write your own plugin, but the WP fail2ban plugin does exactly this and have a few very useful features. I usually just download the single PHP file in the plugin into my
mu-plugins directory, but you may also install it as a regular plugin. Without any more fuzz, WordPress will now log all authentication events.
Note that if you are behind a reverse proxy, it is extremely important that you set the
WP_FAIL2BAN_PROXIES constant. Otherwise you will end up banning your proxy, blocking all incoming requests from everybody. Read the FAQ for info on this.
Setting up the Filter and Jail
First we have to set up the filter. It is really important that the regexes in the filter matches the log entry format from WordPress. Luckily a filter is also provided with the WP fail2ban plugin, so we can just download that:
$ sudo curl https://plugins.svn.wordpress.org/wp-fail2ban/trunk/filters.d/wordpress-hard.conf > /etc/fail2ban/filter.d/wordpress.conf
Feel free to inspect the file so you understand what it does.
(Oh, and btw: Since I first wrote this post, the plugin also includes a “soft” filter. You should check it out.)
Now, we’ll set up the jail. Create the file
/etc/fail2ban/jail.d/wordpress.conf with the following content:
enabled = true
filter = wordpress
logpath = /var/log/auth.log
port = http,https
Restart fail2ban and you should be all set:
$ service fail2ban restart
To make sure it is working, try a few bogus logins to WordPress. You should see entries like this appended to your
Nov 7 18:58:27 localusername wordpress(example.com): Authentication failure for footer from 22.214.171.124
Do that more that 5 times, and you should see something like this in your /var/log/fail2ban.log file:
2014-11-07 19:01:35,640 fail2ban.actions: WARNING [wordpress] Ban 126.96.36.199
All connections from 188.8.131.52 should now be stopped in iptables. Note that the default
iptables-multiport action won’t drop already established TCP connections, so you might still receive requests from your test browser if keepalive is enabled. If you want to make sure all established connections to the offending IP is killed, you have to create your own custom action and include a tool like
tcpkill or simply use the provided
Customizing the Jail
/etc/fail2ban/jail.conf to see what the default settings are, but ever edit this file directly. Do your changes in the
jail.local file (you’ll have to create it) or files in the
jail.d folder like we did above.
The most important options that you might want to tinker with are these, which you should add to your
maxretry = 3
findtime = 10800
bantime = 86400
Tune these settings to your liking. What my suggestion here does is: If 3 failed login attempts from the same IP is found within 10800 seconds (3 hours), the IP will be banned for 86400 seconds (24 hours).
Optional: Changing the Default Action
The default action is
iptables-multiport. I like to use the
route action instead. It is extremely fast both for a large number of IPs and since it blocks traffic even before it hits iptables. It also stops any traffic on already established connections immediately.
To change the default action for your WordPress jail, find one you like in
/etc/fail2ban/action.d – like
route and add the following to the
action = route
To inspect what bans are in place with route, issue the following command:
$ ip route list | grep unreachable
Optional: Custom Action: Using UFW
I like to keep things simple, so I like to use UFW (Uncomplicated FireWall) instead of iptables directly.
Since it is quite easy to create your own custom actions, setting up an action that use UFW is really easy. Just drop the following in
actionban = ufw insert 1 deny from <ip>
actionunban = ufw delete deny from <ip>
I don’t think it needs an explanation. Now you can use
action = ufw in your jail.
Important Note on Reverse Proxies
If you’re using a reverse proxy, but it is on a different host than your WordPress installation – like a load balancer, banning on your WP host isn’t going to do anything, since all the traffic is coming from your reverse proxy. In that case, you will have to set up a custom action (like the previous chapter with UFW), using an API so you can ban the offending IP on the host where your reverse proxy is.
If you happen to be using a Rackspace Cloud LoadBalancer, I have the recipe for you:
Using fail2ban from behind a Rackspace Cloud LoadBalancer
If you’re using something else, you can still use it as a starting point, but you’ll have to provide your own API wrapper.
No IPv6 support, but it’s coming. A patch with experimental support is available.
… there’s more
Now, if you haven’t already, go take a look at the FAQ for the WP fail2ban plugin. Because the plugin isn’ t restricted to logging successful and unsuccessful login attempts.
It can also:
- log pingbacks
- change the logfile
- block user enumeration attempts
- shortcut the login process if a specified username is provided
So go check it out now.