Updated Cloudflare action for fail2ban that bans IPs on a zone

#
#
# - bans IPs on a particular cloudfare zone
# - uses API Bearer token instead of the legacy Global API Key auth method
#
# IMPORTANT
#
# Please set jail.local's permission to 640 because it contains your CF API token
# if you pass it as an argument to the action, or set this file's permission to 640
# if you set the token here.
#
# This action depends on curl (and optionally jq).
# Referenced from http://www.normyee.net/blog/2012/02/02/adding-cloudflare-support-to-fail2ban by NORM YEE
#
# To get your CloudFlare API Key: https://www.cloudflare.com/a/account/my-account
#
# CloudFlare API error codes: https://www.cloudflare.com/docs/host-api.html#s4.2

[Definition]

# Option:  actionstart
# Notes.:  command executed on demand at the first ban (or at the start of Fail2Ban if actionstart_on_demand is set to false).
# Values:  CMD
#
actionstart =

# Option:  actionstop
# Notes.:  command executed at the stop of jail (or at the end of Fail2Ban)
# Values:  CMD
#
actionstop =

# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#
actioncheck =

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#
# API v1
#actionban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=ban' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
# API v4
actionban = curl -s -f -X POST <_cf_api_prms> \
            -d '{"mode":"block","configuration":{"target":"ip","value":"<ip>"},"notes":"Fail2Ban <name>"}' \
            <_cf_api_url>
            ok=$?
            if [ $ok -ne 0 ]; then 
                echo "Failed to ban <ip> on cloudfare"; exit 0;
            fi


# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#
# API v1
#actionunban = curl -s -o /dev/null https://www.cloudflare.com/api_json.html -d 'a=nul' -d 'tkn=<cftoken>' -d 'email=<cfuser>' -d 'key=<ip>'
# API v4
actionunban = id=$(curl -s -X GET <_cf_api_prms> \
                   "<_cf_api_url>?mode=block&configuration_target=ip&configuration_value=<ip>&page=1&per_page=1&notes=Fail2Ban%%20<name>" \
                   | { jq -r '.result[0].id' 2>/dev/null || tr -d '\n' | sed -nE 's/^.*"result"\s*:\s*\[\s*\{\s*"id"\s*:\s*"([^"]+)".*$/\1/p'; })
              if [ -z "$id" ]; then echo "<name>: id for <ip> cannot be found"; exit 0; fi;
              curl -s -o /dev/null -X DELETE <_cf_api_prms> "<_cf_api_url>/$id"

_cf_api_url = https://api.cloudflare.com/client/v4/zones/<cfzone>/firewall/access_rules/rules
_cf_api_prms = -H 'Authorization: Bearer <cftoken>' -H 'Content-Type: application/json'

[Init]

# If you like to use this action with mailing whois lines, you could use the composite action
# action_cf_mwl predefined in jail.conf, just define in your jail:
#
# action = %(action_cf_mwl)s
# # Your CF account e-mail
# cfemail  = 
# # Your CF API Key
# cfapikey = 

cftoken =

cfzone =

solution for IPv6

#
# 1. REQUIRES jp TO BE INSTALLED IN THE USER PATH! Install it here:
#    https://github.com/jmespath/jp
#
# 2. ! IMPORTANT ! Set jail.local's permission to 640 because it contains one of
#    your CF API tokens. Grab your account id, your api token, and your "hostile
#    ip" list id before continuing (see end of file for details).
#
# 3. Create a new custom list. Name it known_hostile_ips.
#    https://developers.cloudflare.com/waf/tools/lists
#
# 4. Ensure every zone you want fail2ban to protect has an enabled WAF ban rule
#    referencing $known_hostile_ips.
#    https://developers.cloudflare.com/waf/custom-rules
#
# 5. Use the fail2ban CLI and the Cloudflare dashboard/Traces to test and make
#    sure everything is working properly. You may need to add a permission to
#    your api token to ensure proper function. See the end of this file for
#    details.
#
# To get your CloudFlare API Key:
# https://www.cloudflare.com/a/account/my-account
#
# CloudFlare API error codes: https://www.cloudflare.com/docs/host-api.html#s4.2
#
# Note that if you're using Nginx, Apache, Litespeed, etc, you need to modify
# your logs and/or your filters such that the real client IP is being captured
# and not Cloudflare's IPs.

[Definition]

# Option:  actionstart
# Notes.:  command executed on demand at the first ban (or at the start of
#          Fail2Ban if actionstart_on_demand is set to false).
# Values:  CMD
#
actionstart =

# Option:  actionstop
# Notes.:  command executed at the stop of jail (or at the end of Fail2Ban)
# Values:  CMD
#
actionstop =

# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#
actioncheck =

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#
# API v4 WAF
actionban = curl -s -o /dev/null -X POST <_cf_api_prms> \
                 -d '[{"ip":"'"<cfip>"'","comment":"Created by fail2ban <name>"}]' \
                 <_cf_api_url>

# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#
# API v4 WAF
actionunban = id=$(curl -s -X GET <_cf_api_prms> \
                   "<_cf_api_url>?search=<cfip>&per_page=1" \
                   | { jp --unquoted 'result[0].id | not_null(@, `""`)' 2>/dev/null; })
              if [ -z "$id" ]; then echo "<name>: id for <ip> cannot be found"; exit 0; fi;
              curl -s -o /dev/null -X DELETE <_cf_api_prms> \
                   -d '{"items":[{"id":"'"$id"'"}]}' \
                   <_cf_api_url>

_cf_api_url = https://api.cloudflare.com/client/v4/accounts/<cfaccountid>/rules/lists/<cfbanlistid>/items
_cf_api_prms = -H 'Authorization: bearer <cfapitoken>' -H 'Content-Type: application/json'

[Init]

# If you like to use this action with mailing whois lines, you could use the
# composite action action_cf_mwl predefined in jail.conf, just define in your
# jail:
#
# action = %(action_cf_mwl)
# # Your CF API Key
# cfapitoken =
# cfaccountid =
# cfbanlistid =

# Your Cloudflare User API Token. It will need "EDIT" level on the "Account
# Filter Lists" permission.
# https://dash.cloudflare.com/profile/api-tokens
cfapitoken = 

# The identifier of the Cloudflare account used to update the hostile IP list.
cfaccountid = 

# Your Cloudflare WAF "hostile ip" List id. You can find it using the API or or
# following the instructions here:
# https://community.cloudflare.com/t/what-token-permissions-for-ip-list-edits/525222/6
# https://api.cloudflare.com/client/v4/accounts/:cfaccountid/rules/lists
#
# Note that even free CF accounts get 1 free list with 10,000 slots, yay!
# https://dash.cloudflare.com/:cfaccountid/configurations/lists
cfbanlistid = 

cfip = <ip>

[Init?family=inet6]

cfip = $(fail2ban-python -c 'import sys; from fail2ban.server.ipdns import IPAddr; a = IPAddr(sys.argv[1]+"/"+sys.argv[2]); print(str(a))' "<ip>" 64)

For those who are using a docker-compose. This is how I installed jp as the instructions require. Add this to your docker-compose.yaml file underneath the fail2ban container.

     -command: /bin/sh -c "apk add --update jp && fail2ban-server -f"