As promised in the preceding post about a recent security incident, here are the details of how to set up a uWSGI jail for fail2ban. These details should work with both fail2ban-0.8 and fail2ban-0.9.

The idea is that multiple 4xx or 5xx errors from the same remote IP address within a short period of time should result in a ban, because your website is probably being scanned for vulnerabilities. We assume that your uwsgi logfile is in the default format.

First, you need to ensure that the correct remote IP address is being reported in the uWSGI logfile - not the IP address of your proxy or load balancer in front of uWSGI, but the IP address of the original request. Most front ends add the X-Forwarded-For header which contains the correct IP address, and uWSGI will log it properly if you add this to your uWSGI config:

log-x-forwarded-for = true

Now you need to create some fail2ban config files as follows. (Replace ‘yourapp’ with your actual application name, obviously, and replace ‘3031’ with the correct port number. Be sure to set maxRetry, findtime and banTime appropriately. Uncomment mail-whois if you want email alerts.)

filename: /etc/fail2ban/jail.local

[uwsgi-yourapp]
enabled  = true
filter   = uwsgi
logpath  = /var/log/uwsgi/yourapp.log
action   = iptables-stringmatch-xff[name=uwsgi-yourapp,port=3031]
#          mail-whois[name=uwsgi-yourapp,dest=security@yourdomain.example.com]
maxRetry = 10
findtime = 300
banTime  = 3600

filename: /etc/fail2ban/filter.d/uwsgi.conf

[Definition]
failregex   = <HOST> \(.*\) .* \(HTTP/[012.]+ [45][0-9][0-9]\)
ignoreregex =

We can’t ban the attacker’s IP address in iptables, because the host running uWSGI sees packets that come from the front end, not from the attacker. But we can inspect incoming packets to see if they contain the attacker’s X-Forwarded-For header (thanks to Centos.Tips).

filename: /etc/fail2ban/action.d/iptables-stringmatch-xff.conf

[INCLUDES]
before = iptables.conf

[Init]
protocol = tcp

[Definition]

actionstart = iptables -N f2b-<name>
              iptables -A f2b-<name> -j RETURN
              iptables -I <chain> -p <protocol> --dport <port> -j f2b-<name>

actionstop  = iptables -D <chain> -p <protocol> --dport <port> -j f2b-<name>
              iptables -F f2b-<name>
              iptables -X f2b-<name>

actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]'

actionban   = iptables -I f2b-<name> 1 -p tcp -m string --algo bm --string "X-Forwarded-For: <ip>," -j <blocktype>

actionunban = iptables -D f2b-<name>   -p tcp -m string --algo bm --string "X-Forwarded-For: <ip>," -j <blocktype>