What is the proper CURL command to enable & disable custom waf rule?

I’m trying to follow this guide:

But I can’t make it enable or disable my existing custom firewall rule.
I’m running it via a bash script with an API key.

I’m not sure what options I need to put inside the
--data '{}'

What I mean by custom WAF rules are those that are located at /domain.com/security/waf/custom-rules.

The endpoint you’re trying is for the old Firewall Rules.

You need to:

List your zone rulesets to get the ID of your Custom Rules ruleset:

CF_Zone_ID='your Zone ID'
CF_Email='[email protected]'
CF_API_Key='your API Key'
curl --request GET --url https://api.cloudflare.com/client/v4/zones/$CF_Zone_ID/rulesets --header 'X-Auth-Key: '$CF_API_Key --header 'X-Auth-Email: '$CF_Email --header 'Content-Type: application/json'

Get that ruleset to check the IDs of the rules you want to enable or disable:

Ruleset_ID='the http_request_firewall_custom ruleset ID you got from previous request'
curl --request GET --url https://api.cloudflare.com/client/v4/zones/$CF_Zone_ID/rulesets/$Ruleset_ID --header 'Content-Type: application/json' --header 'X-Auth-Email: '"$CF_Email" --header 'X-Auth-Key: '"$CF_API_Key"

With the rule ID(s) at hand, only then you update it by providing both the “id” and “enabled” fields within --data {}: You need to provide the action, description, enabled, and id fields. So it may be a good idea to get the list details in your script before you update it to avoid unnecessary errors.

EDITED: several fields are needed to update a ruleset rule using this endpoint.

2 Likes

Thanks for the guide.
I got the same ID as visiting the firewall rule edit on my browser.

So the --data values would be?
–data { “id”: “xxxxx”, “enabled”: true}

2 Likes

What other issues do I have with my CURL command?

CLOUDFLARE_ZONE_ID='xx'
CLOUDFLARE_API_KEY='xx'
CLOUDFLARE_WAF_RULE_ID='xx'
curl --request PUT \
--url "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/firewall/rules" \
    --header "Authorization: Bearer $CLOUDFLARE_API_KEY" \
    --header "Content-Type: application/json" \
    --data '{"id": "$CLOUDFLARE_WAF_RULE_ID", "enabled": true}'

What I got with that command is this:

{
  "result": null,
  "success": false,
  "errors": [
    {
      "code": 10014,
      "message": "firewallrules.api.malformed_request_body"
    }
  ],
  "messages": []
}

In my environment (Ubuntu under Windows WSL) I’d need to unwrap the variable from the single quote and wrap them in double quotes. You’d need to try what variation works for you.

--data '{"id": "'"$CLOUDFLARE_WAF_RULE_ID"'", "enabled": true}'
1 Like

I still got this output:

{
  "result": null,
  "success": false,
  "errors": [
    {
      "code": 20015,
      "message": "'' is not a valid value for action because the action is required to create or update a rule",
      "source": {
        "pointer": "/rules/5/action"
      }
    },
    {
      "code": 20125,
      "message": "'' is not a valid value for expression because the expression cannot be blank",
      "source": {
        "pointer": "/rules/5/expression"
      }
    }
  ],
  "messages": null
}

The endpoint I linked to, to update a rule, uses PATCH, not PUT.

1 Like

I’m sorry, it seems I’ve given the wrong advice here, based on past experience with other endpoints.

Despite the documentation mentioning only the enabled field as required in the body of the request, this endpoint will not properly update a rule unless you also provide action, expression, description, and id (actually, when trying without the description the rule was updated with a blank title, despite it being required in the UI.)

So the proper way to do the last step would be:

Rule_ID='your rule ID'
curl --request PATCH --url https://api.cloudflare.com/client/v4/zones/$CF_Zone_ID/rulesets/$Ruleset_ID/rules/$Rule_ID --header 'Content-Type: application/json' --header 'X-Auth-Email: '"$CF_Email" --header 'X-Auth-Key: '"$CF_API_Key" 
--data '{
  "action": "block",
  "description": "testing the api",
  "enabled": true,
  "expression": "(ip.geoip.country eq \"XX\" and not (http.user_agent contains \"blah blah\"))",
  "id": "'"$Rule_ID"'"
}'

It would be advisable to get the rule’s details in your script before updating it via this endpoint, to make sure it’s always enabled/disabled while maintaining the latest version in case some modification was done to the rule in the Dashboard.

1 Like