In this tutorial, you will learn about how to protect WordPress against brute force attacks using Fail2ban. Fail2ban is a python based intrusion prevention tool that;
monitors log files (e.g. /var/log/auth.log,/var/log/apache/access.log) and temporarily or persistently bans failure-prone addresses by updating existing firewall rules. Fail2ban allows easy specification of different actions to be taken such as to ban an IP using iptables or hostsdeny rules, or simply to send a notification email.
By default, it comes with filter expressions for various services (sshd, apache, proftpd, sasl, etc.) but configuration can be easily extended for monitoring any other text file. All filters and actions are given in the config files, thus fail2ban can be adopted to be used with a variety of files and firewalls. Following recommends are listed:
- iptables/nftables — default installation uses iptables for banning. nftables is also supported. You most probably need it
- whois — used by a number of mail-whois actions to send notification emails with whois information about attacker hosts. Unless you will use those you don’t need whois
- python3-pyinotify — unless you monitor services logs via systemd, you need pyinotify for efficient monitoring for log files changes.
Are you using WordPress and looking for a professional WordPress website builder? Look no further since Elementor can help you create beautiful pages.
Using Fail2ban to Protect WordPress Against Brute force Attacks
As much as there are multiple ways of protecting WordPress logins against brute force attacks such as Restricting Access to WordPress Login Page to Specific IPs with libModSecurity, this guide will focus on the use of Fail2ban for that purpose.
Installing Fail2ban
To begin with, install Fail2ban on your host system. Installation of Fail2ban varies from one Linux distribution to the other.
If you are on an Ubuntu/Debian system, simply run the command below to install Fail2ban;
apt update
apt install fail2ban
On CentOS and similar distros, run the commands below to install Fail2ban;
dnf install epel-release
dnf update
dnf install fail2ban
Configure Fail2ban to Protect WordPress Authentications against Brute force attacks
First off, you need to know that Fail2ban monitors log files (e.g. /var/log/auth.log,/var/log/apache/access.log) and temporarily or persistently bans failure-prone addresses by updating existing firewall rules. Fail2ban allows easy specification of different actions to be taken such as to ban an IP using iptables or hostsdeny rules…
To effectively ban or block offending IP addresses, Fail2ban uses jails
which is basically a combination of various filters
and actions
. Fail2ban filters
are just but regular that matches a specific pattern in the respective log file being monitored. The can pattern can correspond to a failed login attempt, request status code, request URI or any other suspicious activity. Fail2ban actions
on the other hand define commands that are executed when the filter matches a specified pattern such as suspicious IP address.
Create Fail2ban WordPress Jail
Fail2ban has four configuration file types:
fail2ban.conf
: Fail2Ban global configuration (such as logging)filter.d/*.conf
: Filters specifying how to detect authentication failuresaction.d/*.conf
: Actions defining the commands for banning and unbanning of IP addressjail.conf
: Jails defining combinations of Filters with Actions.
Note: It is recommended that *.conf files should remain unchanged to ease upgrades. If needed, customizations should be provided in *.local files.
Copy the jail.conf
file to jail.local
file;
cp /etc/fail2ban/jail.{conf,local}
Next, edit the jail.local
file and set the default parameters under the [DEFAULT]
section.
vim /etc/fail2ban/jail.local
Some of the parameters we will adjust include;
- ignoreip: specifies IP addresses or hostnames to ignore.
- bantime: defines the duration in seconds of how long a host is banned. The default is 600 seconds (10 minutes).
- maxretry: defines the number of failures before a is banned.
- findtime: defines a time period within which if a host exceeds the maxretry value, it is banned.
...
ignoreip = 127.0.0.1/8 192.168.57.33
...
bantime = 3600
...
findtime = 60
...
maxretry = 3
This basically means, if there will be any suspicious activity like more than 3 failed logins within a duration of one minute, the suspicious host, except localhost and 192.168.57.33, will be banned for one hour.
Once you have defined the default options above, create a jail for wordpress. For example, paste the content below at the bottom of the jail.local file above;
...
# WordPress Jail
[wordpress-auths]
enabled = true
port = http,https
filter = wordpress-auth
logpath = /var/log/apache2/wp.access.log
You can adjust all these settings to meet your needs!!
Most options above are self explanatory. The filter specifies the name of the filter that we will create to filter
the logs that identifies the WordPress logins logs in the log file defined by the logpath
.
Save and exit the file.
Create Fail2ban WordPress Login Filter
Next you need to create a filter to define a regular expression that filters out the WordPress authentication logs from the specified log file.
Note that, in our case, we use the log file, /var/log/apache2/wp.access.log
.
For every WordPress login attempt, a POST
request is sent to /wp-login.php
file with 200 status code and is written to the Web server access log. Below are the sample logs for our logins;
tail -f /var/log/apache2/wp.access.log | grep "POST /wp-login.php"
192.168.57.33 - - [11/Mar/2021:20:18:15 +0000] "POST /wp-login.php HTTP/1.1" 200 2825 "http://kifarunix-demo.com/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0"
192.168.57.1 - - [11/Mar/2021:20:19:14 +0000] "POST /wp-login.php HTTP/1.1" 200 2815 "http://kifarunix-demo.com/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"
So while creating our filter, we will use a regex that filters out such kind of log lines from the log file.
Multiple Fail2ban filters are located under, /etc/fail2ban/filter.d/
. You can check any of them on how filters can be created.
Create a WordPress authentication filter. Pay attention the name of the filter configuration file
vim /etc/fail2ban/filter.d/wordpress-auth.conf
[Definition]
failregex = ^<HOST> .* "POST /wp-login.php HTTP.* 200
That is a sample failregex for our WordPress logins.
Save and exit. You can add more filters that suits your needs.
Test Fail2ban WordPress Filter
To verify if your filter can match the pattern in the log files, use fail2ban-regex
tool as follows.
Command synopsis.
fail2ban-regex [OPTIONS] <LOG> <REGEX> [IGNOREREGEX]
So to test our WordPress filter;
fail2ban-regex /var/log/apache2/wp.access.log /etc/fail2ban/filter.d/wordpress-auth.conf
Sample output showing matched lines;
Running tests
=============
Use failregex filter file : wordpress-auth, basedir: /etc/fail2ban
Use log file : /var/log/apache2/wp.access.log
Use encoding : UTF-8
Results
=======
Failregex: 10 total
|- #) [# of hits] regular expression
| 1) [10] ^<HOST> .* "POST /wp-login.php HTTP.* 200
`-
Ignoreregex: 0 total
Date template hits:
|- [# of hits] date format
| [577] Day(?P<_sep>[-/])MON(?P=_sep)ExYear[ :]?24hour:Minute:Second(?:\.Microseconds)?(?: Zone offset)?
`-
Lines: 577 lines, 0 ignored, 10 matched, 567 missed
[processed in 0.25 sec]
Missed line(s): too many to print. Use --print-all-missed to print all 567 lines
That confirms that the filter works fine.
Running Fail2ban
Restart Fail2ban service. Note that is started and enabled to run on system boot upon installation.
systemctl restart fail2ban
Checking the status;
systemctl status fail2ban
● fail2ban.service - Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2021-03-11 20:43:32 UTC; 44s ago
Docs: man:fail2ban(1)
Process: 5484 ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS)
Main PID: 5486 (f2b/server)
Tasks: 7 (limit: 2282)
Memory: 11.8M
CGroup: /system.slice/fail2ban.service
└─5486 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
Mar 11 20:43:32 ubuntu20 systemd[1]: Starting Fail2Ban Service...
Mar 11 20:43:32 ubuntu20 systemd[1]: Started Fail2Ban Service.
Mar 11 20:43:33 ubuntu20 fail2ban-server[5486]: Server ready
Check Enabled Fail2ban Jails
Check the enabled jails using fail2ban-client status
command.
fail2ban-client status
Status
|- Number of jail: 2
`- Jail list: sshd, wordpress-auths
Verifying WordPress authentication Protection
You can simulate events of the multiple failed authentications to WordPress to check if any banning can happen.
While tailing the logs and running failed login test;
tail -f /var/log/apache2/wp.access.log | grep "POST /wp-login.php"
192.168.57.1 - - [11/Mar/2021:20:47:59 +0000] "POST /wp-login.php HTTP/1.1" 200 2825 "http://kifarunix-demo.com/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"
192.168.57.1 - - [11/Mar/2021:20:48:02 +0000] "POST /wp-login.php HTTP/1.1" 200 2814 "http://kifarunix-demo.com/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"
192.168.57.1 - - [11/Mar/2021:20:48:04 +0000] "POST /wp-login.php HTTP/1.1" 200 2825 "http://kifarunix-demo.com/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"
After the third failed login, my IP is blocked and get such error on browser;
Checking Status of Fail2ban Banned IP
To check the status of the banned IPs, use fail2ban-client
command;
fail2ban-client status wordpress-auths
Status for the jail: wordpress-auths
|- Filter
| |- Currently failed: 0
| |- Total failed: 3
| `- File list: /var/log/apache2/wp.access.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 192.168.57.10
You can obtain a list of jails using fail2ban-client status
command.
Checking Iptables;
iptables -L -nv
...
Chain f2b-wordpress-auths (1 references)
pkts bytes target prot opt in out source destination
21 9078 REJECT all -- * * 192.168.57.1 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Unbanning Fail2ban Banned IP
If you want to unban an IP, run the command;
fail2ban-client set <jail> unbanip <IP>
fail2ban-client set wordpress-auths unbanip 192.168.57.1
Further Reading
Other Tutorials
Visualize WordPress User Activity Logs on ELK Stack
Create Scrolling Text Box on WordPress Newspaper Theme
How to fix WordPress could not establish a secure connection to WordPress.org
How to Install and Use WPScan WordPress Vulnerability Scanner Ubuntu 18.04
This was a great article thank you.
Question. What is the correct way to add multiple Definitions?
What if I wanted to also look for this…
[Definition]
failregex = ^ .* “POST /wp-login.php HTTP.* 503
Thanks for your help.
Thanks for the really in-depth article. Fail2Ban is definitely tricky! Have you looked at something like GuardGiant? It’s a more modern approach that tracks the devices that users use to login. It’s a better approach I think.
Hi Steve. thanks. We will have a look at it and see.