Manage WAAP via Terraform v0
Every WAAP policy, rule, and response page in Terraform references a domain resource by ID, so the domain is the first thing a configuration must declare before anything else can attach to it. The CDB Terraform provider v0 manages this entire chain as code, replacing the manual click-through setup in the Portal.
The following resources are supported: WAAP domains, security policies, custom rules, advanced rules, firewall rules, API paths, custom response pages, and security insight silence rules.
Provider installation and authentication are covered in the Terraform overview. WAAP resources require provider v0 with permanent_api_token authentication.
Workflow
Add resource configuration to main.tf, then run:
terraform plan
Review the output, then apply:
terraform apply
Enter yes when prompted. The getting started guide covers the full workflow.
Domains
A WAAP domain is the root resource that all other WAAP resources depend on. It is linked to a CDN resource and controls the operating mode and protection settings for a domain.
Create a WAAP domain
The following configuration creates a CDN resource with WAAP enabled and attaches a WAAP domain to it.
- 1
Open the main.tf file
- 2
Copy and customize the code
resource "gcore_cdn_resource" "example" { cname = "app.example.com" origin = "origin.example.com" options { waap { value = true } } } resource "gcore_waap_domain" "domain" { name = gcore_cdn_resource.example.cname status = "monitor" settings { ddos { global_threshold = 2000 burst_threshold = 1000 } api { api_urls = [ "https://app.example.com/api/v1", "https://app.example.com/api/v2" ] } } api_discovery_settings { description_file_location = "https://app.example.com/openapi.json" description_file_scan_enabled = true description_file_scan_interval_hours = 24 traffic_scan_enabled = true traffic_scan_interval_hours = 6 } } - 3
Configure the CDN resource
- Specify
cname— the domain name to protect. - Specify
origin— the origin server hostname or IP address. - Keep
waap { value = true }to enable WAAP on this CDN resource.
- Specify
- 4
Configure the WAAP domain
name— must match the CDN resourcecname.status— set to"monitor"to inspect traffic without blocking, or"active"to enforce protection. Start with"monitor"to validate the configuration before going live.- (optional) Add
settings.ddosto configure DDoS protection thresholds:global_threshold— maximum number of requests per second across all IPs before rate limiting applies.burst_threshold— maximum number of requests per second from a single IP before it is rate limited.
- (optional) Add
settings.apito define API endpoints:api_urls— list of URL patterns treated as API endpoints. JavaScript injection and CAPTCHA are disabled for these paths.is_api— set totrueto treat the entire domain as an API domain. If set,api_urlsis ignored.
- (optional) Add
api_discovery_settingsto enable automated API Discovery:description_file_location— URL of the OpenAPI specification file (YAML or JSON, versions 2, 3, or 3.1).description_file_scan_enabled— enable periodic scanning of the specification file.description_file_scan_interval_hours— scan interval in hours.traffic_scan_enabled— enable traffic-based API discovery.traffic_scan_interval_hours— traffic scan interval in hours.
After creation, switch
statusfrom"monitor"to"active"only after reviewing traffic in the Events page and confirming that legitimate requests are not blocked.
After a WAAP domain is created, it takes a few seconds to propagate before rules can be attached to it. When the domain and its rules are defined in the same configuration, the first terraform apply may fail rule creation with a 429 error ("domain is still being propagated"). Running terraform apply a second time resolves this — Terraform picks up where it left off and creates the remaining resources.
Security policies
Security policies are built-in rule sets that cover common attack categories, grouped into OWASP threats, IP reputation, Behavioral WAF, Bot protection, CMS protection, and API protection. Each policy can be enabled or disabled per domain.
Enable or disable security policies
The following configuration enables and disables specific security policies for a domain.
- 1
Open the main.tf file
- 2
Copy and customize the code
data "gcore_waap_domain_policy" "sql_injection" { domain_id = gcore_waap_domain.domain.id name = "SQL injection" group = "WAF" } data "gcore_waap_domain_policy" "xss" { domain_id = gcore_waap_domain.domain.id name = "Cross-site scripting (XSS)" group = "WAF" } resource "gcore_waap_policy" "domain_policies" { domain_id = gcore_waap_domain.domain.id policies = { "${data.gcore_waap_domain_policy.sql_injection.id}" = true "${data.gcore_waap_domain_policy.xss.id}" = true # Policy IDs can also be specified directly. # Retrieve the full list from the WAAP API: # GET /docs/waap/v1/domains/{domain_id}/rule-sets S3008701 = true # Shellshock exploit S3008702 = false # Remote file inclusion (RFI) - disabled } } - 3
Configure the policy resource
domain_id— the ID of the WAAP domain.policies— a map of policy IDs to boolean values.trueenables the policy,falsedisables it.- Use the
gcore_waap_domain_policydata source to look up a policy bynameandgroupinstead of hardcoding the ID. - Policy IDs are domain-specific and can be retrieved from the WAAP API:
GET /docs/waap/v1/domains/{domain_id}/rule-sets. The IDs are in therules[].idfield.
- Use the
Only policies explicitly listed in the
policiesmap are modified. Policies not listed retain their current state.
Custom rules
Custom rules match incoming requests against conditions and apply an action when all conditions are met. Up to five conditions can be combined in a single rule.
Create a custom rule
The following configuration creates a rule that blocks requests from a specific IP address.
- 1
Open the main.tf file
- 2
Copy and customize the code
resource "gcore_waap_custom_rule" "block_ip" { domain_id = gcore_waap_domain.domain.id name = "Block suspicious IP" enabled = true description = "Block requests from a known malicious IP" action { block { status_code = 403 action_duration = "24h" } } conditions { ip { ip_address = "203.0.113.42" } } } - 3
Configure the rule
domain_id— the ID of the WAAP domain.name— a descriptive name for the rule.enabled— set totrueto activate the rule immediately.- (optional)
description— additional context for the rule.
Rule names accept letters, digits, whitespace, and the characters
.:'";<>?&|. Hyphens and other symbols are rejected with a validation error. This applies tonameon custom rules, firewall rules, and advanced rules. - 4
Configure the action
block— block the request.status_code— HTTP status code returned to the client. Must be one of:403,405,418,429. Default:403.action_duration— how long subsequent requests from the same source remain blocked. Format: a number followed bys(seconds),m(minutes),h(hours), ord(days). Example:12h.
allow = true— allow the request, bypassing all other security checks.captcha = true— present a CAPTCHA challenge.handshake = true— perform automatic browser validation (JS challenge).monitor = true— log the request without taking action.tag— a block that attaches user-defined tags to the request for analytics:tag { tags = ["tag-name"] }.
blockandtagare blocks;allow,captcha,handshake, andmonitorare plain boolean fields. - 5
Configure conditions
At least one condition is required, up to five total. All conditions must match for the rule to trigger.
Available condition types:
Condition Key field(s) Match types ipip_addressexact ip_rangelower_bound,upper_boundrange urlurlExact,Contains,Regexuser_agentuser_agentExact,Containsheaderheader,valueExact,Containsheader_existsheaderpresence check http_methodhttp_methodone of CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACEcountrycountry_codeISO 3166-1 alpha-2 list content_typecontent_typelist file_extensionfile_extensionlist organizationorganizationexact owner_typesowner_typeslist of COMMERCIAL,EDUCATIONAL,GOVERNMENT,HOSTING_SERVICES,ISP,MOBILE_NETWORK,NETWORK,RESERVEDrequest_ratepath_pattern,requests,timerate threshold session_request_countrequest_countcount threshold tagstagssystem tag IDs user_defined_tagstagsuser-defined tag names All conditions support an optional
negation = truefield to invert the match.When multiple custom rules match the same request, the action with the highest priority takes effect. Priority order (highest first): Allow > Block > Captcha > JS Challenge > Monitor. Tag actions run first and do not stop rule evaluation.
Create a rate-limiting rule
The following configuration blocks requests from a single IP that exceed a rate threshold — the same protection configurable as an advanced rate limiting rule in the CDB Technical Web Portal.
- 1
Open the main.tf file
- 2
Copy and customize the code
resource "gcore_waap_custom_rule" "rate_limit" { domain_id = gcore_waap_domain.domain.id name = "Rate limit API endpoint" enabled = true action { block { status_code = 429 action_duration = "10m" } } conditions { request_rate { path_pattern = "^/api/" requests = 100 time = 60 http_methods = ["GET", "POST"] } } } - 3
Configure the request_rate condition
path_pattern— a regular expression matching the URL path.requests— number of requests within the time window that triggers the condition.time— time window in seconds.- (optional)
http_methods— restrict the rate check to specific HTTP methods. - (optional)
ips— restrict the rate check to specific source IPs. - (optional)
user_defined_tag— restrict the rate check to requests carrying a specific user-defined tag.
Match requests by tag
Tags in the tags condition are system-assigned IDs, one per detected attack category — xss for cross-site scripting, sql for SQL injection. Resolve an ID from its display name with the gcore_waap_tag data source instead of hardcoding it:
data "gcore_waap_tag" "xss" {
name = "Cross-Site Scripting"
}
resource "gcore_waap_custom_rule" "tag_monitor" {
domain_id = gcore_waap_domain.domain.id
name = "Monitor XSS attempts"
enabled = true
action {
monitor = true
}
conditions {
tags {
tags = [data.gcore_waap_tag.xss.id]
}
}
}
user_defined_tags works the same way but takes the literal tag names created by tag rules — no data source lookup is needed.
Advanced rules
Advanced rules require an Enterprise WAAP plan — accounts without this feature receive a 401 error from the API. Contact CDB support to upgrade.
Advanced rules use an expression language to match requests with greater precision than custom rules. The expression is evaluated per request, and the rule fires when it returns true.
Create an advanced rule
The following configuration blocks requests from a specific IP in the access phase.
- 1
Open the main.tf file
- 2
Copy and customize the code
resource "gcore_waap_advanced_rule" "block_expression" { domain_id = gcore_waap_domain.domain.id name = "Block specific IP in access phase" enabled = true description = "Block a known bad actor by IP" phase = "access" source = "request.ip == '203.0.113.99'" action { block { status_code = 403 action_duration = "1h" } } } - 3
Configure the rule
source— the match expression. Uses request object fields —request.ip,request.uri,request.method,request.headers['name'], and more listed in the advanced rule objects reference, which also covers the full expression syntax.phase— the processing phase. Use"access"to evaluate the rule before the request is forwarded to the origin.action— same action types as custom rules:block,allow,captcha,handshake,monitor,tag.
Firewall rules
Firewall rules are IP-focused access controls, equivalent to the IP allowlist and blocklist in the Customer Portal. They support a single condition per rule and are simpler than custom rules.
Create a firewall rule
The following configuration blocks a range of IP addresses.
- 1
Open the main.tf file
- 2
Copy and customize the code
resource "gcore_waap_firewall_rule" "block_ip_range" { domain_id = gcore_waap_domain.domain.id name = "Block IP range" description = "Block a suspicious IP range" enabled = true action { block { status_code = 403 action_duration = "12h" } } conditions { ip_range { lower_bound = "192.168.1.1" upper_bound = "192.168.1.254" } } } - 3
Configure the rule
domain_id— the ID of the WAAP domain.name— a descriptive name.enabled— set totrueto activate the rule.action—blockorallow. Supports the samestatus_codeandaction_durationfields as custom rules.conditions— one condition block. Supportsip,ip_range,country,url,user_agent,header,header_exists,http_method, andrequest_rate. All condition types supportnegation = true.
Use firewall rules for simple IP allowlists and blocklists. For multi-condition logic or rate limiting, use custom rules instead.
API paths
API paths and API Discovery require an Enterprise WAAP plan, same as advanced rules.
API paths define individual endpoints for the API Discovery and Protection feature. Defining paths explicitly lets WAAP enforce API-specific protection independently of traffic-based discovery.
Add an API path
The following configuration registers a single API endpoint for a domain.
- 1
Open the main.tf file
- 2
Copy and customize the code
resource "gcore_waap_api_path" "get_items" { domain_id = gcore_waap_domain.domain.id path = "/v1/items/{id}" method = "GET" http_scheme = "HTTPS" api_version = "v1" tags = ["catalog", "public"] api_groups = ["items"] } - 3
Configure the API path
domain_id— the ID of the WAAP domain.path— the API path:/v1/items/{id}uses curly braces to mark a dynamic segment.method— the HTTP method. Must be one of:GET,POST,PUT,PATCH,DELETE,TRACE,HEAD,OPTIONS.http_scheme— the scheme. Must beHTTPorHTTPS.- (optional)
api_version— a version label for the endpoint:"v1". - (optional)
tags— a list of tags to associate with the path. - (optional)
api_groups— a list of API group names to associate with the path. - (optional)
status— the discovery status. Must be one of:CONFIRMED_API,POTENTIAL_API,NOT_API,DELISTED_API. UseCONFIRMED_APIfor manually defined paths.
Custom response pages
Custom response pages replace the default WAAP challenge and block pages with branded content. A page set is created once and assigned to one or more domains.
Create a custom page set
The following configuration creates a page set with custom block and CAPTCHA pages.
- 1
Open the main.tf file
- 2
Copy and customize the code
resource "gcore_waap_custom_page_set" "pages" { name = "brand-pages" domains = [gcore_waap_domain.domain.id] block { enabled = true header = "Access Denied" title = "This request has been blocked" text = "If you believe this is a mistake, contact support." } captcha { enabled = true header = "Security Check" title = "Please confirm you are human" text = "Complete the challenge below to continue." error = "CAPTCHA validation failed. Please try again." } handshake { enabled = true header = "Verifying your browser" title = "Please wait..." } } - 3
Configure the page set
name— a unique name for the page set.domains— a list of WAAP domain IDs to assign this page set to.
- 4
Configure page types
Each type is an optional block with
enabled = trueto activate it:Block Displayed when blockA request is blocked by a rule block_csrfA request is blocked due to a CSRF violation captchaA CAPTCHA challenge is presented cookie_disabledCookies are disabled in the client browser handshakeA JS challenge (browser validation) is presented javascript_disabledJavaScript is disabled in the client browser Each page block supports:
header— text displayed in the page header.title— text displayed as the page title (not available forcookie_disabledandjavascript_disabled). Minimum three characters.text— body text. Minimum 20 characters.logo— base64-encoded image (data:image/png;base64,...). Supported formats: JPEG, PNG, JPG. Maximum dimensions: 450 x 130 px.error— error message text (available forcaptchaonly).
Security insight silence rules
Security Insights require an Enterprise WAAP plan, same as advanced rules.
Security insights are alerts generated when WAAP detects configuration issues or patterns that indicate a security risk. Silence rules suppress specific insight types to reduce noise for known or accepted conditions — the same rules shown on the Silence rules tab in the Customer Portal.
If terraform apply reports an error, the WAAP API may already have created the silence. Confirm the rule in the Customer Portal or the Security insights API.
Create a silence rule
The following configuration silences a specific insight type for a limited period.
- 1
Open the main.tf file
- 2
Copy and customize the code
data "gcore_waap_security_insight_type" "attack_on_disabled_policy" { name = "Attack on disabled policy" } resource "gcore_waap_security_insight_silence" "silence" { domain_id = gcore_waap_domain.domain.id insight_type = data.gcore_waap_security_insight_type.attack_on_disabled_policy.id comment = "Policy disabled intentionally for testing" author = "ops-team" labels = { policy_name = "SQL injection" } expire_at = "2026-12-31T00:00:00Z" } - 3
Configure the silence rule
domain_id— the ID of the WAAP domain.insight_type— the slug of the insight type to silence. Use thegcore_waap_security_insight_typedata source to look up a type by name, or retrieve the slug from the WAAP API at/v1/security-insights/types.labels— must use the exact label keys the insight type declares, not arbitrary key-value pairs.attack-on-disabled-policydeclarespolicy_id,attack_type, andpolicy_name;allowed-high-risk-ipdeclaresip,rule_id, andrule_name. The insight type's declared labels are listed alongside itsslugat/v1/security-insights/types.comment— a description of why the insight is being silenced.author— the name or identifier of the person or team creating the silence.- (optional)
expire_at— the date and time when the silence expires, in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ). If omitted, the silence does not expire.