I'm trying to setup Cloudflare blocking with fail2ban using this guide and while it bans the IP correctly, fail2ban cannot unban the IP from Cloudflare either after timeout or with manual shell command. It always returns 400 Bad Request error.

Am I doing something wrong?

Here is the relevant cURL command:

actionunban = curl -s -X DELETE "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$( \
          curl -s -X GET "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration_target=ip&configuration_value=<ip>&page=1&per_page=1&match=all" \
          -H "X-Auth-Email: <cfuser>" \
          -H "X-Auth-Key: <cftoken>" \
          -H "Content-Type: application/json" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'id'\042/){print $(i+1)}}}' | tr -d '"' | head -n 1)" \
          -H "X-Auth-Email: <cfuser>" \
          -H "X-Auth-Key: <cftoken>" \
          -H "Content-Type: application/json"

Update: I can run the above mentioned nested cURL commands separately and they return correct responses.

  • Without looking closely, I use the same script, and mine seems to unban - at least I assume it does, because I see bans regularly but CloudFlare has only a small number of IPs blocked any time I look. – Tim May 6 at 9:17
  • There's nothing in the log except the 400 Bad Request error. I had doubt about the cURL command. Can you diff it against yours? – Vinit Wankhede May 6 at 9:27
  • Which log are you meaning I'll do that in about 12 hours when I have access to that server. I did things as I wrote in this article, which is an attributed copy of that original article you used. – Tim May 6 at 19:46
  • The fail2ban DEBUG log. – Vinit Wankhede May 7 at 6:47
  • I have fail2ban debug log working now. Waiting for the unban command to come up in a day or two. – Tim May 15 at 8:13
up vote 1 down vote accepted

Looks like Cloudflare has adjusted their API to return formatted JSON (previously the returned JSON was on a single line). You should be able to fix the problem by stripping the new line characters before piping to the awk command:

actionunban = curl -s -X DELETE "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$( \
      curl -s -X GET "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration_target=ip&configuration_value=<ip>&page=1&per_page=1&match=all" \
      -H "X-Auth-Email: <cfuser>" \
      -H "X-Auth-Key: <cftoken>" \
      -H "Content-Type: application/json" | tr -d '\n' | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'id'\042/){print $(i+1)}}}' | tr -d '"' | head -n 1)" \
      -H "X-Auth-Email: <cfuser>" \
      -H "X-Auth-Key: <cftoken>" \
      -H "Content-Type: application/json"

You also might want to swap to the new Cloudflare filters that ships with Fail2ban 0.10, as it is much more succinct:

actionban = curl -s -o /dev/null -X POST -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' \
        -H 'Content-Type: application/json' -d '{ "mode": "block", "configuration": { "target": "ip", "value": "<ip>" } }' \
        https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules

actionunban = curl -s -o /dev/null -X DELETE -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' \
          https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$(curl -s -X GET -H 'X-Auth-Email: <cfuser>' -H 'X-Auth-Key: <cftoken>' \
          'https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration_target=ip&configuration_value=<ip>&page=1&per_page=1' | tr -d '\n' | cut -d'"' -f6)

I think has problems with API's Cloudflare. I have try split out command unban IP as following:

id_CF=`curl -s -X GET "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration_target=ip&configuration_value=xxx.xxx.xxx.xxx&page=1&per_page=1&match=all" \
            -H "X-Auth-Email: <My's Email>" \
            -H "X-Auth-Key: <My's Token Cloudflare>" \
            -H "Content-Type: application/json" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'id'\042/){print $(i+1);}}}' | tr -d '"' | head -n 1`


curl -s -X DELETE "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$id_CF \
            -H "X-Auth-Email: <My's Email>" \
            -H "X-Auth-Key: <My's Token Cloudflare>" \
            -H "Content-Type: application/json"

Resuit is: When run command second position then returns 400 Bad Request error, but when I get value of the variable $id_CF, suppose is the following id string: 71eceb59a7b846c6bd2b2692ab438e86 and run command second, again. Then it success.

curl -s -X DELETE "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/71eceb59a7b846c6bd2b2692ab438e86" \
            -H "X-Auth-Email: <My's Email>" \
            -H "X-Auth-Key: <My's Token Cloudflare>" \
            -H "Content-Type: application/json"

{ "result": null, "success": true, "errors": null, "messages": null }

I really don't know where does problem exactly come from, but previously, when I used the old API, it was not.

Tks Jake Jackson, You are correct!

I was test by stripping string the first blank line of ID and successed. Currently, I add the sed command to the pipe in the following command:

actionunban = curl -s -X DELETE "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$( \ curl -s -X GET "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration_target=ip&configuration_value=27.78.160.247&page=1&per_page=1&match=all" \ -H "X-Auth-Email: <My's Email>" \ -H "X-Auth-Key: <My's Token Cloudflare>" \ -H "Content-Type: application/json" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'id'\042/){print $(i+1);}}}' | tr -d '"' | sed -e 's/^[ \t]*//' | head -n 1)" \ -H "X-Auth-Email: <My's Email>" \ -H "X-Auth-Key: <My's Token Cloudflare>" \ -H "Content-Type: application/json"

I hope will help everybody!

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.