Fail2Ban CloudFlare action using Authorization Bearer Token

OVERVIEW

In this article we revisit the Fail2Ban CloudFlare action. CloudFlare recently introduced Authorization Bearer Tokens to make there service more RFC compliant. Kazimer Corp.’s CloudFlare REST v4 action for Fail2Ban only needs a few slight alterations to use these new tokens.

tl;dr version: You can get the updated action file on the fail2ban-action-cloudflare-restv4-bearer-token GitHub repository.

THE NEW ACTIONBAN COMMAND

The new actionban command for CloudFlare IP address blocking is as follows…

actionban = curl -s -X POST https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules \
	-H "Authorization: Bearer <cfkey>" -H "Content-Type: application/json" \
	--data '{"mode":"block","configuration":{"target":"ip","value":"<ip>"},"notes":"Banned by Fail2Ban"}'

This is a straight forward use of CloudFlare’s User Level Create Access Rule command. In the POST data we set the mode to block, send the IP address to be blocked, and set a note that Fail2Ban created this rule. There is no error checking of the response data returned by CloudFlare. The actioncheck command could test if the IP address is already banned. However, there is no need to consume more resources running an actioncheck command before every actionban command since CloudFlare will simply ignore any Create Access Rule command that targets an IP address that is already blocked.


THE NEW ACTIONUNBAN COMMAND

The actionunban command is a bit more difficult to update to the new API because you can’t simply specify an IP address to unblock, you need the firewall rule ID for the blocked IP address to delete the block. The solution is to use the CloudFlare List Access Rules command to look up the firewall rule ID for an IP address, pipe the response to a Python script to extract the IP address, then pipe the script output into a CloudFlare Delete Access Rule command. The new actionban command needed is as follows…

actionunban = curl -s -X GET -H "Authorization: Bearer <cfkey>" -H "Content-Type: application/json" \
	"https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?page=1&per_page=5&mode=block&configuration.target=ip&configuration.value=<ip>&notes=Banned by Fail2Ban&match=all&order=configuration.value&direction=desc" | \
	python -c "import sys, json; print json.load(sys.stdin)['result'][0]['id'];" | \
	xargs [email protected]@ curl -s -X DELETE -H "Authorization: Bearer <cfkey>" -H "Content-Type: application/json" https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/@@

The first curl command asks CloudFlare for a list of firewall rules that match both the given IP address and has a note that contains “Banned by Fail2Ban”. Filtering on both the IP address and the note contents ensures that we are only working with firewall rules created by Fail2Ban, and not deleting any firewall rules created manually or by some other tool.

The JSON response data from CloudFlare gets piped into a single line Python script which extracts the firewall rule ID from the first result in the response and prints it. This output is piped into the xargs command, which then builds and executes the CloudFlare Delete Access Rule command.

IMPLEMENTATION

We could modify the /etc/fail2ban/action.d/cloudflare.conf file with these new commands. But to avoid package updates possibly overwriting our changes, it would be better to create a new action configuration file. You can download the latest version of the new action file /etc/fail2ban/action.d/cloudflare-restv4-bearer-token.conf from the fail2ban-action-cloudflare-restv4-bearer-token GitHub repository. Copy this new action file the /etc/fail2ban/action.d/ directory on your server.

Changes to jail.local

Next, add, or update, CloudFlare’s IPv4 Addresses Ranges and IPv6 Address Ranges to your ignoreip variable in your jail.local file. This will prevent Fail2Ban from accidentally banning any of CloudFlare’s servers. Also, if you are running Apache2, please take a look at mod_cloudflare to make sure your log files are reporting the correct client IP address. Otherwise, the following ignoreip setting ignores in all filter hits…

# "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not
# ban a host which matches an address in this list. Several addresses can be
# defined using space (and/or comma) separator.
ignoreip = 127.0.0.1/8 ::1/128 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22 2400:cb00::/32 2606:4700::/32 2803:f800::/32 2405:b500::/32 2405:8100::/32 2a06:98c0::/29 2c0f:f248::/32

Next, fill in cfapitoken with your CloudFlare Authorization Bearer Token API Key. Define a new action value called action_cfv4_token as shown below. You will probably not want to use this as the default action since CloudFlare only deals with web site traffic.  For example, a CloudFlare IP block will not stop a direct connection to your server’s SSH port. Instead, use this CloudFlare action as an override for web service related jails. However, the recidive jail is a good candidate for using the CloudFlare action to entirely block repeat offenders.

# ACTIONS
#
# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
# to the destemail. Replace cfapitoken example value with your own Authorization Bearer Token.
# Bearer Token must be granted read-write-delete rights to your firewall rules.
#
cfapitoken = c2547eb745079dac9320b638f5e225cf483cc5cfdda41

action_cfv4_token = cloudflare-restv4-bearer-token[cfkey="%(cfapitoken)s"]
               %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]

RECIDIVE EXAMPLE

Here is an example of how to modify the recidive jail to use the new action. Since the recidive jail bans repeat offenders of any other jails, the ban may or may not be happening because of abuse of web services. You need to ban the offending IP address from all ports and protocols of the server’s firewall and on CloudFlare to bock access to web services through their servers. Here is how to implement that…

# JAILS
#

# Jail for more extended banning of persistent abusers
# !!! WARNINGS !!!
# 1. Make sure that your loglevel specified in fail2ban.conf/.local
#    is not at DEBUG level -- which might then cause fail2ban to fall into
#    an infinite loop constantly feeding itself with non-informative lines
# 2. Increase dbpurgeage defined in fail2ban.conf to e.g. 648000 (7.5 days)
#    to maintain entries for failed logins for sufficient amount of time
[recidive]
logpath  = /var/log/fail2ban.log
banaction = %(banaction_allports)s
action   = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
           cloudflare-restv4-bearer-token[cfkey="%(cfapitoken)s"]
           %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
bantime  = 604800  ; 1 week
findtime = 172800  ; 2 days
enabled = true

This jail will now perform three separate actions when banning an IP address. The first action bans the IP addresses on all ports and protocols with the server’s firewall. But since access to your web services are proxied through CloudFlare the attacker can still get through to the server’s web sites, so the second action uses the new cloudflare-restv4-bearer-token action to block web access by the offending IP address. The third action, %(mta)s-whois-lines, sends an e-mail about what triggered the ban through your configured e-mail service and to your configured notification e-mail address.

Don’t forget to restart the Fail2Ban service after these edits…

sudo service fail2ban restart

TESTING

fail2ban-cloudflareVerify the action is working by logging in to your CloudFlare account.  Then select a web site.  Then navigate to the Firewall page, and scroll down to the Access Rules table. If you received a notice from Fail2Ban that a jail configured to use the cloudflare-restv4-bearer-token action has banned an IP address, you should find it listed as “Banned by Fail2Ban” in the the Access List.

If fail2ban won’t restart after you have edited configuration files, try manually starting it with verbose messaging enabled to find out where it is failing as it is starting up.

fail2ban-client -vvv -x start

Once you have fixed all errors in your configuration files, restart it as a service.

fail2ban-client -x stop
service fail2ban start