Protect Wordpress Login in Docker container with Fail2Ban

💡
This tutorial is part of the How to: Fail2Ban tutorials series, you may check the main post to know what Fail2ban is, and how Fail2Ban works.

Step 1: Add a New Fail2ban Jail

Edit jail.local file:

sudo nano /etc/fail2ban/jail.local

Add a new jail "wplogin" if it doesn't exist:

[wplogin]

enabled = true
port = http,https
filter = wplogin
chain = DOCKER-USER
logpath = /var/lib/docker/containers/*/*-json.log
banaction = docker-action

Note

This jail will use a new filter named wplogin (we will create later) and use default settings to ban, which is my configuration:
bantime = 604800
findtime = 100
maxretry = 2

means that if there is a failed attempt twice within 100 seconds, then the IP will be banned for 604800 seconds.

The log file for the Docker container to be monitored is located on the Docker host under /var/lib/docker/containers/<CONTAINERID>/<CONTAINERID>-json.log

Step 2: Create a Custom Fail2ban Filter

Create a new filter file on /etc/fail2ban/filter.d/wplogin.conf

[Definition]
failregex = {"log":"<HOST> -.*POST.*wp-login.php.*
ignoreregex =

Step 3: Create a Fail2ban action

Create a new action file: nano /etc/fail2ban/action.d/docker-action.conf

[Definition]

actionstart = iptables -N f2b-wplogin
              iptables -A f2b-wplogin -j RETURN
              iptables -I INPUT -p tcp -m multiport --dports 80,443 -j f2b-wplogin

actionstop = iptables -D INPUT -p tcp -m multiport --dports 80,443 -j f2b-wplogin
             iptables -F f2b-wplogin
             iptables -X f2b-wplogin

actioncheck = iptables -n -L INPUT | grep -q 'f2b-wplogin[ \t]'

actionban = iptables -I f2b-wplogin 1 -s <ip> -j DROP

actionunban = iptables -D f2b-wplogin -s <ip> -j DROP

After adding a new jail, filter, and action, Fail2ban should be restarted. Restart fail2ban with:

sudo systemctl stop fail2ban
sudo systemctl start fail2ban

Step 4: Let's try it out!

Like the other tutorials [nginx-auth, and mysql-auth], we need to test Fail2ban policies to ensure they block traffic as expected.

In order to test it, you need to:

  • Install Docker Engine (docs)
  • A docker-compose wordpress-mysql (gists)
  • Nginx proxy, if you deployed WP on a different port
Protect Wordpress Login in Docker container with Fail2Ban
Protect Wordpress Login in Docker container with Fail2Ban - compose.yaml

After the container was deployed, we can check the list of docker containers using:

root@server:~/wp# docker ps -a
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS                                   NAMES
9c269ebb7a16   wordpress:latest       "docker-entrypoint.s…"   12 minutes ago   Up 12 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp   wp-wordpress-1
5ece75d940d0   mariadb:10.6.4-focal   "docker-entrypoint.s…"   12 minutes ago   Up 12 minutes   3306/tcp, 33060/tcp                     wp-db-1

You can see my WP container ID is 9c269ebb7a16

Now, check the jail status with the following command:

root@server:~/wp#  fail2ban-client status wplogin
Status for the jail: wplogin
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- File list:        /var/lib/docker/containers/9c269ebb7a16e891383c388b7eeb21f6cdb01e803c33508ba39d6153991e9a5f/9c269ebb7a16e891383c388b7eeb21f6cdb01e803c33508ba39d6153991e9a5f-json.log
 /var/lib/docker/containers/5ece75d940d06a39b0e7de6b65548080735982b1276bca19f802bbbea9851b2c/5ece75d940d06a39b0e7de6b65548080735982b1276bca19f802bbbea9851b2c-json.log
`- Actions
   |- Currently banned: 0
   |- Total banned:     0
   `- Banned IP list:
Fail2ban successfully monitored both containers

My Fail2ban log path/file list matches with my wp and mysql container.

Now open your wordpress site, and install wordpress as usual (like http://yourdomain.com/wp-admin/install.php) until you can log in on wp-login.php.

And the last step, let's try creating an error to trigger fail2ban jail.

https://ns1.my.id/unggah/2023/03/wordpress-docker-nginx-fail2ban.gif
Protect Wordpress Login in Docker container with Fail2Ban

List Blocked IP

To get a list of blocked IPs in your server, run the following command:

fail2ban-client status wplogin
Output
root@server:~/wp# fail2ban-client status wplogin
Status for the jail: wplogin
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     6
|  `- File list:        /var/lib/docker/containers/6390d681392a6a38830f51e8fb5ea7e9c8c3a2fedc57383de3fe816f2d8838d6/6390d681392a6a38830f51e8fb5ea7e9c8c3a2fedc57383de3fe816f2d8838d6-json.log /var/lib/docker/containers/b344eed0327b7645da323249b8d44c5d19e403f87a441840d8da9301734dab13/b344eed0327b7645da323249b8d44c5d19e403f87a441840d8da9301734dab13-json.log
`- Actions
   |- Currently banned: 1
   |- Total banned:     1
   `- Banned IP list:   192.168.0.6

Unblock IP

To unblock banned IPs, run the following command

fail2ban-client set wplogin unbanip 192.168.0.6

Thanks for reading!

Reference: