Ok, so probably the reason you’re here..is because you’re being swamped with failed login attempts against wp-login, or perhaps xmlrpc.

Not only are these really annoying, but also consume resources. I was recently working on a webserver with upwards of 40 different WordPress installations on it, to find that a massive amount of traffic each day was basically spurious or unwanted.

Fail2Ban works well as a log file analyser, which can then carry out actions based on parsing a given logfile. In this context, we want it to detect failed logins (and other nastiness), then firewall the culprit entirely. This way, the traffic doesn’t end up at the webserver, and doesn’t consume any more resources other than the kernel rejecting the TCP traffic. Nice!

This “How To” is specifically for CentOS 7, as the configuration procedure was somewhat awkward. So this not only (hopefully) server as a help for you…but also gives me something I can come back to later when I’ve forgotten how to do it! ?

What We’re Dealing With:

  • WordPress
  • Fail2Ban
  • CentOS 7 – with firewalld removed, as I don’t enjoy dozens of needless rules in my firewall. YMMV
  • wp-fail2ban – for logging auth attempts to /var/log/messages (really important, its not in /var/log/auth.log)
  • wp-cli – (optional really, but makes life easy if you already have ssh access)

Getting It Done..

Ok, first things first. Get logged into the server over ssh or whatever (sit at the machine if you wish), and install the required software. Fail2ban is in the epel repository, so we need that first (if not already installed)


sudo yum install epel-releasen

next up, grab fail2ban

sudo yum install fail2bann

next up, this article assumes you’re going to use wp-cli. If you’re not, then login to your WordPress install, head to plugins and install “wp-fail2ban”, then skip the next step. I’ve actually put together some wp-cli rpms but haven’t updated them for a bit. Check there first though, in case I got around to it ?

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.pharnchmod +x wp-cli.pharnsudo mv wp-cli.phar /usr/local/bin/wp

That’s wp-cli installed. Next to use it to install the wp-fail2ban plugin. cd into your website’s root directory (usually cd /var/www/html/yourwebsite)

/usr/local/bin/wp plugin install wp-fail2bann/usr/local/bin/wp plugin activate wp-fail2ban

With a couple of green confirmation messages, you should now have wp-fail2ban installed, and fail2ban on the actual machine.

Annoying Note:

I don’t much like firewalld. I’m completely comfortable hand-writing iptables scripts, and as such don’t really enjoy the whole “wrapper” experience. Ultimately it aims to simplify the manipulation of firewall rules, personally, I find it does the opposite (YMMV). If like me..you don’t want to use firewalld, you will find that during the process of installing fail2ban, you got a cheeky package fail2ban-firewalld which drops a firewalld specific config file in /etc/fail2ban/jail.d. To get rid of this:

rpm -e --nodeps fail2ban-firewalld

for some reason, whoever packages fail2ban things that firewalld *is* a dependency that’s needed. I disagree, but as such if you try and remove it with yum, it’ll try to remove fail2ban at the same time.


Time to test that wp-fail2ban is actually doing its thing. If you tail /var/log/messages, then try to login with false credentials, you should see something like:

# tail -f /var/log/messagesnnJul 10 17:35:10 localhost wordpress(yourwebsite.com)[7081]: Authentication failure for admin from x.x.x.x

That being the case, wp-fail2ban is doing its job. Next up, configure fail2ban itself.

Configuring Fail2ban

Firstly, you want to grab wordpress-soft.conf and wordpress-hard.conf from your plugin directory (webroot/wp-content/plugins/wp-fail2ban) and drop those into /etc/fail2ban/filter.d. To make things easier, you can use these (July 2016, these WILL change over time, best to grab the ones from the plugin)


# Fail2Ban configuration filenn[INCLUDES]nnbefore = common.confnn[Definition]nn_daemon = (?:wordpress|wp)nnfailregex = ^{f341ca813eb6d53b4b1b00b626e056248f8080d1a93e6cdef029826c23be15b1}(__prefix_line)sAuthentication failure for .* from $n ^{f341ca813eb6d53b4b1b00b626e056248f8080d1a93e6cdef029826c23be15b1}(__prefix_line)sXML-RPC authentication failure from $nnignoreregex =

and /etc/fail2ban/filter.d/wordpress-hard.conf

# Fail2Ban configuration filenn[INCLUDES]nnbefore = common.confnn[Definition]nn_daemon = (?:wordpress|wp)nnfailregex = ^{f341ca813eb6d53b4b1b00b626e056248f8080d1a93e6cdef029826c23be15b1}(__prefix_line)sAuthentication attempt for unknown user .* from ( via XML-RPC)?$n ^{f341ca813eb6d53b4b1b00b626e056248f8080d1a93e6cdef029826c23be15b1}(__prefix_line)sBlocked authentication attempt for .* from ( via XML-RPC)?$n ^{f341ca813eb6d53b4b1b00b626e056248f8080d1a93e6cdef029826c23be15b1}(__prefix_line)sBlocked user enumeration attempt from $n ^{f341ca813eb6d53b4b1b00b626e056248f8080d1a93e6cdef029826c23be15b1}(__prefix_line)sPingback error .* generated from $nnignoreregex =

These files set up the filters so that when fail2ban is scanning /var/log/messages, it knows how to handle failed logins, pingback errors, etc.

Next up, configure the jail file. Try not to edit /etc/fail2ban/jail.conf, as this will change with updates. Instead, I chose to drop this in /etc/fail2ban/jail.d/00-dcr.conf

[DEFAULT]n# Ban hosts for one hour:nbantime = 3600nn# Override /etc/fail2ban/jail.d/00-firewalld.conf:nbanaction = iptables-multiportnn[wordpress-hard]nenabled = truenfilter = wordpress-hardnlogpath = /var/log/messagesnmaxretry = 1nport = http,httpsnn[wordpress-soft]nenabled = truenfilter = wordpress-softnlogpath = /var/log/messagesnmaxretry = 3nport = http,https

This enables the two filters and sets them to the default ban action. Time to start fail2ban:

systemctl start fail2ban.servicensystemctl enable fail2ban.service

Now fail2ban should be running, you can confirm this with:

# fail2ban-client statusnStatusn|- Number of jail: 2n`- Jail list: wordpress-hard, wordpress-soft

this is showing us that not only is fail2ban running, but so are our two jails..win!

At this point, you should be set and running. Any failed login attempts will be logged in /var/log/messages, and any ban actions by fail2ban will be logged in /var/log/fail2ban.log

I’ve tried to make this as complete as possible, and easy to follow…I’d really appreciate the feedback (in the comments below) if you have any trouble, or want the howto adjusting for some reason (for example, if you REALLY want to use firewalld, or if you want a howto for CentOS 6 maybe)

Thanks for reading!

Let's Talk Start Now