Configure WAF custom rules
This page provides examples of creating WAF custom rules in a zone or account using Terraform. The examples cover the following scenarios:
Zone-level configurations:
Account-level configurations:
For more information on custom rules, refer to Custom rules in the Cloudflare WAF documentation.
Before you start
Obtain the necessary account or zone IDs
The Terraform configurations provided in this page need the zone ID (or account ID) of the zone/account where you will deploy rulesets.
- To retrieve the list of accounts you have access to, including their IDs, use the List accounts API operation.
- To retrieve the list of zones you have access to, including their IDs, use the List zones API operation.
Import or delete existing rulesets
Terraform assumes that it has complete control over account and zone rulesets. If you already have rulesets configured in your account or zone, do one of the following:
- Import existing rulesets to Terraform using the
cf-terraforming
tool. Recent versions of the tool can generate resource definitions for existing rulesets and import their configuration to Terraform state. - Start from scratch by deleting existing rulesets (account and zone rulesets with
"kind": "root"
and"kind": "zone"
, respectively) and then defining your rulesets configuration in Terraform.
Zone-level configurations
Add a custom rule to a zone
The following example configures a custom rule in the zone entry point ruleset for the http_request_firewall_custom
phase for zone with ID <ZONE_ID>
. The rule will block all traffic on non-standard HTTP(S) ports:
resource "cloudflare_ruleset" "zone_custom_firewall" { zone_id = "<ZONE_ID>" name = "Phase entry point ruleset for custom rules in my zone" description = "" kind = "zone" phase = "http_request_firewall_custom"
rules { action = "block" expression = "(not cf.edge.server_port in {80 443})" description = "Block ports other than 80 and 443" enabled = true }
}
Account-level configurations
Create and deploy a custom ruleset
The following example creates a custom ruleset in the account with ID <ACCOUNT_ID>
containing a single custom rule. This custom ruleset is then deployed using a separate cloudflare_ruleset
Terraform resource. If you do not deploy a custom ruleset, it will not execute.
The following configuration creates the custom ruleset with a single rule:
resource "cloudflare_ruleset" "account_firewall_custom_ruleset" { account_id = "<ACCOUNT_ID>" name = "Custom ruleset blocking traffic in non-standard HTTP(S) ports" description = "" kind = "custom" phase = "http_request_firewall_custom"
rules { action = "block" expression = "(not cf.edge.server_port in {80 443})" description = "Block ports other than 80 and 443" enabled = true }
}
The following configuration deploys the custom ruleset at the account level. It defines a dependency on the account_firewall_custom_ruleset
resource and uses the ID of the created custom ruleset in action_parameters
:
resource "cloudflare_ruleset" "account_firewall_custom_entrypoint" { account_id = "<ACCOUNT_ID>" name = "Account-level entry point ruleset for the http_request_firewall_custom phase deploying a custom ruleset" description = "" kind = "root" phase = "http_request_firewall_custom"
depends_on = [cloudflare_ruleset.account_firewall_custom_ruleset]
rules { action = "execute" action_parameters { id = cloudflare_ruleset.account_firewall_custom_ruleset.id } expression = "(cf.zone.name eq \"example.com\")" description = "Deploy custom ruleset for example.com" enabled = true }
}
For more information on configuring and deploying custom rulesets, refer to Work with custom rulesets in the Ruleset Engine documentation.
Add a custom rule checking for exposed credentials
The following configuration creates a custom ruleset with a single rule that checks for exposed credentials.
resource "cloudflare_ruleset" "account_firewall_custom_ruleset_exposed_creds" { account_id = "<ACCOUNT_ID>" name = "Custom ruleset checking for exposed credentials" description = "" kind = "custom" phase = "http_request_firewall_custom"
rules { action = "rewrite" action_parameters { headers { name = "Exposed-Credential-Check" operation = "set" value = "1" } } exposed_credential_check { username_expression = "url_decode(http.request.body.form[\"username\"][0])" password_expression = "url_decode(http.request.body.form[\"password\"][0])" } expression = "http.request.method == \"POST\" && http.request.uri == \"/login.php\"" description = "Add header when there is a rule match and exposed credentials are detected" enabled = true }
}
The following configuration deploys the custom ruleset. It defines a dependency on the account_firewall_custom_ruleset_exposed_creds
resource and obtains the ID of the created custom ruleset:
resource "cloudflare_ruleset" "account_firewall_custom_entrypoint" { account_id = "<ACCOUNT_ID>" name = "Account-level entry point ruleset for the http_request_firewall_custom phase deploying a custom ruleset checking for exposed credentials" description = "" kind = "root" phase = "http_request_firewall_custom"
depends_on = [cloudflare_ruleset.account_firewall_custom_ruleset_exposed_creds]
rules { action = "execute" action_parameters { id = cloudflare_ruleset.account_firewall_custom_ruleset_exposed_creds.id } expression = "(cf.zone.name eq \"example.com\")" description = "Deploy custom ruleset for example.com" enabled = true }
}