How to set-up fail2ban for a WordPress site

What is Fail2ban

Fail2ban is a tool which you can use to reduce the impact of attacks on your servers. Typically you configure it to monitor a log file for suspicious activity.  Then once the activity crosses a threshold you can have it take an action, such as block the source IP address in your firewall.  It’s a good way to stop attacks early but doesn’t entirely prevent them.

Why use it to protect WordPress

Due to it’s popularity, WordPress is often the target of automated attacks.  We often see bruteforce attacks targeting xmlrpc.php or wp-login.php, these rely on making a huge number of requests in the hope that one will eventually be successful.  Using strong passwords, especially for accounts with admin access is important to reduce the risk from attacks.  Fail2ban can be used to slow attackers down.  This helps for two reasons: it makes them less likely to succeed; it reduces the load on the server caused by processing these requests.

Other options

Blocking an attack as far upstream as possible is always advantageous to save resources so we would typically favour a Web Application Firewall or Webserver configuration over using fail2ban or a WordPress plugin however every site, server and customer is different so it will depend on the exact configuration used and required.

Install & config fail2ban (for Ubuntu)

sudo apt-get install fail2ban

Fail2ban works by having a jail file which references the log file, a filter and an action.  By default fail2ban will protect sshd.  If you are restricting SSH access in another way then you might want to turn this off.  You can do so by creating the following as /etc/fail2ban/jail.local

enabled = false

Fail2ban doesn’t come with a filter for WordPress.  I’d like to credit these two articles for providing a good starting point.  We see requests to ‘//xmlrpc.php’ (note the double slashes) fairly frequently so tweaked the below to also flag them.  As this is detecting any requests to wp-login/xmlrpc it will flag legitimate admin users when they login etc.  We’ll look to account for this with the jail configuration.

You can create the filter as /etc/fail2ban/filter.d/wordpress.conf

failregex = ^<HOST> .* "(GET|POST) /+wp-login.php
            ^<HOST> .* "(GET|POST) /+xmlrpc.php

The jail file has most of the configuration options:

  • logpath, in this case the path to your Apache access log
  • action, adjust this if you’re using a different firewall or want to be sent email instead, you can see available options in /etc/fail2ban/action.d/
  • maxretry, the number of requests within findtime seconds to ban after
  • bantime, the number of seconds to ban for, with this action how long they’re blocked in iptables

As mentioned, the filter will catch both malicious and legitimate users.  We’re configuring maxrety fairly high and bantime relatively low to minimise the probability and impact if we do block a legitimate user.  Whilst this allows attackers to make roughly one request every ten seconds this is a fraction of what they’d make without fail2ban.

You can create the jail file as /etc/fail2ban/jail.d/wordpress.conf

enabled = true
port = http,https
filter = wordpress
action = iptables-multiport[name=wordpress, port="http,https", protocol=tcp]
logpath = /var/log/apache2/all-sites-access.log
maxretry = 12
findtime = 120
bantime = 120

Restart fail2ban to load in your new config

sudo systemctl restart fail2ban

Checking your fail2ban set-up

Show the jails

sudo fail2ban-client status

Show info about the WordPress jail

sudo fail2ban-client status wordpress

List the f2b-wordpress iptables chain (this will be created the first time fail2ban blocks an IP and contain blocked IPs)

sudo iptables -v -n -L f2b-wordpress
sudo ip6tables -v -n -L f2b-wordpress

View the log to see any IPs banned or flagged

sudo less /var/log/fail2ban.log


Feature image by Jussie D.Brito licensed CC BY-SA 2.0.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *