Fail2ban configuration additions for Plone

I was having fun with today. I run a Linode and have had the satisfaction of watching ssh login attempts result in IP address bans... automatically! All you have to do is install and run fail2ban.

Over the last while I've also been pondering the daily email reports fail2ban sends me that show attempts to use Plone's join_form and sendto_form as well as a relatively new annoyance: an attempt to use the search form with lots of empty parameters.

Here are some configuration additions you might find useful.

Add this to your jail.local:

enabled  = true
filter   = plone
logpath  = /var/log/nginx/access.log
port     = 80,443

enabled  = true
filter   = plone-search
logpath  = /var/log/nginx/access.log
port     = 80,443
maxretry = 1

enabled  = true
filter   = no-php
logpath  = /var/log/nginx/access.log
port     = 80,443
maxretry = 1

Create these new jail configurations in your filter.d directory.


failregex = ^<HOST> -.*GET *\/.*(join_form|sendto_form).*$
ignoreregex =


failregex = ^<HOST> -.*GET *\/.*(search).*\&\&\&.*$
ignoreregex =

and my favourite... no-php.conf

failregex = ^<HOST> -.*(GET|POST) *\/.*\.(php).*$
ignoreregex =

Then use

sudo fail2ban client reload

For fun, you can see how many IP addresses you just banned (it may take a minute for this to show up).

sudo fail2ban-client status no-php
Status for the jail: no-php
|- filter
|  |- File list:        /var/log/nginx/access.log
|  |- Currently failed: 0
|  `- Total failed:     24
`- action
   |- Currently banned: 17
   |  `- IP list: 173.2\
45.49.129 69.3\
   `- Total banned:     17

this is awesome, I working on this tomorrow

Beware if you use cloudflare... these bans will eventually prevent cloudflare servers from seeing your site.

This explains the problem... you need to ban the real visitor IP address.

How to get the real visitor IP addresses

How to get the real visitor IP addresses with nginx

You will need to know these CloudFlare IP ranges

How to configure nginx to show the real visitor IP addresses, not CloudFlare's

This is a way of configuring fail2ban to use CloudFlare's API to ban IPs (but it still requires that you get the real visitor IP addresses, as above):

I'm trying this out and will let you know the outcome :smile:

we used fail2ban during the Olympics to protect our site from attacks and there are some things that you should know before deploying this in production:

first, fail2ban consumes a considerable amount of CPU resources so, if you have a site with some traffic, you can put yourself into huge problems with the configuration you're providing. the right way to process this with fail2ban is not by processing the access.log file, but the error.log, which is less resource intensive.

second, if you really want to block some resources you better use nginx built-in features on first place; I, for instance, block all PHP traffic with the following directives in my nginx configuration:

server {
    location ~ \.php$ {
        return 410;
        access_log off;

this is way better, less resource intensive and less dangerous than your current settings as you could end blocking a whole institution just because somebody asked for a single banned URL.

nginx has a couple o additional nice features like connection limiting and rate limiting; we used that to protect our site from DDoS and we blocked offending IP addresses temporarily without major issues (I can share the configurations during the Conference).

you should also protect the search and join forms with rate limiting instead and then use fail2ban to block the IP addresses that are making a lot of requests to those forms (just follow the instruction in Survive DDoS attack with nginx and fail2ban); and "a lot" here is different for each case. for instance I can say that I don't want more that 2 requests to the search form per second from an specific IP, but for some people that value should be more or less:

this is a typical nginx configuration to achieve that:

http {
    # rate limiting settings
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    limit_conn_status 429;
    limit_req_zone $binary_remote_addr zone=ddos:10m rate=10r/s;
    limit_req_status 429;

server {
    location / {
        limit_conn addr 30;
        limit_req zone=ddos burst=50;

note the use of the burst parameter.

possibilities are endless.


Thanks @hvelarde! So noted...

Because I have been unable to figure out (yet?) how to get CloudFlare to pass in the original IP addresses (and/or for my receiving nginx to read those), I have instead used CloudFlare page rules (you get 3 free ones per domain) to activate the "I'm under attack" mode for URLs that match (say) "join_form".

CloudFlare Page Rules Tutorial

What does "I'm Under Attack Mode" do?

IIRC, in CloudFlare you have to enable a feature called ''True-Client-IP Header'', but it requires an Enterprise plan; see How do I restore original visitor IP with Nginx?