Protect WordPress wp-login with Apache HTTP Auth + fail2ban

Most tutorials for protecting wp-login.php for WordPress block users who are actually trying to make login attempts which makes logical sense. The problem with this method is that these brute force protection methods are still expensive for your web server. When a user tries to log in, PHP processing occurs and MySQL queries are made to verify if the user is valid or not. If you are getting lots of failed login attempts then you will see CPU usage spike and RAM be unnecessarily used up.

Using a basic http authentication method with Apache is far less resource intensive, no PHP or MySQL required so your server will be using considerably less energy to protect itself against malicious attackers and hackers. We can use fail2ban to scan the Apache log files and ban users who fail after a certain amount of attempts.

For this tutorial you will need shell access to your web server running Debian or Ubuntu.

Protect WordPress wp-login with Apache HTTP Auth + fail2ban

Installation overview

  • Install Basic HTTP Authoriziation for Apache
  • Configure Apache to use HTTP Authentication
  • Configure fail2ban to ban users trying to break in to WordPress

Install Basic HTTP Authorization for Apache

Update your repository list

sudo apt-get update
sudo apt-get install apache2-utils fail2ban -y

Create the password file for the basic HTTP authentication

sudo htpasswd -c /etc/apache2/.htpasswd wpbullet
New password:
Re-type new password:
Adding password for user wpbullet

Have a look and you can see the .htpasswd file just contains an md5-hashed version of your password

cat /etc/apache2/.htpasswd

Configure Apache with Basic HTTP Auth for WordPress

Now we need to enable .htpasswd for Apache and the wp-login.php file in .htaccess

nano /var/www/wp-bullet/.htaccess

Add the red lines to enable the basic HTTP Authorization for your WordPress site running on Apache

# Stop Apache from serving .ht* files
<Files ~ "^\.ht">
Order allow,deny
Deny from all
</Files>
# Protect wp-login
<Files wp-login.php>
AuthUserFile /etc/apache2/.htpasswd
AuthName "Private access"
AuthType Basic
require user wpbullet
</Files>

Ctrl+X, Y and Enter to Save and Exit.

Check your Apache virtual host to make sure you have error logging enabled, replace wp-bullet.conf with your Apache virtual host.

sudo nano /etc/apache2/sites-available/wp-bullet.conf

I have highlighted the line enabling the error logs which fail2ban needs to scan for failed logins.

<VirtualHost *:80>
        ServerAdmin [email protected]
        ServerName wp-bullet
        ServerAlias wp-bullet
        DocumentRoot /var/www/wp-bullet
        <Directory />
                Options FollowSymLinks
                AllowOverride All
        </Directory>
        <Directory /var/www/wp-bullet/ >
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>

        ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
        <Directory "/usr/lib/cgi-bin">
                AllowOverride All
                Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                Order allow,deny
                Allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/wp-bullet.error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn
        LogFormat "%h %l %u %t \"%r\" %>s %b" combined
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Check the Apache configuration syntax is ok

sudo apachectl configtest

Reload Apache

sudo service apache2 restart

Configure fail2ban to Ban WordPress Hackers

Now we generate some log data so the nginx log is populated with http authorization failures

Make sure to use the right username and wrong password. Also use an incorrect user and password as it generates different data

Have a peek in your nginx log file

cat /var/log/apache2/wp-bullet.error.log

You will see the WordPress login failures

[Fri Apr 22 03:56:22.528131 2016] [auth_basic:error] [pid 10246] [client 192.168.60.1:55069] AH01618: user wpadmin not found: /wp-login.php
[Fri Apr 22 03:57:26.282621 2016] [auth_basic:error] [pid 10253] [client 192.168.60.1:55081] AH01617: user wpbullet: authentication failure for "/wp-login.php": Password Mismatch
[Fri Apr 22 03:57:28.037511 2016] [auth_basic:error] [pid 10253] [client 192.168.60.1:55081] AH01618: user  not found: /wp-login.php

fail2ban already has an Apache HTTP authorization filter so we can enable it and test it

Test the fail2ban Apache HTTP Authorization filter by specifying your error log file from your Apache virtual host.

fail2ban-regex /var/log/apache2/wp-bullet-error.log /etc/fail2ban/filter.d/apache-auth.conf

You get this output

Running tests
=============

Use   failregex file : /etc/fail2ban/filter.d/apache-auth.conf
Use         log file : /var/log/apache2/wp-bullet.error.log


Results
=======

Failregex: 3 total
|-  #) [# of hits] regular expression
|   2) [1] ^\[[^]]*\] \[(:?error|\S+:\S+)\]( \[pid \d+(:\S+ \d+)?\])? \[client (:\d{1,5})?\] (AH01617: )?user .*? authentication failure for "\S*": Password Mismatch(, referer: \S+)?$
|   3) [2] ^\[[^]]*\] \[(:?error|\S+:\S+)\]( \[pid \d+(:\S+ \d+)?\])? \[client (:\d{1,5})?\] (AH01618: )?user .*? not found(: )?\S*(, referer: \S+)?\s*$
`-

Ignoreregex: 0 total

Date template hits:
|- [# of hits] date format
|  [18] WEEKDAY MONTH Day Hour:Minute:Second[.subsecond] Year
`-

Lines: 18 lines, 0 ignored, 3 matched, 1 missed
|- Missed line(s):
|  [Fri Apr 22 03:47:41.340097 2016] [mpm_event:notice] [pid 3776:tid 139899287992192] AH00489: Apache/2.4.10 (Debian) configured -- resuming normal operations

Create a fail2ban jail folder if it doesn’t already exist

sudo mkdir -p /etc/fail2ban/jail.d

Create the fail2ban jail configuration for Apache’s HTTP authorization

sudo nano /etc/fail2ban/jail.d/apache-auth.conf

Paste this fail2ban jail configuration that will scan your Apache error log files for 3 login failures within 60 seconds and ban them for 600 seconds.

[apache-auth]
enabled = true
filter = apache-auth
port = http,https
logpath = /var/log/apache2/*error*.log
findtime = 60
bantime = 6000
maxretry = 3

Ctrl+X, Y and Enter to Save and Exit.

Now that we know have made the Apache fail2ban jail, test the fail2ban syntax so make sure it’s all working

sudo fail2ban-client -d

If you didn’t see any errors (warnings are OK) then we can restart fail2ban to activate the filter and jail

service fail2ban restart

Check the Apache HTTP Auth fail2ban Status

Check the status of the fail2ban apache-auth jail.

sudo fail2ban-client status apache-auth

You will be able to see how many hackers have failed and are banned

Status for the jail: apache-auth
|- filter
|  |- File list:        /var/log/apache2/wp-bullet.error.log
|  |- Currently failed: 0
|  `- Total failed:     3
`- action
   |- Currently banned: 1
   |  `- IP list:       192.168.60.1
   `- Total banned:     1

You can also list the iptables

sudo iptables -L -n

This shows the iptables chain for limiting nginx HTTP Auth requests

Chain f2b-apache-auth (2 references)
target     prot opt source               destination
REJECT     all  --  191.96.249.54        0.0.0.0/0            reject-with icmp-port-unreachable
RETURN     all  --  0.0.0.0/0            0.0.0.0/0
RETURN     all  --  0.0.0.0/0            0.0.0.0/0

Sources

fail2ban WordPress login attacks
Protect nginx with fail2ban