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.
Protect WordPress Against Brute force Attacks Using Fail2ban
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.
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 install fail2ban
On CentOS and similar distros, run the commands below to install Fail2ban;
dnf install epel-release
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
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 failures
action.d/*.conf: Actions defining the commands for banning and unbanning of IP address
jail.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.
jail.conf file to
Next, edit the
jail.local file and set the default parameters under the
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
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,
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
[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.
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)  ^<HOST> .* "POST /wp-login.php HTTP.* 200 `- Ignoreregex: 0 total Date template hits: |- [# of hits] date format |  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.
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: Starting Fail2Ban Service... Mar 11 20:43:32 ubuntu20 systemd: Started Fail2Ban Service. Mar 11 20:43:33 ubuntu20 fail2ban-server: Server ready
Check Enabled Fail2ban Jails
Check the enabled jails using
fail2ban-client status command.
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 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.
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
There is more about Fail2ban. But that marks the end of our tutorial on how to protect WordPress against brute force attacks using Fail2ban.