Cloudflare Docs
Terraform
Visit Terraform on GitHub
Set theme to dark (⇧+D)

Configure WAF Managed Rulesets

This page provides examples of deploying and configuring WAF Managed Rulesets in your zone or account using Terraform. It covers the following configurations:

For more information on WAF Managed Rulesets, refer to Managed Rulesets in the Cloudflare WAF documentation. For more information on deploying and configuring rulesets using the Rulesets API, refer to Work with Managed Rulesets in the Ruleset Engine documentation.

Before you start

Delete any existing rulesets before using Terraform

Terraform assumes that it has complete control over account and zone rulesets. Before you can start configuring your account and zone using Terraform, you must delete existing rulesets (any ruleset with kind: root or kind: zone at the account and zone level, respectively), and then recreate them using Terraform.

Cloudflare recommends that you delete rulesets in both scopes (account and zone) so that you can manage them all using Terraform. However, you can also manage rulesets for only one of the scopes: account or zone. In this case, delete the rulesets in the desired scope before you start managing rulesets using Terraform.

To find existing entry point rulesets, use the API operations described in List existing rulesets, for the account and zone levels. To delete existing rulesets, use the API operations described in Delete ruleset, for the account and zone levels.

Obtain the necessary account, zone, and Managed Ruleset IDs

The Terraform configurations provided in this page need the zone ID (or account ID) of the zone/account where you will deploy WAF Managed Rulesets.

The deployment of WAF Managed Rulesets via Terraform requires that you use the ruleset IDs. To find the IDs of WAF Managed Rulesets, use the List account rulesets API operation. The response will include the description and IDs of the existing WAF Managed Rulesets.

Deploy WAF Managed Rulesets

The following example deploys two WAF Managed Rulesets to a zone using Terraform, using a cloudflare_ruleset resource with two rules that execute the Managed Rulesets.

# Configure a ruleset at the zone level for the "http_request_firewall_managed" phase
resource "cloudflare_ruleset" "zone_level_managed_waf" {
zone_id = "<ZONE_ID>"
name = "Managed WAF entry point ruleset"
description = ""
kind = "zone"
phase = "http_request_firewall_managed"
# Execute Cloudflare Managed Ruleset
rules {
action = "execute"
action_parameters {
id = "efb7b8c949ac4650a09736fc376e9aee"
version = "latest"
}
expression = "true"
description = "Execute Cloudflare Managed Ruleset on my zone-level phase entry point ruleset"
enabled = true
}
# Execute Cloudflare OWASP Core Ruleset
rules {
action = "execute"
action_parameters {
id = "4814384a9e5d4991b9815dcfc25d2f1f"
version = "latest"
}
expression = "true"
description = "Execute Cloudflare OWASP Core Ruleset on my zone-level phase entry point ruleset"
enabled = true
}
}

Configure skip rules

The following example adds two skip rules (or WAF exceptions) for the Cloudflare Managed Ruleset:

  • The first rule will skip the execution of the entire Cloudflare Managed Ruleset (with ID efb7b8c949ac4650a09736fc376e9aee) for specific URLs, according to the rule expression.
  • The second rule will skip the execution of two rules belonging to the Cloudflare Managed Ruleset for specific URLs, according to the rule expression.

Add the two skip rules to the cloudflare_ruleset resource before the rule that deploys the Cloudflare Managed Ruleset:

# Skip execution of the entire Cloudflare Managed Ruleset for specific URLs
rules {
action = "skip"
action_parameters {
rulesets = ["efb7b8c949ac4650a09736fc376e9aee"]
}
expression = "(cf.zone.name eq \"example.com\" and http.request.uri.query contains \"skip=rulesets\")"
description = "Skip Cloudflare Manage ruleset"
enabled = true
}
# Skip execution of two rules in the Cloudflare Managed Ruleset for specific URLs
rules {
action = "skip"
action_parameters {
rules = {
# Format: "<RULESET_ID>" = "<RULE_ID_1>,<RULE_ID_2>,..."
"efb7b8c949ac4650a09736fc376e9aee" = "5de7edfa648c4d6891dc3e7f84534ffa,e3a567afc347477d9702d9047e97d760"
}
}
expression = "(cf.zone.name eq \"example.com\" and http.request.uri.query contains \"skip=rules\")"
description = "Skip WordPress and SQLi rules"
enabled = true
}
# Execute Cloudflare Managed Ruleset
rules {
action = "execute"
action_parameters {
id = "efb7b8c949ac4650a09736fc376e9aee"
version = "latest"
}
expression = "true"
description = "Execute Cloudflare Managed Ruleset on my zone-level phase entry point ruleset"
enabled = true
}
# (...)
}

