In a previous blog post (Fail2ban + Tarpit), I explained how to setup a Tarpit for Fail2ban to use it against the attacker that got banned multiple times. It works great especially in conjunction with WP Fail2ban, a fail2ban plugin for WordPress.
Later on, I moved this website to CloudFlare, by doing so I rendered one my security mechanism inefficient.
WP Fail2ban
This plugin simply log the failed login attempts to one of the system log like /var/log/user.log or even the normal syslog. You can also set a group of user that should be logged directly when there is an attempt on it like “admin”. It’s useful to find the bot attempting to login into your admin panel with a default username “admin”.
I set up 2 different jail, one for failed login attempts and one for login attempts from blocked source.
You can find all the information on how to setup this in the documentation of the plugin:Â Installation steps
Problems
By using CloudFlare, it means all the traffic to the website is first sent to CloudFlare then to my server. Which means, my server only get connections from CloudFlare. This poses 2 problems for fail2ban.
No more visitor IP
The first one being the lack of real IP of the visitor, since only CloudFlare is connecting to my server, this can be easily resolved and I’ll explain how. The second problem I faced is directly linked to the resolution of the first one. Even with the real IP of the visitor, fail2ban won’t be able to ban the “visitor” (understand
Real IP useless
Even with the real IP of the visitor, fail2ban won’t be able to ban the “visitor” (understand bot) trying to connect as admin to my WordPress since it’s coming from CloudFlare IP’s. And obviously banning it as I was doing before with TARPIT won’t have any effect.
Solutions
To resolve those problems, I first setup Nginx to gather the real IP of the visitor, CloudFlare does provide a header with this information.
Secondly, I created a script to interact with CloudFlare API to use their firewall to ban an IP.
Getting the real IP
Configuring Nginx for doing is is pretty easy. Add this file to your conf.d directory. It contains all the CloudFlare CIDR, IPv4 and IPv6.
Now Nginx will send, to your different application, the right IP
CloudFlare Firewall
The script I created directly use the API v4 of CloudFlare to ban the IP directly for your website but adding a captcha. I prefer to put the captcha because a bot can’t pass it (thanks reCAPTACHA) and because if it’s a legitimate user, he’s not completely banned from accessing the website.
Then I created a new action for fail2ban to automatically add and remove IP ban in CloudFlare. The last part was to assign it to the wordpress-hard rule in jail.d. (Or any other jail you setup in your configuration).
Script
To use my script you need to have JQ installed. I’m using it to parse the response from CloudFlare API when wanting to remove a blocked IP.
sudo apt-get install jq
Don’t forget to make the file executable (chmod +x). I placed it at  /usr/local/sbin/cloudflare-firewall
Fail2ban Action
Put this file in your action.d directory. You also need to edit it to add your CloudFlare username, API Key and ZONE ID. You can find all of them in your CloudFlare profile. The ZoneID is the ID assigned to your domain by CloudFlare. You can override this default in each one of the jail you create by given parameter to the action.
Fail2ban Jail for WP Fail2Ban
And the last part is to set your jail to use the new action like this:
Thanks for your article! I know it’s a few years old, but it served as ‘inspiration’ for my own configuration. Indeed, I was having trouble dealing with a single bash command-line on action.d, and pushed everything out into separate external scripts, just as you did.
Eventually, I saw the latest version of how the fail2ban core developers implemented this functionality directly in action.d/cloudflare.conf. They cheated! 🙂 They used several lines instead of just one! Apparently, fail2ban has no trouble dealing with multiple lines, so, ultimately, I moved all the code back into that and removed my external scripts…
The advantage of having those scripts, of course, is that you can manually ban/unban IPs on Cloudflare without going through fail2ban-client, which is sometimes useful, especially when debugging 🙂
BTW, Cloudflare’s IP addresses may change over time (not much, but it happens). The safest way to be up-to-date is to have a cron job updating the file included by nginx. Cloudflare eagerly publishes their ‘current’ list of IP addresses in an easy-to-retrieve format, and I’m sure there are many scripts to do exactly that; I use https://github.com/pothi/wordpress-nginx/blob/master/scripts/update-cloudflare-ip-list.sh (that repository has a lot of great tips to run WordPress on top of nginx).
To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behaviour or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions. Cookies are used for ads personalisation.
Functional
Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
13th January 2021 at 12:13
Thanks for your article! I know it’s a few years old, but it served as ‘inspiration’ for my own configuration. Indeed, I was having trouble dealing with a single
bashcommand-line onaction.d, and pushed everything out into separate external scripts, just as you did.Eventually, I saw the latest version of how the
fail2bancore developers implemented this functionality directly inaction.d/cloudflare.conf. They cheated! 🙂 They used several lines instead of just one! Apparently,fail2banhas no trouble dealing with multiple lines, so, ultimately, I moved all the code back into that and removed my external scripts…The advantage of having those scripts, of course, is that you can manually ban/unban IPs on Cloudflare without going through
fail2ban-client, which is sometimes useful, especially when debugging 🙂BTW, Cloudflare’s IP addresses may change over time (not much, but it happens). The safest way to be up-to-date is to have a
cronjob updating the file included bynginx. Cloudflare eagerly publishes their ‘current’ list of IP addresses in an easy-to-retrieve format, and I’m sure there are many scripts to do exactly that; I use https://github.com/pothi/wordpress-nginx/blob/master/scripts/update-cloudflare-ip-list.sh (that repository has a lot of great tips to run WordPress on top ofnginx).