WAF Exceptions: Configuration, Limits and Validation¶
This page is for the platform team. It describes how the Application Gateway addon of the shared services configures managed rule exceptions in a WAF policy, which limits apply, and which checks run when you plan. For the request process and the review, see Application Gateway and WAF policy review.
Reference design, your deployment may differ
This page describes the appgw addon of the shared services code. Whether the Application Gateway module of your hub
supports the same exceptions block is not documented in the sources. Check the schema of your gateway before you copy an
example.
Exclusions and exceptions¶
A WAF policy in prevention mode blocks requests that match its managed rules (for example the OWASP rule set). The module offers two ways to handle a legitimate request that is blocked:
| Exclusion | Exception | |
|---|---|---|
| Key in the policy file | managed_rules.<name>.exclusions |
exceptions (top level of the policy) |
| Matches on | A request attribute (match_variable, selector, selector_match_operator) |
RemoteAddr, RequestHeader or RequestURI, with a list of values |
| Scope | Rule sets and rule groups with the rules to exclude (excluded_rule_sets) |
One managed rule set, optionally limited to rule groups and rule IDs |
| Deployed by | The azurerm provider |
An azapi update after the policy, because the azurerm provider does not support exceptions |
Prefer the narrowest option: one rule ID, for one value. Do not disable a rule set because of one request.
Policy file structure¶
Policy files are JSON, in the folder set by waf_json_file_relative_path (default waf-definitions/) and matched with
waf_json_file_pattern (default *.json). Each file has one object web_application_firewall_policy with an optional name,
custom_rules, policy_settings, managed_rules and exceptions. Without name, the policy is called
wafp-shared-hub-<environment>-<location_short>-001. Changing the name of an existing policy replaces it.
An exception (anonymised example):
"exceptions": [
{
"match_variable": "RequestHeader",
"selector": "X-Trusted-Partner",
"selector_match_operator": "Equals",
"value_match_operator": "Equals",
"values": ["true"],
"exception_managed_rule_sets": [
{
"type": "OWASP",
"version": "3.2",
"rule_groups": [
{
"rule_group_name": "REQUEST-942-APPLICATION-ATTACK-SQLI",
"rules": [942100, 942110]
}
]
}
]
}
]
| Field | Meaning |
|---|---|
match_variable |
RemoteAddr, RequestHeader or RequestURI |
value_match_operator |
Contains, EndsWith, Equals, IPMatch or StartsWith |
values |
The values to match. At least one. |
selector, selector_match_operator |
Only for RequestHeader, and then both are required. The selector is the header name. The operator is Contains, EndsWith, Equals or StartsWith. |
exception_managed_rule_sets |
Exactly one entry per exception: type, version and rule_groups |
rule_groups |
List of rule_group_name and rules (rule IDs). The repository example for a RemoteAddr exception uses an empty list, which the validation treats as the scope of the whole rule set. |
An IPMatch value must be a single IPv4 address. CIDR ranges and IPv6 are rejected.
Limits¶
The checks enforce the Azure limits that apply at the time of writing:
| Limit | Value |
|---|---|
| Exceptions per WAF policy | 60 |
| Exceptions in total across the WAF policies of one Application Gateway | 60 |
Values for RemoteAddr exceptions |
600 |
Values for RequestURI exceptions |
10 |
Values for RequestHeader exceptions |
10 |
The total counts the exceptions of all policy files in the folder, not only one. These limits are constants in the module: check them against the current Azure limits when you upgrade.
Validation at plan time¶
All checks are Terraform preconditions on the exception resource. A problem stops the plan, which matters: a failure
during apply, after the managed rules have changed, would leave the policy without exceptions. For the same reason, any
change to managed_rules of a policy re-applies its exceptions automatically (a replace trigger), because Azure removes the
exceptions when the managed rules are updated.
| Check | Fails when |
|---|---|
| Match variable | Not RemoteAddr, RequestHeader or RequestURI |
| Value match operator | Not one of the allowed operators |
| IP values | An IPMatch value is not a single IPv4 address |
| Selector | A RequestHeader exception lacks selector or selector_match_operator, or another match variable sets them |
| Values | An exception has no values |
| Rule set count | An exception does not have exactly one exception_managed_rule_sets entry |
| Rule set | The type and version are not one of the managed rule sets configured in the same policy |
| Rule group | rule_group_name does not exist in that rule set |
| Rule ID | A rule ID does not exist in that rule group |
| Limits | One of the limits above is exceeded |
The rule set, group and rule checks use the catalogue of available WAF rule sets of the subscription (the Azure resource type
Microsoft.Network/applicationGatewayAvailableWafRuleSets), read during plan. The identity that runs the plan needs read access
to it. The error messages list the valid values, so you can copy the right group or rule ID.
Add an exception¶
- Collect the evidence from the WAF log of the blocked request: the rule ID, the rule group, the URI and the argument or header (Application Gateway logs).
- Get the approval of security, and record the rationale and an example request in the change record.
- Add the exception to the policy file. Choose the narrowest match: a header value or a single address is better than a URI that matches many requests.
- Plan. Fix every precondition message.
- Review (WAF policy review) and apply.
- Test the request again, and keep a list of all exceptions with their rationale for periodic review.