Configure overrides

The following example adds three overrides for the Cloudflare Managed Ruleset:

  • A rule override for rule with ID 5de7edfa648c4d6891dc3e7f84534ffa setting the action to log.
  • A rule override for rule with ID 75a0060762034a6cb663fd51a02344cb disabling the rule.
  • A tag override for the wordpress tag, setting the action of all the rules with this tag to js_challenge.

The following configuration includes the three overrides in the rule that executes the Cloudflare Managed Ruleset:

# Execute Cloudflare Managed Ruleset
rules {
action = "execute"
action_parameters {
id = "efb7b8c949ac4650a09736fc376e9aee"
version = "latest"
overrides {
rules {
id = "5de7edfa648c4d6891dc3e7f84534ffa"
action = "log"
status = "enabled"
}
rules {
id = "75a0060762034a6cb663fd51a02344cb"
status = "disabled"
}
categories {
category = "wordpress"
action = "js_challenge"
status = "enabled"
}
}
}
expression = "true"
description = "Execute Cloudflare Managed Ruleset on my zone-level phase entry point ruleset"
enabled = true
}
}

Configure payload logging

This example enables payload logging for matched rules of the Cloudflare Managed Ruleset, setting the public key used to encrypt the logged payload.

Building upon the rule that deploys the Cloudflare Managed Ruleset, the following configuration adds the matched_data object with the public key used to encrypt the payload:

# Execute Cloudflare Managed Ruleset
rules {
action = "execute"
action_parameters {
id = "efb7b8c949ac4650a09736fc376e9aee"
version = "latest"
matched_data {
public_key = "Ycig/Zr/pZmklmFUN99nr+taURlYItL91g+NcHGYpB8="
}
}
expression = "true"
description = "Execute Cloudflare Managed Ruleset on my zone-level phase entry point ruleset"
enabled = true
}
}

Configure the OWASP paranoia level, score threshold, and action

The OWASP Managed Ruleset supports the following configurations:

  • Enable all the rules up to a specific paranoia level by creating tag overrides that disable all the rules associated with higher paranoia levels.

  • Set the action to perform when the calculated threat score is greater than the score threshold by creating a rule override for the last rule in the Cloudflare OWASP Core Ruleset (rule with ID 6179ae15870a4bb7b2d480d4843b323c), and including the action property.

  • Set the score threshold by creating a rule override for the last rule in the Cloudflare OWASP Core Ruleset (rule with ID 6179ae15870a4bb7b2d480d4843b323c), and including the score_threshold property.

For more information on the available configuration values, refer to the Cloudflare OWASP Core Ruleset page in the WAF documentation.

The following example rule of a cloudflare_ruleset Terraform resource performs the following configuration:

  • Deploys the OWASP Managed Ruleset.
  • Sets the OWASP paranoia level to PL2.
  • Sets the score threshold to 60 (Low).
  • Sets the ruleset action to log.
    # Execute Cloudflare OWASP Core Ruleset
    rules {
    action = "execute"
    action_parameters {
    id = "4814384a9e5d4991b9815dcfc25d2f1f"
    overrides {
    # By default, all PL1 to PL4 rules are enabled.
    # Set the paranoia level to PL2 by disabling rules with
    # tags "paranoia-level-3" and "paranoia-level-4".
    categories {
    category = "paranoia-level-3"
    status = "disabled"
    }
    categories {
    category = "paranoia-level-4"
    status = "disabled"
    }
    rules {
    id = "6179ae15870a4bb7b2d480d4843b323c"
    action = "log"
    score_threshold = 60
    }
    }
    }
    expression = "true"
    description = "zone"
    enabled = true
    }