Fail2ban WordPress Nginx


I’ve been using the Limit Login Attempts plugin for WordPress for quite a while. It basically logs failed login attempts and automatically blocks multiple attempts from a single IP address. A few days ago I’ve switched to fail2ban instead, which is pretty new to me.

Fail2ban is a fairly simple yet very flexible framework that monitors log files for certain patterns, and runs preconfigured actions upon certain events.

Out of the box fail2ban comes with many so called filters, which are sets of matching rules, for example SSH auth failure, vsftpd login failure and more. As well as predefined actions, like block the IP address via iptables, send an e-mail with the IP WHOIS info, etc.

I haven’t had too much time to play around with the configs, but I did manage to get it to work with my WordPress install on nginx, and here’s how.

I created a new security.php plugin in wp-content/mu-plugins with the following code (not really sure why Core doesn’t do this already):

function my_login_failed_403() {
    status_header( 403 );
add_action( 'wp_login_failed', 'my_login_failed_403' );

Basically the problem is that WordPress returns authentication failure with the status code of 200, which is the same status code of an authentication success, so there’s no easy way to tell the difference between the two just by looking at nginx’s access log.

The above code adds the 403 status code to failed login attempts, either via wp-login.php or xmlrpc.php.

Next, assuming you’ve already installed fail2ban (sudo apt-get install fail2ban) let’s define a new filter in /etc/fail2ban/filter.d and call it wordpress-auth.conf:

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

If you look at other files in filter.d, you’ll know what’s going on. failregex is a regular expression that matches a POST request to wp-login.php or xmlrpc.php with a status code of 403 in nginx’s access logs, assuming you’re using the default format. Probably not the best and fastest regex in the world, but serves its purpose.

Finally, let’s create a jail for all those bruteforce script kiddies. Let’s append the following code to the end of /etc/fail2ban/jail.conf:


enabled  = true
port     = http,https
filter   = wordpress-auth
logpath  = /var/log/nginx/access.log
maxretry = 3
bantime  = 3600

This is pretty easy to understand. We’re going to use our new wordpress-auth filter against nginx’s access.log file (double check the path) and ban IPs for one hour upon three or more failed attempts. Then we’re gonna restart the fail2ban daemon and watch the logs:

$ sudo service fail2ban restart
$ sudo tail -f /var/log/fail2ban.log
2014-01-16 05:46:54,372 fail2ban.actions: WARNING [wordpress] Ban
2014-01-16 06:05:01,915 fail2ban.actions: WARNING [wordpress] Ban
2014-01-16 06:37:50,345 fail2ban.actions: WARNING [wordpress] Ban


It is also a good idea to enable fail2ban for monitoring SSH access, FTP and any other services you’re running on your server. Up until a few days ago, I haven’t even realized someone was trying to bruteforce my SSH password. Now I get e-mail alerts everyday.

Frome the Great