Protect a site with a WAF firewall and rulesets
You create a firewall and a reusable ruleset, bind the ruleset and enable the firewall on a group.
Note: Balancer is in beta. Endpoints, fields and behavior may change before general availability, and the feature must be enabled on your account before use. Contact your NetActuate account manager to request access.
Prerequisites
- Balancer access and a key in the BALANCER_API_KEY environment variable with permission for these operations.
- The API host shown in the Balancer portal. Replace
<your-api-host>in every example with that host. - A shell, curl and Python 3 for examples that build JSON from environment variables.
- An HTTP group and a ruleset whose rules you have reviewed. Set WAF_RULESET_ID to that ruleset identifier.
Resource identifiers come from your API responses. Set each variable when its value is available, before the first step that uses it.
| Environment variable | Value |
|---|---|
GROUP_ID | Returned resource identifier, or your selected scope identifier. |
HTTP_LB_ID | Returned resource identifier, or your selected scope identifier. |
WAF_FIREWALL_ID | Returned resource identifier, or your selected scope identifier. |
WAF_RULESET_ID | Returned resource identifier, or your selected scope identifier. |
For an identifier, enter the value returned for your resource and export it:
read -r -p 'GROUP_ID: ' GROUP_ID
export GROUP_ID
Steps
-
Create a firewall.
curl --silent --show-error --include \
--request POST \
--url "https://<your-api-host>/waf-firewalls" \
--header "x-api-key: $BALANCER_API_KEY" \
--header "Content-Type: application/json" \
--data-binary '{"name":"site_firewall","crsSensitivity":"medium"}'API reference: Create a new WAF firewall
Expected: HTTP 200. The following shows the response fields to check; additional data depends on your saved resources. Resource identifiers are illustrative.
{
"code": 200,
"result": "success",
"data": {
"wafFirewallId": 1
}
}Set WAF_FIREWALL_ID to the returned wafFirewallId.
-
Create a ruleset if you do not have one.
curl --silent --show-error --include \
--request POST \
--url "https://<your-api-host>/waf-rulesets" \
--header "x-api-key: $BALANCER_API_KEY" \
--header "Content-Type: application/json" \
--data-binary '{"name":"site_rules"}'API reference: Create a new private WAF ruleset
Expected: HTTP 200. The following shows the response fields to check; additional data depends on your saved resources. Resource identifiers are illustrative.
{
"code": 200,
"result": "success",
"data": {
"wafRulesetId": 1
}
}Set WAF_RULESET_ID to the returned wafRulesetId. A new ruleset needs rules before it adds custom inspection.
-
Read and review your rules.
curl --silent --show-error --include \
--request GET \
--url "https://<your-api-host>/waf-rulesets/$WAF_RULESET_ID/rules" \
--header "x-api-key: $BALANCER_API_KEY"API reference: Returns the ordered list of readable WAF rules inside one ruleset
Expected: HTTP 200. The following shows the response fields to check; additional data depends on your saved resources. Resource identifiers are illustrative.
{
"code": 200,
"result": "success"
}TODO(verify): A complete custom WAF expression rule with its accepted action object.
-
Bind the reviewed ruleset to the firewall.
python3 - <<'PYBODY' | \
curl --silent --show-error --include \
--request POST \
--url "https://<your-api-host>/waf-firewalls/$WAF_FIREWALL_ID/rulesets" \
--header "x-api-key: $BALANCER_API_KEY" \
--header "Content-Type: application/json" \
--data-binary @-
import json, os
print(json.dumps({'priority': 10, 'wafRulesetId': int(os.environ['WAF_RULESET_ID'])}))
PYBODYAPI reference: Create an ordered ruleset binding inside a WAF firewall
Expected: HTTP 200. The following shows the response fields to check; additional data depends on your saved resources. Resource identifiers are illustrative.
{
"code": 200,
"result": "success",
"data": {
"wafRulesetBindingId": 1
}
} -
Observe matches in shadow mode.
python3 - <<'PYBODY' | \
curl --silent --show-error --include \
--request PATCH \
--url "https://<your-api-host>/http-loadbalancers/$HTTP_LB_ID/groups/$GROUP_ID" \
--header "x-api-key: $BALANCER_API_KEY" \
--header "Content-Type: application/json" \
--data-binary @-
import json, os
print(json.dumps({'defaultWafFirewallId': int(os.environ['WAF_FIREWALL_ID']), 'defaultWafMode': 'shadow'}))
PYBODYAPI reference: Update an HTTP group configuration
Expected: HTTP 200. The following shows the response fields to check; additional data depends on your saved resources. Resource identifiers are illustrative.
{
"code": 200,
"result": "success",
"data": {
"groupId": 1
}
} -
After reviewing request observations, enable enforcement.
curl --silent --show-error --include \
--request PATCH \
--url "https://<your-api-host>/http-loadbalancers/$HTTP_LB_ID/groups/$GROUP_ID" \
--header "x-api-key: $BALANCER_API_KEY" \
--header "Content-Type: application/json" \
--data-binary '{"defaultWafMode":"enforce"}'API reference: Update an HTTP group configuration
Expected: HTTP 200. The following shows the response fields to check; additional data depends on your saved resources. Resource identifiers are illustrative.
{
"code": 200,
"result": "success",
"data": {
"groupId": 1
}
} -
Read the final assignment.
curl --silent --show-error --include \
--request GET \
--url "https://<your-api-host>/http-loadbalancers/$HTTP_LB_ID/groups/$GROUP_ID" \
--header "x-api-key: $BALANCER_API_KEY"API reference: Get HTTP group
Expected: HTTP 200. The following shows the response fields to check; additional data depends on your saved resources. Resource identifiers are illustrative.
{
"code": 200,
"result": "success",
"data": {
"groupId": 1
}
}
Verify
Send a controlled request that matches one reviewed rule and a normal request. Compare rule observations with the selected mode and action. A match alone does not establish a block. Custom inspection runs before managed inspection.
Use your configured site domain in this traffic check:
curl --silent --show-error --include "https://example.com/"
Expected: the site behavior described above. The application response body and status depend on your origin and selected policies.
An API mutation saves desired configuration first. Readback and a live traffic check answer different questions. No fixed propagation time is established.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Rule matches but traffic continues | Shadow mode, rule action or a bypass permits it | Check the effective assignment, mode, action and allow policies. |
| Request returns 502 | Security configuration or body handling fails | Read validation state and inspect body capture settings. |
| Custom rule does not appear | Binding or effective route differs | Check ruleset binding, priority and route overrides. |
Limits and defaults
| Setting | Limit or default |
|---|---|
| Firewall sensitivity | Default medium; low, medium or high |
| Paranoia level | 1 to 4 |
| Ruleset binding priority | Required; 1 to 100000000 |
| Expression text | Maximum 8192 bytes |
| Body inspection limit | 1024 to 1048576 bytes |
| Body capture timeout | 50 to 5000 ms |
API status codes
| Status | Meaning |
|---|---|
| 200 | Request succeeds; inspect returned data. |
| 400 | Invalid arguments or configuration. |
| 401 | Authentication or authorization denied. |
| 404 | Resource or parent is unavailable. |
| 409 | Resource state conflicts with the operation. |
| 412 | Inspect the returned error details. |
| 500 | The request encounters a server error. |
Related API operations
DELETE /waf-firewalls/{wafFirewallId}DELETE /waf-firewalls/{wafFirewallId}/dearm-rules/{wafDearmRuleId}DELETE /waf-firewalls/{wafFirewallId}/rulesets/{wafRulesetBindingId}DELETE /waf-rulesets/{wafRulesetId}DELETE /waf-rulesets/{wafRulesetId}/rules/{wafRuleId}GET /http-loadbalancers/{httpLbId}/groups/{groupId}GET /waf-firewallsGET /waf-firewalls/{wafFirewallId}GET /waf-firewalls/{wafFirewallId}/dearm-rulesGET /waf-firewalls/{wafFirewallId}/dearm-rules/{wafDearmRuleId}GET /waf-firewalls/{wafFirewallId}/rulesetsGET /waf-firewalls/{wafFirewallId}/rulesets/{wafRulesetBindingId}GET /waf-rulesetsGET /waf-rulesets/{wafRulesetId}GET /waf-rulesets/{wafRulesetId}/rulesGET /waf-rulesets/{wafRulesetId}/rules/{wafRuleId}PATCH /http-loadbalancers/{httpLbId}/groups/{groupId}PATCH /waf-firewalls/{wafFirewallId}PATCH /waf-firewalls/{wafFirewallId}/dearm-rules/{wafDearmRuleId}PATCH /waf-firewalls/{wafFirewallId}/rulesets/{wafRulesetBindingId}PATCH /waf-rulesets/{wafRulesetId}PATCH /waf-rulesets/{wafRulesetId}/rules/{wafRuleId}POST /http-loadbalancers/{httpLbId}/groups/{groupId}/routes/{routeId}/waf/statisticsPOST /http-loadbalancers/{httpLbId}/groups/{groupId}/waf/statisticsPOST /http-loadbalancers/{httpLbId}/waf/statisticsPOST /waf-firewallsPOST /waf-firewalls/{wafFirewallId}/dearm-rulesPOST /waf-firewalls/{wafFirewallId}/rulesetsPOST /waf-rulesetsPOST /waf-rulesets/{wafRulesetId}/rules
API reference
-
Returns the ordered list of ruleset bindings for a WAF firewall
-
Returns the ordered list of readable WAF rules inside one ruleset
-
Update a WAF ruleset binding, including include target and order
Need Help?
Contact your account manager or use the Balancer portal.