From 6dea17bba0691012afc5e7e1a6e2ce6d70b099d9 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Fri, 3 Oct 2025 16:56:04 +0100 Subject: [PATCH 01/17] feat: Add additional F5 WAF for NGINX pages This commit adds a handful of pages to F5 WAF for NGINX, migrating and rewriting content from the prior documentation set. It includes: - Custom dimensions for log entries - User-defined URLs and parameters - Do-nothing - Override rules --- content/includes/waf/table-policy-features.md | 4 + content/waf/logging/custom-dimensions.md | 100 +++ content/waf/logging/debug-logs.md | 2 +- content/waf/logging/operation-logs.md | 2 +- content/waf/logging/security-logs.md | 2 +- content/waf/policies/attack-signatures.md | 2 +- content/waf/policies/do-nothing.md | 43 ++ content/waf/policies/jwt-protection.md | 2 +- content/waf/policies/override-rules.md | 246 +++++++ content/waf/policies/user-urls-parameters.md | 617 ++++++++++++++++++ 10 files changed, 1015 insertions(+), 5 deletions(-) create mode 100644 content/waf/logging/custom-dimensions.md create mode 100644 content/waf/policies/do-nothing.md create mode 100644 content/waf/policies/override-rules.md create mode 100644 content/waf/policies/user-urls-parameters.md diff --git a/content/includes/waf/table-policy-features.md b/content/includes/waf/table-policy-features.md index be565decb..80cc1849e 100644 --- a/content/includes/waf/table-policy-features.md +++ b/content/includes/waf/table-policy-features.md @@ -11,6 +11,7 @@ | [Cookie enforcement]({{< ref "/waf/policies/cookie-enforcement.md" >}}) | By default all cookies are allowed and not enforced for integrity. The user can add specific cookies, wildcards or explicit, that will be enforced for integrity. It is also possible to set the cookie attributes: HttpOnly, Secure and SameSite for cookies found in the response. | | [Data guard]({{< ref "/waf/policies/data-guard.md" >}}) | Detects and masks Credit Card Number (CCN) and/or U.S. Social Security Number (SSN) and/or custom patterns in HTTP responses. Disabled by default. | | [Deny and Allow IP lists]({{< ref "/waf/policies/deny-allow-ip.md" >}}) | Manually define denied & allowed IP addresses as well as IP addresses to never log. | +| [Do-nothing]({{< ref "/waf/policies/do-nothing.md" >}}) | Do-nothing allows you to configure the avoidance of inspecting or parsing content of a URL. | | [Disallowed file type extensions]({{< ref "/waf/policies/disallowed-extensions.md" >}}) | Support any file type, and includes a predefined list of file types by default | | [Evasion techniques]({{< ref "/waf/policies/evasion-techniques.md" >}}) | All evasion techniques are enabled by default, and can be disabled individually. These include directory traversal, bad escaped characters and more. | | [Geolocation]({{< ref "/waf/policies/geolocation.md" >}}) | The geolocation feature allows you to configure enforcement based on the location of an object using the two-letter ISO code representing a country. | @@ -20,10 +21,13 @@ | [IP address lists]({{< ref "/waf/policies/ip-address-lists.md" >}}) | Organize lists of allowed and forbidden IP addresses across several lists with common attributes. | | [IP intelligence]({{< ref "/waf/policies/ip-intelligence.md" >}}) | Configure the IP Intelligence feature to customize enforcement based on the source IP of the request, limiting access from IP addresses with questionable reputation. | | [JWT protection]({{< ref "/waf/policies/jwt-protection.md" >}}) | JWT protection allows you to configure policies based on properties of JSON web tokens, such as their header and signature properties. | +| [Override rules]({{< ref "/waf/policies/override-rules.md" >}}) | Override rules allow you to override default policy settings under specific conditions. | | [Server technology signatures]({{< ref "/waf/policies/server-technology-signatures.md" >}}) | Support adding signatures per added server technology. | | [Time-based signature staging]({{< ref "/waf/policies/time-based-signature-staging.md" >}}) | Time-based signature staging allows you to stage signatures for a specific period of time. During the staging period, violations of staged signatures are logged but not enforced. After the staging period ends, violations of staged signatures are enforced according to the policy's enforcement mode. | | [Threat campaigns]({{< ref "/waf/policies/threat-campaigns.md" >}}) | These are patterns that detect all the known attack campaigns. They are very accurate and have almost no false positives, but are very specific and do not detect malicious traffic that is not part of those campaigns. The default policy enables threat campaigns but it is possible to disable it through the respective violation. | | [User-defined HTTP headers]({{< ref "/waf/policies/user-headers.md" >}}) | Handling headers as a special part of requests | +| [User-defined URLs and parameters]({{< ref "/waf/policies/user-urls-parameters.md" >}}) | Use user-defined properties when configuring violations. | | [XFF trusted headers]({{< ref "/waf/policies/xff-headers.md" >}}) | Disabled by default, and can accept an optional list of custom XFF headers. | | [XML and JSON content]({{< ref "/waf/policies/xml-json-content.md" >}}) | XML content and JSON content profiles detect malformed content and signatures in the element values. Default policy checks maximum structure depth. It is possible to enable more size restrictions: maximum total length of XML/JSON data, maximum number of elements and more. | + {{< /table >}} diff --git a/content/waf/logging/custom-dimensions.md b/content/waf/logging/custom-dimensions.md new file mode 100644 index 000000000..624fc8c67 --- /dev/null +++ b/content/waf/logging/custom-dimensions.md @@ -0,0 +1,100 @@ +--- +title: Custom dimensions for log entries +toc: false +weight: 200 +nd-content-type: reference +nd-product: NAP-WAF +--- + +Custom dimensions log entries feature refers to the new configuration in F5 WAF for NGINX, where the new directive called `app_protect_custom_log_attribute` is assigned to a particular location or server or http level in the `nginx.conf` file. The need is to be able to add custom identifiers to the respective location and/or server and identify requests in the Security Log by those identifiers. + +The `app_protect_custom_log_attribute` directive will be used to track the assigned location/server/http dimension of each request by adding the `app_protect_custom_log_attribute` to the **Security Logs** a.k.a **Request Logs**. Since it is a custom attribute a customer can set, that custom attribute will appear for every request log entry that was handled by that location/server. + +### Configuration + +A new directive `app_protect_custom_log_attribute` will be added to the `nginx.conf` file. You can set this directive at all scopes: http, server and location. The setting at the location scope overrides the setting in the server and/or http scopes and the server scope overrides the http scope. The `app_protect_custom_log_attribute` directive syntax will consist of a **name/value** or **key/value** pair i.e. "app_protect_custom_log_attribute ". + +Example Configuration: + +In the below example, we are configuring the `app_protect_custom_log_attribute` directive at the server and location level where we define the **key/value** pair as one string. + +```nginx + +user nginx; +load_module modules/ngx_http_app_protect_module.so; +error_log /var/log/nginx/error.log debug; + +events { + worker_connections 65536; +} +server { + + listen 80; + + server_name localhost; + proxy_http_version 1.1; + app_protect_custom_log_attribute ‘environment' 'env1'; + + location / { + + app_protect_enable on; + app_protect_custom_log_attribute gateway gway1; + app_protect_custom_log_attribute component comp1; + proxy_pass http://172.29.38.211:80$request_uri; + } + } +``` + +The **key/value** pair will be 'environment env1', ‘gateway gway1’ and ‘component comp1’ in the above examples, i.e. + +- app_protect_custom_log_attribute environment env1; +- app_protect_custom_log_attribute gateway gway1; +- app_protect_custom_log_attribute component comp1; + +The above key/value pair will be parsed as below: + +```shell +"customLogAttributes": [ + { + "name": "gateway", + "value": "gway1" + }, + { + "name": "component", + "value": "comp1" + }, +] +``` + +### Things to Remember While Configuring the Custom Dimensions Log Entries + +The `app_protect_custom_log_attribute` directive has a few limitations which should be kept in mind while configuring this directive: + +- Key and value strings are limited to 64 chars +- Maximum possible directive numbers are limited to 10 (in total) in each context i.e. Limit of 10 keys and values + +### Errors and Warnings + +An error message "`app_protect_custom_log_attribute` directive is invalid" will be displayed in the Security Log if the below conditions are met: + +1. If the `app_protect_custom_log_attribute` exceeds the maximum number of 10 directives +2. If the `app_protect_custom_log_attribute` exceeds the maximum name length of 64 chars +3. If the `app_protect_custom_log_attribute` exceeds the maximum value of 64 chars + +Error message example: + +```shell +app_protect_custom_log_attribute directive is invalid. Number of app_protect_custom_log_attribute directives exceeds maximum +``` + +### Logging and Reporting + +When `app_protect_custom_log_attribute` is assigned to a particular location/server/http context, it will appear in the `json_log` field as a new JSON property called "customLogAttributes" at the top level. The property will not appear if no `app_protect_custom_log_attribute` directive was assigned. + +Attributes at the http level applies to all servers and locations unless a specific server or location overrides the same key with a different value. Same goes for the server level and all locations under it. In the below example, the "environment" attribute will appear in logs of all locations under that server. + +Security logging example in json_log: + +```json +""customLogAttribute"":[{""name"":""component"",""value"":""comp1""},{""name"":""gateway"",""value"":""gway1""}]}" +``` \ No newline at end of file diff --git a/content/waf/logging/debug-logs.md b/content/waf/logging/debug-logs.md index 274b3cc87..e3bad3381 100644 --- a/content/waf/logging/debug-logs.md +++ b/content/waf/logging/debug-logs.md @@ -1,7 +1,7 @@ --- title: Debug logs toc: false -weight: 400 +weight: 500 nd-content-type: reference nd-product: NAP-WAF --- diff --git a/content/waf/logging/operation-logs.md b/content/waf/logging/operation-logs.md index 389081289..a3492eb88 100644 --- a/content/waf/logging/operation-logs.md +++ b/content/waf/logging/operation-logs.md @@ -1,7 +1,7 @@ --- title: Operation logs toc: false -weight: 300 +weight: 400 nd-content-type: reference nd-product: NAP-WAF --- diff --git a/content/waf/logging/security-logs.md b/content/waf/logging/security-logs.md index cc9744cb1..25ed5674b 100644 --- a/content/waf/logging/security-logs.md +++ b/content/waf/logging/security-logs.md @@ -1,7 +1,7 @@ --- title: Security logs toc: true -weight: 200 +weight: 300 nd-content-type: reference nd-product: NAP-WAF --- diff --git a/content/waf/policies/attack-signatures.md b/content/waf/policies/attack-signatures.md index a6dca026f..36ca18bb0 100644 --- a/content/waf/policies/attack-signatures.md +++ b/content/waf/policies/attack-signatures.md @@ -203,7 +203,7 @@ To exclude multiple attack signatures, each signature ID needs to be added as a In the previous examples, the signatures were disabled for all the requests that are inspected by the respective policy. You can also exclude signatures for specific URLs or parameters, while still enable them for the other URLs and parameters. -The topics [User-defined URLs]() and [User-defined parameters]() have more details. +The topic [User-defined URLs and parameters]({{< ref "/waf/policies/user-urls-parameters.md" >}}) has more details. In some cases, you may want to remove a whole signature set that was included in the default policy. For example, a protected application may not use XML and is not vulnerable to XPath injection. diff --git a/content/waf/policies/do-nothing.md b/content/waf/policies/do-nothing.md new file mode 100644 index 000000000..92d87b7ea --- /dev/null +++ b/content/waf/policies/do-nothing.md @@ -0,0 +1,43 @@ +--- +# We use sentence case and present imperative tone +title: "Do-nothing" +# Weights are assigned in increments of 100: determines sorting order +weight: 1050 +# Creates a table of contents and sidebar, useful for large documents +toc: true +# Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this +nd-content-type: reference +# Intended for internal catalogue and search, case sensitive: +# Agent, N4Azure, NIC, NIM, NGF, NAP-DOS, NAP-WAF, NGINX One, NGINX+, Solutions, Unit +nd-product: NAP-WAF +--- + +This topic describes the do-nothing policy feature of F5 WAF for NGINX. + +Within _urlContentProfiles_, adding the _do-nothing_ type allows the user to avoid inspecting or parsing the content in a policy, and instead handle the request's header according to the specifications outlined in the security policy. + +The following example configures do-nothing for a specific user-defined URL: + +```json +{ + "policy" : { + "name": "ignore_body", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "urls": [ + { + "method": "*", + "name": "*", + "type": "wildcard", + "urlContentProfiles": [ + { + "headerName": "*", + "headerOrder": "default", + "headerValue": "*", + "type": "do-nothing" + } + ] + } + ] + } +} +``` \ No newline at end of file diff --git a/content/waf/policies/jwt-protection.md b/content/waf/policies/jwt-protection.md index 8edb1239d..35b0b043f 100644 --- a/content/waf/policies/jwt-protection.md +++ b/content/waf/policies/jwt-protection.md @@ -1,6 +1,6 @@ --- title: JWT protection -weight: 1650 +weight: 1700 toc: true nd-content-type: reference nd-product: NAP-WAF diff --git a/content/waf/policies/override-rules.md b/content/waf/policies/override-rules.md new file mode 100644 index 000000000..7e573f5ea --- /dev/null +++ b/content/waf/policies/override-rules.md @@ -0,0 +1,246 @@ +--- +title: Override rules +weight: 1800 +toc: true +nd-content-type: reference +nd-product: NAP-WAF +nd-docs: DOCS-000 +--- + +The **Override Rules** feature allows overriding of the **default policy** settings. Each override rule consists of a condition followed by changes to the original policy applied to requests that meet the respective condition. This feature provides the ability to include the override rules within a declarative policy such that all incoming requests are verified against those rules. + +With this enhancement, users now have more control over how a unique policy setting is applied to incoming requests with a specific method, source IP address, header or URI value through one or multiple unique override rules. Each override rule possesses a unique name and specific conditions that are matched against incoming traffic from a specific client side. The structure of these override rules adheres to the JSON schema defined by the declarative policy. + +Here is an example of a declarative policy using an override rules entity: + +```shell +{ + "policy": { + "name": "override_rules_example", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "override-rules": [ + { + "name": "localhost-log-only", + "condition": "host.contains('localhost') and clientIp == '127.0.0.1' and userAgent.lower().startsWith('curl')", + "override": { + "policy": { + "enforcementMode": "transparent" + } + } + }, + { + "name": "login_page", + "condition": "method == 'POST' and not parameters['ref'].lower().matches('example') and uri.contains('/login/')", + "actionType": "replace-policy", + "override": { + "policy": { + "name": "login_page_block_redirect", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "signature-sets": [ + { + "name": "All Signatures", + "block": true, + "alarm": true + } + ], + "response-pages": [ + { + "responseRedirectUrl": "https://example.com/rejected?id=<%TS.request.ID()%>", + "responseActionType": "redirect", + "responsePageType": "default" + } + ] + } + } + }, + { + "name": "api-strict", + "condition": "uri.contains('api4') and not (clientIp.matches('fd00:1::/48') or userAgent.lower().startsWith('Mozilla'))", + "actionType": "replace-policy", + "override": { + "$ref": "file:///NginxStrictPolicy.json" + } + }, + { + "name": "strict-post", + "condition": "method.matches('POST') and (cookies['sessionToken'] != 'c2Vzc2lvblRva2Vu' or headers['Content-Encoding'] == 'gzip')", + "actionType": "replace-policy", + "override": { + "$ref": "file:///NginxStrictPolicy.json" + } + }, + "name": "usa-only", + "condition": "geolocation != 'US'", + "actionType": "violation", + "violation": { + "block": true, + "alarm": true, + "attackType": { + "name": "Forceful Browsing" + }, + "description": "Attempt to access from outside the USA", + "rating": 4 + } + } + ] + } +} +``` + +The above "override_rules_example" policy contains five override rules: + +1. The **"localhost-log-only"** rule applies to the requests with a user agent header starting with "curl", a host header containing "localhost", and a client IP address set to 127.0.0.1. It switches the enforcement mode to "transparent" without blocking the request. The remaining policy settings remain unchanged. This type of override rule is an example of an **Inline Policy Reference**. +2. The **"login_page"** rule is triggered by POST requests to URIs containing "/login/". Since the "actionType" field is set to "replace-policy", it overrides the policy with a new one named "login_page_block_redirect". This new policy is independent of the "override_rules_example" policy. It enables all signature sets and redirects the user to a rejection page. This is another example of an **Inline Policy Reference** with a different condition. +3. The **"api-strict"** rule is applied for requests with "api4" in the URI, except for client IP addresses matching the "fd00:1::/48" range and user agents starting with "Mozilla". It references an external policy file named "NginxStrictPolicy.json" located at "/etc/app_protect/conf/" to override the current policy. The "actionType" field is set to "replace-policy" and the external policy can be specified using a reference to its file using **$ref**. The file is the JSON policy source of that policy. This type of policy switching is known as **External Policy Reference**. +4. The **"strict-post"** rule is triggered when POST requests include a session token in the cookies that is not equal to "c2Vzc2lvblRva2Vu" or when the "gzip" value is found in the content-encoding headers. This rule follows a similar approach to referencing an external policy file, just like the **api-strict** rule mentioned above. +5. The **"usa-only"** rule is triggered when a request coming from a country other than the USA. The actionType is set to "violation", meaning that `VIOL_RULE` violation is triggered for such a request. This violation will block and mark the request as illegal with regard to the "block" and "alarm" attributes. There is no change in policy for this rule. For more details about **Geolocation** feature, see [Geolocation in Policy Override Rules Conditions](#geolocation-in-policy-override-rules-conditions). + +These five rules demonstrate how the override rules feature allows for customization and the ability to modify specific aspects of the original policy based on predefined conditions. + + +{{< call-out "note" >}} +- By default, the actionType field is configured to "extend-policy". +- External references are supported for any policy reference. +{{< /call-out >}} + +### First Match Principle + +The policy enforcement operates on the **first match** principle. This principle is applied when multiple conditions match or are similar, indicating that any incoming requests that match the first condition will be processed. In the following example, the "override_rules_example2" policy uses two override rules: "this_rule_will_match" and "non_matching_rule". Since both conditions match, the first match principle will be applied, and requests with "api" in the URI will be processed. It will reference an external policy file named "NginxStrictPolicy.json" to override the current policy. . + +For example: + +```shell +{ + "policy": { + "name": "override_rules_example2", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "override-rules": [ + { + "name": "this_rule_will_match", + "condition": "uri.contains('api')", + "actionType": "replace-policy", + "override": { + "$ref": "file:///NginxStrictPolicy.json" + } + }, + { + "name": "non_matching_rule", + "condition": "uri.contains('api') and not clientIp == '192.168.0.10'", + "actionType": "extend-policy", + "override": { + "policy": { + "enforcementMode": "transparent" + } + } + } + ] + } +} +``` + +### Important Things to Remember About Override Rules + +Here are some key points to remember regarding the Override Rules feature: + +- To ensure efficient compilation time and optimal resource allocation for policies, there are limitations in place. Currently, policies have a maximum limit of 10 rules and a maximum of 5 clauses in a condition. These limitations help maintain better performance and manageability. A compilation error will not occur if a policy file contains more than 5 clauses or 10 overrides. +- The replacement policy should not include any override rules. Override rules should be used to extend or switch to a different policy, rather than being part of the replacement policy itself. +- Each override rule will be compiled as a separate policy, whether extending the main policy or switching to a new one. The enforcer will switch to the policy that corresponds to the matched rule, but the main policy name will be reported along with the override rule property. +- The URI, host, and user-agent strings in the request will be treated as plain ASCII characters and won't undergo language decoding. If any of these strings contain non-ASCII characters, they may be misinterpreted and may not comply with rules that expect specific values in the conditions. + +### Override Rules Logging & Reporting + +If a request matches an override rule, the `json_log` field will include a new block named 'overrideRule'. However, if no rules match the request, the log will not contain any related information. When the 'actionType' flag is set to "replace-policy", the 'originalPolicyName' field in the log will reflect the name of the original policy name (the one that contains override rules), and the `policy_name` field will reflect the policy that was enforced. + +For example, if the matching override rule is called "login_page": + +```shell + +... +policy_name="login_page_block_redirect" +... + +json_log will have: + +{ + ... + "overrideRule": { + "name": "login_page", + "originalPolicyName": "override_rule_example" +} + ... + +``` + +If the matching override rule is called "usa-only": + +```shell +{ + "enforcementState": { + "isBlocked": true, + "isAlarmed": true, + "rating": 4, + "attackType": [ + { + "name": "ATTACK_TYPE_FORCEFUL_BROWSING" + } + ] + }, + "violation": { + "name": "VIOL_RULE" + }, + "policyEntity": { + "override-rule": { + "name": "usa-only" + } + }, + "description": "Trying to access special" +}, + +``` + +### Errors and Warnings + +#### Missing Policy Name + +Every policy must have a name if actionType is either "extend-policy" or "replace-policy". If the policy 'name' is not provided in the override section, an error message will be displayed indicating the missing policy 'name' within that specific override rule. For instance, in the override rule below, the policy name is not specified. + + +Example of Missing policy 'name': + +```shell +"override-rules": [ + { + "name": "example-rule", + "condition": "uri.contains('127')", + "actionType": "replace-policy", + "override": { + "policy": { + "name": "policy_name", <--- the missing part + "enforcementMode": "transparent" + } + } + } +] +``` + +Example of Missing policy 'name' error: + +```shell +"error_message": "Failed to import Policy 'policy1' from '/etc/app_protect/conf/test.json': Missing policy 'name' in the override rule 'example-rule'." +``` + +#### Cyclic Override Rule Error + +If an inline or externally referenced policy contains an override rule, a Cyclic Override Rule error will be issued. + +Example of Cyclic Override Rule error: + +```shell +"error_message": "Failed to import an override policy: Cyclic override-rules detected." +``` \ No newline at end of file diff --git a/content/waf/policies/user-urls-parameters.md b/content/waf/policies/user-urls-parameters.md new file mode 100644 index 000000000..548ffa2b0 --- /dev/null +++ b/content/waf/policies/user-urls-parameters.md @@ -0,0 +1,617 @@ +--- +# We use sentence case and present imperative tone +title: "User-defined URLs and parameters" +# Weights are assigned in increments of 100: determines sorting order +weight: 2150 +# Creates a table of contents and sidebar, useful for large documents +toc: true +# Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this +nd-content-type: reference +# Intended for internal catalogue and search, case sensitive: +# Agent, N4Azure, NIC, NIM, NGF, NAP-DOS, NAP-WAF, NGINX One, NGINX+, Solutions, Unit +nd-product: NAP-WAF +--- + +This topic describes the user-defined URLs and parameters feature of F5 WAF for NGINX. + + +## URLs + +User-defined URLs allows you to configure a URL with the following options: +- Define a protected URL configuration with an explicitly path or implicit wildcard +- Define a list of allowed/disallowed methods per URL that will override the list defined in the policy level. +- Define the content-types `json/xml/form-data` for a user-defined URL. +- Define an Allowed/Disallowed for user-defined URL. +- Add user-defined URLs to the Signature/Metacharacters override list. + +For `urlContentProfiles` default values, see F5 WAF for NGINX [Declarative Policy guide.]({{< ref "/nap-waf/v4/declarative-policy/policy.md" >}}) + +This example configures allowed meta-characters for a user-defined URL: + +```json +{ + "policy": { + "name": "/Common/user_defined_URL", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_URL", + "alarm": true, + "block": true + }, + { + "name": "VIOL_URL_METACHAR", + "alarm": true, + "block": true + } + ] + }, + "urls": [ + { + "method": "*", + "name": "/meta*", + "protocol": "http", + "type": "wildcard", + "metacharsOnUrlCheck": true, + "metacharOverrides": [ + { + "isAllowed": true, + "metachar": "0x3c" + }, + { + "isAllowed": false, + "metachar": "0x28" + } + ], + "wildcardOrder": 2 + } + ] + } +} +``` + +This next example disables the detection of a specific signature (`200010093`) and enables another (`200010008`) for the user-defined URL `/Common/user_defined_URL`. + +These signature settings take effect only in requests to that URL. + +For other requests, the signature behavior is determined by the signature sets these signatures belong to. View [signature sets]({{< ref "/waf/policies/attack-signatures.md#signature-sets" >}}) for more details. + +```json +{ + "policy": { + "name": "/Common/user_defined_URL", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_URL", + "alarm": true, + "block": true + } + ] + }, + "urls": [ + { + "method": "*", + "name": "/test*", + "protocol": "http", + "type": "wildcard", + "wildcardOrder": 1, + "attackSignaturesCheck": true, + "signatureOverrides": [ + { + "enabled": true, + "signatureId": 200010008 + }, + { + "enabled": false, + "signatureId": 200010093 + } + ] + } + ] + } +} +``` + +This example shows wildcard and explicit URL configuration, where the first URL is permitted for all methods, and the second is permitted only for GET: + +```json +{ + "policy": { + "name": "/Common/user_defined_URL", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_URL", + "alarm": true, + "block": true + } + ] + }, + "urls": [ + { + "method": "*", + "name": "/test*", + "protocol": "http", + "type": "wildcard", + "wildcardOrder": 1 + }, + { + "method": "GET", + "name": "/index.html", + "protocol": "http", + "type": "explicit" + } + ] + } +} +``` + +This final example shows how to configure json/xml/form-data content types for a specific user-defined URL: + +```json +{ + "policy": { + "name": "/Common/user_defined_URL", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_URL", + "alarm": true, + "block": true + }, + { + "name": "VIOL_METHOD", + "alarm": true, + "block": true + }, + { + "name": "VIOL_JSON_MALFORMED", + "alarm": true, + "block": true + }, + { + "name": "VIOL_JSON_FORMAT", + "alarm": true, + "block": true + }, + { + "name": "VIOL_XML_FORMAT", + "alarm": true, + "block": true + } + ] + }, + "json-profiles": [ + { + "name": "Default", + "handleJsonValuesAsParameters": false, + "defenseAttributes": { + "maximumTotalLengthOfJSONData": "any", + "maximumArrayLength": "any", + "maximumStructureDepth": "any", + "maximumValueLength": "any" + } + } + ], + "xml-profiles": [ + { + "name": "Default", + "defenseAttributes": { + "maximumAttributesPerElement": "any", + "maximumDocumentDepth": "any", + "maximumAttributeValueLength": "any", + "maximumChildrenPerElement": "any", + "maximumDocumentSize": "any", + "maximumElements": "any", + "maximumNameLength": "any", + "maximumNSDeclarations": "any", + "maximumNamespaceLength": "any", + "tolerateLeadingWhiteSpace": true, + "tolerateCloseTagShorthand": true, + "allowCDATA": true, + "allowExternalReferences": true, + "allowProcessingInstructions": true + } + } + ], + "urls": [ + { + "method": "*", + "name": "/first*", + "protocol": "http", + "type": "wildcard", + "wildcardOrder": 1, + "urlContentProfiles": [ + { + "headerValue": "*", + "headerName": "*", + "headerOrder": "3", + "type": "form-data" + }, + { + "contentProfile": { + "name": "Default" + }, + "headerValue": "*xml*", + "headerName": "Content-Type", + "headerOrder": "2", + "type": "xml" + }, + { + "contentProfile": { + "name": "Default" + }, + "headerValue": "*json*", + "headerName": "Content-Type", + "headerOrder": "1", + "type": "json" + } + ] + } + ] + } +} +``` + +## Parameters + +User-defined parameters allow you to give specific attributes to specific parameters. + +This feature gives you full control over what the parameter should include and where it should be located, allowing for granularity to configure every parameter. + +With user-defined parameters you can:: + +- Create unique parameters and specify attributes for each +- Define what data type the parameter should contain +- Define the allowed location where you expect to see a parameter +- Define minimum/maximum values and minimum/maximum lengths for a parameter +- Define whether a parameter is mandatory or not +- Define whether the parameter can have empty values or not +- Define whether to inspect a parameter for violations, attack signatures, or meta-characters +- Decide whether to exclude certain violations, attack signatures, or meta-characters for a parameter + +The following example has two user-defined parameters. + +The first one, `text`, takes string values (here configured as alpha-numeric), and limits the length of the allowed string between 4 and 8 characters. Any string below or above these values will trigger the violation `VIOL_PARAMETER_VALUE_LENGTH`. Note that we enable this violation to *block* the violating request. + +The second parameter, `query`, is added to the policy just to avoid a false positive condition due to a specific signature, `200002835`. + +This allows you to create exceptions on known false positives _only_ within the context of a specific parameter. The signature will still be detected on values of other parameters. + +```json +{ + "policy": { + "name": "user_defined_parameters_data_types", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_VALUE_LENGTH", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "text", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "user-input", + "dataType": "alpha-numeric", + "checkMinValueLength": true, + "checkMaxValueLength": true, + "minimumLength": 4, + "maximumLength": 8 + }, + { + "name": "query", + "type": "explicit", + "valueType": "user-input", + "dataType": "alpha-numeric", + "signatureOverrides": [ + { + "enabled": false, + "signatureId": 200002835 + } + ] + } + ] + } +} +``` + +This next example uses a numeric parameter which accepts only integer values and allows values between 9 and 99 (non-inclusive). + +If the request includes anything other than an integer, it will trigger the `VIOL_PARAMETER_DATA_TYPE` violation. + +If the parameter value falls beyond or below the desired values, it will trigger the `VIOL_PARAMETER_NUMERIC_VALUE` violation. + +If you change the values of `exclusiveMin` and `exclusiveMax` to false, values equal to the boundary values will be accepted (namely 9 and 99). + +```json +{ + "policy": { + "name": "user_defined_parameters_data_types", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_NUMERIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_VALUE_LENGTH", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_STATIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_DATA_TYPE", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "number", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "user-input", + "dataType": "integer", + "checkMinValue": true, + "checkMaxValue": true, + "minimumValue": 9, + "maximumValue": 99, + "exclusiveMin": true, + "exclusiveMax": true + } + ] + } +} +``` + +For increased granularity, you can configure whether the parameter value is also a multiple of a specific number. + +This is useful when you wish to limit the input to specific values. + +The following example configures a parameter that accepts values in the range of 0 to 10 and are only multiples of 3. + +This means that the accepted values are 3, 6 and 9. Any other value will trigger the `VIOL_PARAMETER_NUMERIC_VALUE` violation. + +```json +{ + "policy": { + "name": "user_defined_parameters_data_types", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_NUMERIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_VALUE_LENGTH", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_STATIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_DATA_TYPE", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "multiples", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "user-input", + "dataType": "integer", + "checkMinValue": true, + "checkMaxValue": true, + "minimumValue": 0, + "maximumValue": 10, + "checkMultipleOfValue": true, + "multipleOf": 3 + } + ] + } +} +``` + +Another useful example is limiting the parameter to a single context, such as in a header or a query string. + +If the same variable appears in a different location, it will trigger the `VIOL_PARAMETER_LOCATION` violation. + +```json +{ + "policy": { + "name": "user_defined_parameters_misc_test", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_NUMERIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_VALUE_LENGTH", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_STATIC_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_DATA_TYPE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_LOCATION", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "headerparam", + "type": "explicit", + "parameterLocation": "header", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "user-input", + "dataType": "alpha-numeric", + "checkMinValueLength": false, + "checkMaxValueLength": false + } + ] + } +} +``` + +This final example configures: + +- A sensitive parameter `mypass` that should be masked in the logs +- A parameter `empty` that is allowed to be empty +- A parameter `repeated` that can be repeated multiple times +- A parameter `mandatory` that is mandatory for all requests + +New violations are enabled so that the configuration becomes effective. + +```json +{ + "policy": { + "name": "user_defined_parameters_misc_test", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_PARAMETER_EMPTY_VALUE", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER_REPEATED", + "alarm": true, + "block": true + }, + { + "name": "VIOL_MANDATORY_PARAMETER", + "alarm": true, + "block": true + }, + { + "name": "VIOL_PARAMETER", + "alarm": true, + "block": true + } + ] + }, + "parameters": [ + { + "name": "mypass", + "type": "explicit", + "parameterLocation": "any", + "sensitiveParameter": true, + "valueType": "auto-detect" + }, + { + "name": "empty", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": true, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "auto-detect" + }, + { + "name": "repeated", + "type": "explicit", + "parameterLocation": "any", + "mandatory": false, + "allowEmptyValue": false, + "allowRepeatedParameterName": true, + "sensitiveParameter": false, + "valueType": "auto-detect" + }, + { + "name": "mandatory", + "type": "explicit", + "parameterLocation": "any", + "mandatory": true, + "allowEmptyValue": false, + "allowRepeatedParameterName": false, + "sensitiveParameter": false, + "valueType": "auto-detect" + } + ] + } +} +``` \ No newline at end of file From 19f75362bbf5712b7cbf00c398d104f7b0b3718f Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Mon, 6 Oct 2025 12:46:34 +0100 Subject: [PATCH 02/17] feat: Add source content to be re-written --- content/waf/install/virtual-environment.md | 4 +- content/waf/policies/directives.md | 111 ++++++++++++++++++++ content/waf/policies/external-references.md | 13 +++ content/waf/policies/violations.md | 2 +- 4 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 content/waf/policies/directives.md create mode 100644 content/waf/policies/external-references.md diff --git a/content/waf/install/virtual-environment.md b/content/waf/install/virtual-environment.md index 724e90ca5..a1d4e10ed 100644 --- a/content/waf/install/virtual-environment.md +++ b/content/waf/install/virtual-environment.md @@ -162,10 +162,10 @@ Add F5 WAF for NGINX dependencies: sudo wget -P /etc/yum.repos.d https://cs.nginx.com/static/files/dependencies.repo ``` -Enable the _codeready-builder_ repository: +Enable F5 WAF for NGINX dependencies: ```shell -sudo subscription-manager repos --enable codeready-builder-for-rhel-9-x86_64-rpms +sudo dnf config-manager --set-enabled crb ``` Install the F5 WAF for NGINX package and its dependencies: diff --git a/content/waf/policies/directives.md b/content/waf/policies/directives.md new file mode 100644 index 000000000..20653da74 --- /dev/null +++ b/content/waf/policies/directives.md @@ -0,0 +1,111 @@ +--- +# We use sentence case and present imperative tone +title: "Directives" +# Weights are assigned in increments of 100: determines sorting order +weight: 150 +# Creates a table of contents and sidebar, useful for large documents +toc: true +# Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this +nd-content-type: reference +# Intended for internal catalogue and search, case sensitive: +# Agent, N4Azure, NIC, NIM, NGF, NAP-DOS, NAP-WAF, NGINX One, NGINX+, Solutions, Unit +nd-product: NAP-WAF +--- + +## Global directives + +Global configuration consists of a series of `nginx.conf` directives at the `http` context controlling aspects that are not specific to a specific application. + +When applied to a cluster, all cluster members will get the same globals as expected. + +{{< call-out "note" >}} Whether an incoming request is inspected by F5 WAF for NGINX may be determined by the URL in the request. This happens if you configure `app_protect_enable` and `app_protect_policy_file` directives in the `location` scope. In the case where the URL itself has violations such as *bad unescape* or *illegal metacharacter* then the request might be assigned to a location in which F5 WAF for NGINX is disabled or has a relaxed policy that does not detect these violations. Such malicious requests will be allowed without inspection. In order to avoid this, it is recommended to have a basic policy enabled at the `http` scope or at least at the `server` scope to process malicious requests in a more complete manner.{{< /call-out >}} + +{{< bootstrap-table "table table-striped table-bordered table-sm table-responsive" >}} +|Directive Name | Syntax | Description | Default | +| ---| ---| ---| --- | +|app_protect_physical_memory_util_thresholds | app_protect_physical_memory_util_thresholds high= low= | Sets the physical memory utilization thresholds for entering (high) and exiting (low) failure mode. When the high threshold is exceeded the system enters failure mode until memory drops below the low threshold. Setting the value of 100 disables this feature. | high=low=100 (disabled) | +|app_protect_cpu_thresholds | app_protect_cpu_thresholds high= low= | Sets the CPU utilization thresholds for entering and exiting failure mode respectively: when the high threshold is exceeded the system enters failure mode until CPU drops below the low threshold. Setting the value of 100 disables this feature.
**Note**: The system does not enter failure mode during policy compilation after reload even if the threshold is exceeded. | high=low=100 (disabled) | +|app_protect_failure_mode_action | app_protect_failure_mode_action pass | drop | How to handle requests when the App Protect Enforcer cannot process them, either because it is down, disconnected or because of excessive CPU or memory utilization. There are two values:
  • **pass**: Pass the request without App Protect Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by returning the response "503 Service Unavailable", a.k.a. "fail-close".
| pass | +|app_protect_cookie_seed | app_protect_cookie_seed | A long randomized string that serves to generate the encryption key for the cookies generated by App Protect. The string should contain only alphanumeric characters and be no longer than 1000 characters. | Auto-generated random string | +|app_protect_compressed_requests_action | app_protect_compressed_requests_action pass | drop | Determines how to handle compressed requests. There are two values:
  • **pass**: Pass the request without App Protect Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by returning the response "501 Not Implemented", a.k.a. "fail-close".
**Note**: Starting with App Protect release version 4.6, this directive has been deprecated from the `nginx.conf` file. | drop | +|app_protect_request_buffer_overflow_action | app_protect_request_buffer_overflow_action pass | drop | Determines how to handle requests in case the NGINX request buffer is full and requests cannot be buffered anymore. There are two values:
  • **pass**: Pass the request without App Protect Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by resetting connection. No response page is returned, a.k.a. "fail-close".
| pass | +|app_protect_user_defined_signatures | app_protect_user_defined_signatures | Imports the user-defined tagged signature file with the respective tag name from the provided path. Multiple instances of this directive are supported. In order to import multiple signatures files, each file must have a different tag. | N/A | +|app_protect_reconnect_period_seconds| app_protect_reconnect_period_seconds
**Value type**: number with decimal fraction
**Value Range**: 0-60. 0 is illegal | Determines the period of time between reconnect retries of the module to the web application firewall (WAF) engine. The time unit is seconds.| 5 | +{{< /bootstrap-table >}} + +### App Protect Specific Directives + +This table summarizes the nginx.conf directives for F5 WAF for NGINX functionality. + +{{}} +|Directive Name | Syntax | Functionality | nginx.conf Contexts | Example | +| ---| ---| ---| ---| --- | +|load_module | load_module | NGINX directive to load the App Protect module. It must be invoked with the App Protect library path | Global | load_module modules/ngx_http_app_protect_module.so | +|app_protect_enable | app_protect_enable on | off | Whether to enable App Protect at the respective context. If not present, inherits from the parent context | HTTP, Server, Location | app_protect_enable on | +|app_protect_policy_file | app_protect_policy_file | Set a App Protect policy configuring behavior for the respective context. | HTTP, Server, Location | app_protect_policy_file /config/waf/strict_policy.json | +|app_protect_security_log_enable | app_protect_security_log_enable on | off | Whether to enable the App Protect per-request log at the respective context. | HTTP, Server, Location | app_protect_security_log_enable on | +|app_protect_security_log | app_protect_security_log | Specifies the per-request logging: what to log and where | HTTP, Server, Location | app_protect_security_log /config/waf/log_illegal.json syslog:localhost:522 | +|app_protect_custom_log_attribute | app_protect_custom_log_attribute | Specifies the assigned location/server/http dimension of each request. | HTTP, Server, Location | app_protect_custom_log_attribute ‘environment' 'env1' | +{{}} + +#### Horizontal Scaling + +F5 WAF for NGINX can be deployed in multiple instances that share the traffic to the same applications. In that case all the instances must share the same configuration files. It is your responsibility to synchronize the files on all instances. You also have to provide a load balancing solution in front of those instances such as another NGINX instance. + +When deploying multiple scalability instances you have to add the `app_protect_cookie_seed` directive to nginx.conf in the `http` block: + +```nginx +... +http { + ... + app_protect_cookie_seed jkldsf90upiokasdj120; + ... + server { + listen 80; +... +} +... +``` + +As the argument of this directive, put a random alphanumeric string of at least 20 characters length (but not more than 1000 characters). That seed is used by F5 WAF for NGINX to generate the encryption key for the cookies it creates. These cookies are used for various purposes such as validating the integrity of the cookies generated by the application. + +In the absence of this directive, App Protect generates a random string by itself. In that case, each instance will have a different seed. A cookie created and encrypted on one instance of App Protect will fail to be decrypted when sent by the same client to another App Protect instance having a different encryption key. + + +#### Failure Mode + +If the App Protect daemons are down or disconnected from the NGINX workers, there are two modes of operation until they are up and connected again: + +- **Pass** the traffic without inspection. Use this when preferring availability over security. This mode is also known as "fail open". +- **Drop** the traffic. Use this when preferring security over availability. This mode is also known as "fail closed". + +The default is to **pass**, fail open, but you can control this using the `app_protect_failure_mode_action` directive with one argument with two possible values: "pass" or "fail" for the two above options. + +This directive is also placed in the `http` block of the nginx.conf file. + +```nginx +... +http { + ... + app_protect_failure_mode_action drop; + ... + server { + listen 80; +... + } +... +``` + +#### Handling Compressed Requests + +Starting with F5 WAF for NGINX release version 4.6, the [`app_protect_compressed_requests_action`](#global-directives) directive has been deprecated from the nginx configuration. When configuring this directive in the `nginx.conf` file, App Protect will disregard any previously used values ("pass" or "drop") and issue a warning. + +#### Handling Decompression + +Now by default the enforcer will decompress all the HTTP compressed payload request and will apply the enforcment. The supported compression algorithms for this feature are "**gzip**" and "**deflate**". There will be no decompression, if the compression method is not supported. + +The 'Content-Encoding' header must match the compression algorithm used while sending compressed payload in a HTTP request, else the enfocer will fail to decompress the payload. + +The decompressed request must not exceed the size limit of 10 MB. If it does exceed this limit, F5 WAF for NGINX will only decompress the first 10 KB, ignoring the remainder, and trigger the `VIOL_REQUEST_MAX_LENGTH` violation, just as it would for an uncompressed request that exceeds 10 MB. + +In the cases where decompression fails, F5 WAF for NGINX will continue with the scan in the same manner as it does for uncompressed requests. \ No newline at end of file diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md new file mode 100644 index 000000000..8d71e622d --- /dev/null +++ b/content/waf/policies/external-references.md @@ -0,0 +1,13 @@ +--- +# We use sentence case and present imperative tone +title: "External references" +# Weights are assigned in increments of 100: determines sorting order +weight: 200 +# Creates a table of contents and sidebar, useful for large documents +toc: true +# Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this +nd-content-type: reference +# Intended for internal catalogue and search, case sensitive: +# Agent, N4Azure, NIC, NIM, NGF, NAP-DOS, NAP-WAF, NGINX One, NGINX+, Solutions, Unit +nd-product: NAP-WAF +--- \ No newline at end of file diff --git a/content/waf/policies/violations.md b/content/waf/policies/violations.md index 2317be48f..e1ab3fece 100644 --- a/content/waf/policies/violations.md +++ b/content/waf/policies/violations.md @@ -2,7 +2,7 @@ # We use sentence case and present imperative tone title: "Violations" # Weights are assigned in increments of 100: determines sorting order -weight: 150 +weight: 250 # Creates a table of contents and sidebar, useful for large documents toc: true # Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this From f64a43f68bb9faaddb7752e46f1b1943cebb6bea Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Mon, 6 Oct 2025 15:58:01 +0100 Subject: [PATCH 03/17] feat: Push work --- content/waf/policies/directives.md | 104 ++++++++++++------- content/waf/policies/override-rules.md | 101 ++++++++++-------- content/waf/policies/user-urls-parameters.md | 1 - 3 files changed, 124 insertions(+), 82 deletions(-) diff --git a/content/waf/policies/directives.md b/content/waf/policies/directives.md index 20653da74..bb071a5f4 100644 --- a/content/waf/policies/directives.md +++ b/content/waf/policies/directives.md @@ -12,47 +12,60 @@ nd-content-type: reference nd-product: NAP-WAF --- +This topic describes the global NGINX directives and directives specific to F5 WAF for NGINX. + + ## Global directives -Global configuration consists of a series of `nginx.conf` directives at the `http` context controlling aspects that are not specific to a specific application. +Global configuration consists of a series of `nginx.conf` directives at the `http` context controlling aspects that are not specific to a specific application. When applied to a cluster, all cluster members will get the same globals. + +The URL in a request determines whether or not it will be inspected by F5 WAF for NGINX. This is defined by `app_protect_enable` and `app_protect_policy_file` directives in the `location` scope. + +In the case that the URL itself has violations such as *bad unescape* or *illegal metacharacter*, the request may be assigned to a location in which F5 WAF for NGINX is disabled or has a relaxed policy that does not detect these violations. -When applied to a cluster, all cluster members will get the same globals as expected. +Such malicious requests will be allowed without inspection. -{{< call-out "note" >}} Whether an incoming request is inspected by F5 WAF for NGINX may be determined by the URL in the request. This happens if you configure `app_protect_enable` and `app_protect_policy_file` directives in the `location` scope. In the case where the URL itself has violations such as *bad unescape* or *illegal metacharacter* then the request might be assigned to a location in which F5 WAF for NGINX is disabled or has a relaxed policy that does not detect these violations. Such malicious requests will be allowed without inspection. In order to avoid this, it is recommended to have a basic policy enabled at the `http` scope or at least at the `server` scope to process malicious requests in a more complete manner.{{< /call-out >}} +In order to avoid this, it is recommended to have a basic policy enabled at the `http` scope or at least at the `server` scope to process malicious requests in a more complete manner. -{{< bootstrap-table "table table-striped table-bordered table-sm table-responsive" >}} -|Directive Name | Syntax | Description | Default | -| ---| ---| ---| --- | -|app_protect_physical_memory_util_thresholds | app_protect_physical_memory_util_thresholds high= low= | Sets the physical memory utilization thresholds for entering (high) and exiting (low) failure mode. When the high threshold is exceeded the system enters failure mode until memory drops below the low threshold. Setting the value of 100 disables this feature. | high=low=100 (disabled) | -|app_protect_cpu_thresholds | app_protect_cpu_thresholds high= low= | Sets the CPU utilization thresholds for entering and exiting failure mode respectively: when the high threshold is exceeded the system enters failure mode until CPU drops below the low threshold. Setting the value of 100 disables this feature.
**Note**: The system does not enter failure mode during policy compilation after reload even if the threshold is exceeded. | high=low=100 (disabled) | -|app_protect_failure_mode_action | app_protect_failure_mode_action pass | drop | How to handle requests when the App Protect Enforcer cannot process them, either because it is down, disconnected or because of excessive CPU or memory utilization. There are two values:
  • **pass**: Pass the request without App Protect Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by returning the response "503 Service Unavailable", a.k.a. "fail-close".
| pass | -|app_protect_cookie_seed | app_protect_cookie_seed | A long randomized string that serves to generate the encryption key for the cookies generated by App Protect. The string should contain only alphanumeric characters and be no longer than 1000 characters. | Auto-generated random string | -|app_protect_compressed_requests_action | app_protect_compressed_requests_action pass | drop | Determines how to handle compressed requests. There are two values:
  • **pass**: Pass the request without App Protect Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by returning the response "501 Not Implemented", a.k.a. "fail-close".
**Note**: Starting with App Protect release version 4.6, this directive has been deprecated from the `nginx.conf` file. | drop | -|app_protect_request_buffer_overflow_action | app_protect_request_buffer_overflow_action pass | drop | Determines how to handle requests in case the NGINX request buffer is full and requests cannot be buffered anymore. There are two values:
  • **pass**: Pass the request without App Protect Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by resetting connection. No response page is returned, a.k.a. "fail-close".
| pass | -|app_protect_user_defined_signatures | app_protect_user_defined_signatures | Imports the user-defined tagged signature file with the respective tag name from the provided path. Multiple instances of this directive are supported. In order to import multiple signatures files, each file must have a different tag. | N/A | +{{< table >}} + +| Name | Syntax | Description | Default | +| ---- | -------| ----------- | ------- | +| app_protect_physical_memory_util_thresholds | app_protect_physical_memory_util_thresholds high= low= | Sets the physical memory utilization thresholds for entering (high) and exiting (low) failure mode. When the high threshold is exceeded the system enters failure mode until memory drops below the low threshold. Setting the value of 100 disables this feature. | high=low=100 (disabled) | +| app_protect_cpu_thresholds | app_protect_cpu_thresholds high= low= | Sets the CPU utilization thresholds for entering and exiting failure mode respectively: when the high threshold is exceeded the system enters failure mode until CPU drops below the low threshold. Setting the value of 100 disables this feature.
**Note**: The system does not enter failure mode during policy compilation after reload even if the threshold is exceeded. | high=low=100 (disabled) | +| app_protect_failure_mode_action | app_protect_failure_mode_action pass | drop | How to handle requests when the F5 WAF for NGINX Enforcer cannot process them, either because it is down, disconnected or because of excessive CPU or memory utilization. There are two values:
  • **pass**: Pass the request without F5 WAF for NGINX Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by returning the response "503 Service Unavailable", a.k.a. "fail-close".
| pass | +| app_protect_cookie_seed | app_protect_cookie_seed | A long randomized string that serves to generate the encryption key for the cookies generated by F5 WAF for NGINX. The string should contain only alphanumeric characters and be no longer than 1000 characters. | Auto-generated random string | +| app_protect_compressed_requests_action | app_protect_compressed_requests_action pass | drop | Determines how to handle compressed requests. There are two values:
  • **pass**: Pass the request without F5 WAF for NGINX Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by returning the response "501 Not Implemented", a.k.a. "fail-close".
**Note**: Starting with F5 WAF for NGINX release version 4.6, this directive has been deprecated from the `nginx.conf` file. | drop | +| app_protect_request_buffer_overflow_action | app_protect_request_buffer_overflow_action pass | drop | Determines how to handle requests in case the NGINX request buffer is full and requests cannot be buffered anymore. There are two values:
  • **pass**: Pass the request without F5 WAF for NGINX Enforcer inspection, a.k.a. "fail-open".
  • **drop**: Drop the request by resetting connection. No response page is returned, a.k.a. "fail-close".
| pass | +| app_protect_user_defined_signatures | app_protect_user_defined_signatures | Imports the user-defined tagged signature file with the respective tag name from the provided path. Multiple instances of this directive are supported. In order to import multiple signatures files, each file must have a different tag. | N/A | |app_protect_reconnect_period_seconds| app_protect_reconnect_period_seconds
**Value type**: number with decimal fraction
**Value Range**: 0-60. 0 is illegal | Determines the period of time between reconnect retries of the module to the web application firewall (WAF) engine. The time unit is seconds.| 5 | -{{< /bootstrap-table >}} -### App Protect Specific Directives +{{< /table >}} + +## F5 WAF for NGINX directives + +{{< table >}} -This table summarizes the nginx.conf directives for F5 WAF for NGINX functionality. +| Name | Syntax | Functionality | Contexts | Example | +| ---- | ------ | ------------- | -------- | ------- | +| load_module | load_module | NGINX directive to load the F5 WAF for NGINX module. It must be invoked with the F5 WAF for NGINX library path | Global | load_module modules/ngx_http_app_protect_module.so | +| app_protect_enable | app_protect_enable on | off | Whether to enable F5 WAF for NGINX at the respective context. If not present, inherits from the parent context | HTTP, Server, Location | app_protect_enable on | +| app_protect_policy_file | app_protect_policy_file | Set a F5 WAF for NGINX policy configuring behavior for the respective context. | HTTP, Server, Location | app_protect_policy_file /config/waf/strict_policy.json | +| app_protect_security_log_enable | app_protect_security_log_enable on | off | Whether to enable the F5 WAF for NGINX per-request log at the respective context. | HTTP, Server, Location | app_protect_security_log_enable on | +| app_protect_security_log | app_protect_security_log | Specifies the per-request logging: what to log and where | HTTP, Server, Location | app_protect_security_log /config/waf/log_illegal.json syslog:localhost:522 | +| app_protect_custom_log_attribute | app_protect_custom_log_attribute | Specifies the assigned location/server/http dimension of each request. | HTTP, Server, Location | app_protect_custom_log_attribute ‘environment' 'env1' | -{{}} -|Directive Name | Syntax | Functionality | nginx.conf Contexts | Example | -| ---| ---| ---| ---| --- | -|load_module | load_module | NGINX directive to load the App Protect module. It must be invoked with the App Protect library path | Global | load_module modules/ngx_http_app_protect_module.so | -|app_protect_enable | app_protect_enable on | off | Whether to enable App Protect at the respective context. If not present, inherits from the parent context | HTTP, Server, Location | app_protect_enable on | -|app_protect_policy_file | app_protect_policy_file | Set a App Protect policy configuring behavior for the respective context. | HTTP, Server, Location | app_protect_policy_file /config/waf/strict_policy.json | -|app_protect_security_log_enable | app_protect_security_log_enable on | off | Whether to enable the App Protect per-request log at the respective context. | HTTP, Server, Location | app_protect_security_log_enable on | -|app_protect_security_log | app_protect_security_log | Specifies the per-request logging: what to log and where | HTTP, Server, Location | app_protect_security_log /config/waf/log_illegal.json syslog:localhost:522 | -|app_protect_custom_log_attribute | app_protect_custom_log_attribute | Specifies the assigned location/server/http dimension of each request. | HTTP, Server, Location | app_protect_custom_log_attribute ‘environment' 'env1' | -{{}} +{{< /table >}} -#### Horizontal Scaling +### Horizontal scaling -F5 WAF for NGINX can be deployed in multiple instances that share the traffic to the same applications. In that case all the instances must share the same configuration files. It is your responsibility to synchronize the files on all instances. You also have to provide a load balancing solution in front of those instances such as another NGINX instance. +F5 WAF for NGINX can be deployed in multiple instances that share the traffic to the same applications. -When deploying multiple scalability instances you have to add the `app_protect_cookie_seed` directive to nginx.conf in the `http` block: +In this case, all instances must share the same configuration files. + +It is your responsibility to synchronize the files on all instances. You must also load balancing each of those instances, such as using additional NGINX instances. + +When deploying multiple scalability instances add the `app_protect_cookie_seed` directive to nginx.conf in the `http` block: ```nginx ... @@ -67,14 +80,17 @@ http { ... ``` -As the argument of this directive, put a random alphanumeric string of at least 20 characters length (but not more than 1000 characters). That seed is used by F5 WAF for NGINX to generate the encryption key for the cookies it creates. These cookies are used for various purposes such as validating the integrity of the cookies generated by the application. +The argument for the directive should be a random alphanumeric string of at least 20 characters length (Maximum 1000 characters). -In the absence of this directive, App Protect generates a random string by itself. In that case, each instance will have a different seed. A cookie created and encrypted on one instance of App Protect will fail to be decrypted when sent by the same client to another App Protect instance having a different encryption key. +This is a seed used by F5 WAF for NGINX to generate the encryption key for the cookies it creates. These cookies are used for various purposes such as validating the integrity of the cookies generated by the application. +In the absence of this directive, F5 WAF for NGINX generates a random string by itself. In that case, each instance will have a different seed. -#### Failure Mode +A cookie created and encrypted on one instance of F5 WAF for NGINX will fail to be decrypted when sent by the same client to another F5 WAF for NGINX instance having a different encryption key. -If the App Protect daemons are down or disconnected from the NGINX workers, there are two modes of operation until they are up and connected again: +### Failure modes + +If the F5 WAF for NGINX daemons are down or disconnected from the NGINX workers, there are two modes of operation until they are up and connected again: - **Pass** the traffic without inspection. Use this when preferring availability over security. This mode is also known as "fail open". - **Drop** the traffic. Use this when preferring security over availability. This mode is also known as "fail closed". @@ -96,16 +112,26 @@ http { ... ``` -#### Handling Compressed Requests +## Handling compressed requests + +{{< call-out "warning" >}} + +From F5 WAF for NGINX release version 4.6, the `app_protect_compressed_requests_action` directive was removed deprecated from the nginx configuration. + +When configuring this directive in the `nginx.conf` file, F5 WAF for NGINX will disregard any previously used values ("pass" or "drop") and issue a warning. + +{{< /call-out >}} -Starting with F5 WAF for NGINX release version 4.6, the [`app_protect_compressed_requests_action`](#global-directives) directive has been deprecated from the nginx configuration. When configuring this directive in the `nginx.conf` file, App Protect will disregard any previously used values ("pass" or "drop") and issue a warning. +By default, the enforcer will now decompress all the HTTP compressed payload request and will apply the enforcment. -#### Handling Decompression +The supported compression algorithms for this feature are "**gzip**" and "**deflate**". -Now by default the enforcer will decompress all the HTTP compressed payload request and will apply the enforcment. The supported compression algorithms for this feature are "**gzip**" and "**deflate**". There will be no decompression, if the compression method is not supported. +Decompression may fail under certain conditions: -The 'Content-Encoding' header must match the compression algorithm used while sending compressed payload in a HTTP request, else the enfocer will fail to decompress the payload. +- If the compression method is not supported +- If the 'Content-Encoding' header does not match the compression algorithm used while sending a compressed payload in a HTTP request +- If thehe decompressed request is larger than 10 MB -The decompressed request must not exceed the size limit of 10 MB. If it does exceed this limit, F5 WAF for NGINX will only decompress the first 10 KB, ignoring the remainder, and trigger the `VIOL_REQUEST_MAX_LENGTH` violation, just as it would for an uncompressed request that exceeds 10 MB. +If it does exceed this limit, F5 WAF for NGINX will only decompress the first 10 KB, ignoring the remainder, and trigger the `VIOL_REQUEST_MAX_LENGTH` violation, just as it would for an uncompressed request that exceeds 10 MB. In the cases where decompression fails, F5 WAF for NGINX will continue with the scan in the same manner as it does for uncompressed requests. \ No newline at end of file diff --git a/content/waf/policies/override-rules.md b/content/waf/policies/override-rules.md index 7e573f5ea..db0f18e04 100644 --- a/content/waf/policies/override-rules.md +++ b/content/waf/policies/override-rules.md @@ -7,11 +7,30 @@ nd-product: NAP-WAF nd-docs: DOCS-000 --- -The **Override Rules** feature allows overriding of the **default policy** settings. Each override rule consists of a condition followed by changes to the original policy applied to requests that meet the respective condition. This feature provides the ability to include the override rules within a declarative policy such that all incoming requests are verified against those rules. +This topic describes override rules feature of F5 WAF for NGINX. -With this enhancement, users now have more control over how a unique policy setting is applied to incoming requests with a specific method, source IP address, header or URI value through one or multiple unique override rules. Each override rule possesses a unique name and specific conditions that are matched against incoming traffic from a specific client side. The structure of these override rules adheres to the JSON schema defined by the declarative policy. +Override rules allow you to replace **default policy** settings. These override rules can be included as part of a declarative policy so that all incoming requests are verified against those rules. -Here is an example of a declarative policy using an override rules entity: +Each override rule consists of a condition followed by changes to the original policy which will apply to requests that meet the condition. + +You can apply this feature to apply unique policy settings to incoming requests with one or more rules based on multiple parameters: + +- Method +- Source IP address +- Header values +- URI values + +To ensure efficient compilation time and optimal resource allocation for policies, policies have a maximum limit of 10 rules and a maximum of 5 clauses in a condition. A compilation error will not occur if a policy file contains more than 5 clauses or 10 overrides. + +The replacement policy should not include any override rules. Override rules should be used to extend or switch to a different policy, rather than being part of the replacement policy itself. + +Each override rule is compiled as a separate policy, whether extending the main policy or switching to a new one. The enforcer will switch to the policy that corresponds to the matched rule, but the main policy name will be reported along with the override rule property. + +The URI, host, and user-agent strings in the request are treated as plain ASCII characters and won't undergo language decoding. If any of these strings contain non-ASCII characters, they may be misinterpreted and may not comply with rules that expect specific values in the conditions. + +The structure of these override rules adheres to the JSON schema defined by the declarative policy. + +An example could look like the following: ```shell { @@ -91,27 +110,35 @@ Here is an example of a declarative policy using an override rules entity: } ``` -The above "override_rules_example" policy contains five override rules: +The _override_rules_example_ example policy contains five override rules: 1. The **"localhost-log-only"** rule applies to the requests with a user agent header starting with "curl", a host header containing "localhost", and a client IP address set to 127.0.0.1. It switches the enforcement mode to "transparent" without blocking the request. The remaining policy settings remain unchanged. This type of override rule is an example of an **Inline Policy Reference**. -2. The **"login_page"** rule is triggered by POST requests to URIs containing "/login/". Since the "actionType" field is set to "replace-policy", it overrides the policy with a new one named "login_page_block_redirect". This new policy is independent of the "override_rules_example" policy. It enables all signature sets and redirects the user to a rejection page. This is another example of an **Inline Policy Reference** with a different condition. -3. The **"api-strict"** rule is applied for requests with "api4" in the URI, except for client IP addresses matching the "fd00:1::/48" range and user agents starting with "Mozilla". It references an external policy file named "NginxStrictPolicy.json" located at "/etc/app_protect/conf/" to override the current policy. The "actionType" field is set to "replace-policy" and the external policy can be specified using a reference to its file using **$ref**. The file is the JSON policy source of that policy. This type of policy switching is known as **External Policy Reference**. -4. The **"strict-post"** rule is triggered when POST requests include a session token in the cookies that is not equal to "c2Vzc2lvblRva2Vu" or when the "gzip" value is found in the content-encoding headers. This rule follows a similar approach to referencing an external policy file, just like the **api-strict** rule mentioned above. -5. The **"usa-only"** rule is triggered when a request coming from a country other than the USA. The actionType is set to "violation", meaning that `VIOL_RULE` violation is triggered for such a request. This violation will block and mark the request as illegal with regard to the "block" and "alarm" attributes. There is no change in policy for this rule. For more details about **Geolocation** feature, see [Geolocation in Policy Override Rules Conditions](#geolocation-in-policy-override-rules-conditions). +1. The **"login_page"** rule is triggered by POST requests to URIs containing "/login/". Since the "actionType" field is set to "replace-policy", it overrides the policy with a new one named "login_page_block_redirect". This new policy is independent of the "override_rules_example" policy. It enables all signature sets and redirects the user to a rejection page. This is another example of an **Inline Policy Reference** with a different condition. +1. The **"api-strict"** rule is applied for requests with "api4" in the URI, except for client IP addresses matching the "fd00:1::/48" range and user agents starting with "Mozilla". It references an external policy file named "NginxStrictPolicy.json" located at "/etc/app_protect/conf/" to override the current policy. The "actionType" field is set to "replace-policy" and the external policy can be specified using a reference to its file using **$ref**. The file is the JSON policy source of that policy. This type of policy switching is known as **External Policy Reference**. +1. The **"strict-post"** rule is triggered when POST requests include a session token in the cookies that is not equal to "c2Vzc2lvblRva2Vu" or when the "gzip" value is found in the content-encoding headers. This rule follows a similar approach to referencing an external policy file, just like the **api-strict** rule mentioned above. +1. The **"usa-only"** rule is triggered when a request coming from a country other than the USA. The actionType is set to "violation", meaning that `VIOL_RULE` violation is triggered for such a request. This violation will block and mark the request as illegal with regard to the "block" and "alarm" attributes. There is no change in policy for this rule. These five rules demonstrate how the override rules feature allows for customization and the ability to modify specific aspects of the original policy based on predefined conditions. +For more details about the **Geolocation** feature, view the [Geolocation]({{< ref "/waf/policies/geolocation.md" >}}) topic. + +{{< call-out "note" >}} + +By default, the actionType field is configured to "extend-policy". + +External references are supported for any policy reference. -{{< call-out "note" >}} -- By default, the actionType field is configured to "extend-policy". -- External references are supported for any policy reference. {{< /call-out >}} -### First Match Principle +### First match principle -The policy enforcement operates on the **first match** principle. This principle is applied when multiple conditions match or are similar, indicating that any incoming requests that match the first condition will be processed. In the following example, the "override_rules_example2" policy uses two override rules: "this_rule_will_match" and "non_matching_rule". Since both conditions match, the first match principle will be applied, and requests with "api" in the URI will be processed. It will reference an external policy file named "NginxStrictPolicy.json" to override the current policy. . +Policy enforcement operates on the **first match** principle. This principle is applied when multiple conditions match or are similar, indicating that any incoming requests that match the first condition will be processed. -For example: +In the following example, the _override_rules_example2_ policy uses two override rules: `this_rule_will_match` and `non_matching_rule`. + +Since both conditions match, the first match principle will be applied, and requests with "_api_" in the URI will be processed. + +It will reference the external policy file named _NginxStrictPolicy.json_ to override the current policy. ```shell { @@ -144,29 +171,21 @@ For example: } ``` -### Important Things to Remember About Override Rules - -Here are some key points to remember regarding the Override Rules feature: +### Logging & reporting override rules -- To ensure efficient compilation time and optimal resource allocation for policies, there are limitations in place. Currently, policies have a maximum limit of 10 rules and a maximum of 5 clauses in a condition. These limitations help maintain better performance and manageability. A compilation error will not occur if a policy file contains more than 5 clauses or 10 overrides. -- The replacement policy should not include any override rules. Override rules should be used to extend or switch to a different policy, rather than being part of the replacement policy itself. -- Each override rule will be compiled as a separate policy, whether extending the main policy or switching to a new one. The enforcer will switch to the policy that corresponds to the matched rule, but the main policy name will be reported along with the override rule property. -- The URI, host, and user-agent strings in the request will be treated as plain ASCII characters and won't undergo language decoding. If any of these strings contain non-ASCII characters, they may be misinterpreted and may not comply with rules that expect specific values in the conditions. +If a request matches an override rule, the `json_log` field will include a new block named 'overrideRule'. However, if no rules match the request, the log will not contain any related information. -### Override Rules Logging & Reporting - -If a request matches an override rule, the `json_log` field will include a new block named 'overrideRule'. However, if no rules match the request, the log will not contain any related information. When the 'actionType' flag is set to "replace-policy", the 'originalPolicyName' field in the log will reflect the name of the original policy name (the one that contains override rules), and the `policy_name` field will reflect the policy that was enforced. +When the 'actionType' flag is set to _replace-policy_, the _originalPolicyName_ field in the log will reflect the name of the original policy name (Which contains override rules), and the `policy_name` field will reflect the policy that was enforced. For example, if the matching override rule is called "login_page": ```shell - -... policy_name="login_page_block_redirect" -... +``` -json_log will have: +_json_log_ will have: +```JSON { ... "overrideRule": { @@ -177,10 +196,11 @@ json_log will have: ``` -If the matching override rule is called "usa-only": +If the matching override rule is called _usa-only_: ```shell { +... "enforcementState": { "isBlocked": true, "isAlarmed": true, @@ -201,17 +221,19 @@ If the matching override rule is called "usa-only": }, "description": "Trying to access special" }, - +... ``` -### Errors and Warnings +### Errors and warnings + +#### Missing policy name -#### Missing Policy Name +Every policy must have a name if the actionType is `extend-policy` or `replace-policy`. -Every policy must have a name if actionType is either "extend-policy" or "replace-policy". If the policy 'name' is not provided in the override section, an error message will be displayed indicating the missing policy 'name' within that specific override rule. For instance, in the override rule below, the policy name is not specified. +If the policy `name` is not provided in the override section, an error message will be displayed indicating the missing policy 'name' within that specific override rule. +For instance, in the override rule below, the policy name is not specified. -Example of Missing policy 'name': ```shell "override-rules": [ @@ -228,19 +250,14 @@ Example of Missing policy 'name': } ] ``` - -Example of Missing policy 'name' error: - ```shell "error_message": "Failed to import Policy 'policy1' from '/etc/app_protect/conf/test.json': Missing policy 'name' in the override rule 'example-rule'." ``` -#### Cyclic Override Rule Error - -If an inline or externally referenced policy contains an override rule, a Cyclic Override Rule error will be issued. - -Example of Cyclic Override Rule error: +#### Cyclic override rule error +If an inline or externally referenced policy contains an override rule, a cyclic override rule error will be issued. + ```shell "error_message": "Failed to import an override policy: Cyclic override-rules detected." ``` \ No newline at end of file diff --git a/content/waf/policies/user-urls-parameters.md b/content/waf/policies/user-urls-parameters.md index 548ffa2b0..7875cf5f1 100644 --- a/content/waf/policies/user-urls-parameters.md +++ b/content/waf/policies/user-urls-parameters.md @@ -14,7 +14,6 @@ nd-product: NAP-WAF This topic describes the user-defined URLs and parameters feature of F5 WAF for NGINX. - ## URLs User-defined URLs allows you to configure a URL with the following options: From 29d600fbfc72f1e9465ec256277cffc1844d9597 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Mon, 6 Oct 2025 16:08:00 +0100 Subject: [PATCH 04/17] feat: Add parameter reference notice --- _banners/waf-parameter-reference.md | 7 +++++++ content/waf/policies/_index.md | 6 ++++++ 2 files changed, 13 insertions(+) create mode 100644 _banners/waf-parameter-reference.md diff --git a/_banners/waf-parameter-reference.md b/_banners/waf-parameter-reference.md new file mode 100644 index 000000000..a4c60c235 --- /dev/null +++ b/_banners/waf-parameter-reference.md @@ -0,0 +1,7 @@ +{{< banner "note" "Policy parameter reference" >}} + +You can explore the parameters for each F5 WAF for NGINX feature on the [Policy parameter reference]({{< ref "/waf/policies/parameter-reference.md" >}}) page. + +This page was previously referred to as the "Declarative Policy". + +{{< /banner >}} diff --git a/content/waf/policies/_index.md b/content/waf/policies/_index.md index 1c0cce495..7173947ab 100644 --- a/content/waf/policies/_index.md +++ b/content/waf/policies/_index.md @@ -1,5 +1,11 @@ --- title: "Policies" url: /waf/policies/ +cascade: + nd-banner: + enabled: true + type: deprecation + start-date: 2025-09-30 + md: /_banners/waf-parameter-reference.md weight: 400 --- From 03d3dad4b2e74e189f8c71c7d4f1dab1c4e79631 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Mon, 6 Oct 2025 16:42:21 +0100 Subject: [PATCH 05/17] feat: External references work --- content/waf/policies/external-references.md | 552 +++++++++++++++++++- 1 file changed, 551 insertions(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index 8d71e622d..07fb0a6bb 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -10,4 +10,554 @@ nd-content-type: reference # Intended for internal catalogue and search, case sensitive: # Agent, N4Azure, NIC, NIM, NGF, NAP-DOS, NAP-WAF, NGINX One, NGINX+, Solutions, Unit nd-product: NAP-WAF ---- \ No newline at end of file +--- + +This topic describes the external references feature for F5 WAF for NGINX. + +External references in policy are code blocks that can be used as part of a policy without being part of the policy file. This means that you can have a set of pre-defined configurations for parts of the policy, and you can incorporate them as part of the policy by simply referencing them. + +It allows you to separate policy information into smaller files, which can be easier to maintain for a large, complex policy. Another use case for external references is to build a dynamic policy that requires replaceable files. + +You can create and populate specific files with the configuration relevant to your policy, and then compile the policy to include the latest version of these files, ensuring that your policy is always up to date when it comes to a constantly changing environment. + +{{< call-out "note" >}} + +Updating a single file referenced in the policy will not trigger a policy compilation. This action needs to be done actively by reloading the NGINX configuration. + +{{< /call-out >}} + +To use external references, replaced the direct property in the policy file with the _\Reference_ property, where _\_ defines the replacement text for the property changed to singular (if originally plural) and notation converted from snake case to camelCase. + +For example, a `modifications` section could be replaced by `modificationsReference` and `data-guard` would could be replaced by `dataGuardReference`. + +## External reference types + +There are different implementations based on the type of references that are being made. + +### External URL reference + +URL reference is the method of referencing an external source by providing its full URL. + +This is a useful method when trying to combine or consolidate parts of the policy that are present on different host machines. + +{{< call-out "note" >}} + +You need to make sure that the server where the resource files are located is always available when you are compiling your policy. +{{< /call-out >}} + +This example creates a skeleton policy, enabling the file type violation. + +It does not specify the file types as these file types depend on a separate application that defines these types. It populates this section from an external reference instead. + +Note that the `filetypes` section is replaced by the `filetypeReference` section. In the `filetypeReference` section, there is **key/value** pair defining the type of reference and actual URL to use to reach that reference item. + +For the content of the file itself, it is an extension of the original JSON format for the policy. + +```json +{ + "name": "external_resources_file_types", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "blocking-settings": { + "violations": [ + { + "name": "VIOL_FILETYPE", + "alarm": true, + "block": true + } + ] + }, + "filetypeReference": { + "link": "http://domain.com:8081/file-types.txt" + } +} +``` + +The referenced `file-types.txt` file contains the following code: + +```json +[ + { + "name": "*", + "type": "wildcard", + "allowed": true, + "checkPostDataLength": false, + "postDataLength": 4096, + "checkRequestLength": false, + "requestLength": 8192, + "checkUrlLength": true, + "urlLength": 2048, + "checkQueryStringLength": true, + "queryStringLength": 2048, + "responseCheck": false + }, + { + "name": "pat", + "allowed": false + }, + { + "name": "mat", + "allowed": false + } +] +``` + +### HTTPS references + +HTTPS references are a special case of URL references. It uses the HTTPS protocol instead of the HTTP protocol. Make sure that the webserver you are downloading the resources from does also support HTTPS protocol and has certificates setup properly. + +- Certificates must be valid in date (not expired) during the policy compilation. +- Certificates must be signed by a trusted CA. +- For Self-signed certificates, you need to make sure to add your certificates to the trusted CA of the machine where App Protect is installed. +- Certificates must use the exact domain name that the certificate was issued for. For example, SSL will differentiate between domain.com and www.domain.com, considering each a different domain name. + +In this configuration, we are completely satisfied with the basic default policy, and we wish to use it as is. However, we wish to define a custom response page using an external file located on an HTTPS web server. The external reference file contains our custom response page configuration. + +**Policy configuration:** + +```json +{ + "name": "external_references_custom_respsonse", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "responsePageReference": { + "link": "https://securedomain.com:8081/response-pages.txt" + } +} +``` + +Content of the referenced file `response-pages.txt`: + +```json +[ + { + "responseContent": "Custom Reject PageThis is a custom response page, it is supposed to overwrite the default page with custom text.

Your support ID is: <%TS.request.ID()%>

[Go Back]", + "responseHeader": "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nConnection: close", + "responseActionType": "custom", + "responsePageType": "default" + } +] +``` + +In this example, we would like to enable all attack signatures. Yet, we want to exclude specific signatures from being enforced. + +**Policy configuration:** + +```json +{ + "policy": { + "name": "external_resources_signature_modification", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "signature-sets": [ + { + "name": "All Signatures", + "block": true, + "alarm": true + } + ] + }, + "modificationsReference": { + "link": "http://my-domain.com:8081/modifications.txt" + } +} +``` + +Content of the referenced file `modifications.txt`: + +```json +{ + "modifications": [ + { + "entityChanges": { + "enabled": false + }, + "entity": { + "signatureId": 200001834 + }, + "entityType": "signature", + "action": "add-or-update" + } + ] +} +``` + +### File references + +File references refers to accessing local resources on the same machine, as opposed to accessing a remote resource on another server/machine. The user can specify any location that is accessible by App Protect except for the root folder ("/"). If no full path is provided, the default path `/etc/app_protect/conf` will be assumed. Note that file references can only be on the local machine: you cannot use remote hosts! + +Here are some examples of the typical cases: + +| Examples | File path | Notes | +| -------- | --------- | ----- | +| | /etc/app_protect/conf/foo.json | Default directory assumed | +| | /etc/app_protect/conf/foo.json | Formally illegal, but tolerated as long as there is no trailing slash. | +| | /etc/app_protect/conf/foo.json | Full path, but still the default one | +| | /bar/foo.json | Non-default path | +| | **Not accepted** | "etc" is interpreted as remote host name | + + +In this example, we would like to enable all attack signatures. Yet, we want to exclude specific signatures from being enforced. To do this, we reference a local file on the machine. + +**Policy Configuration:** + +```json +{ + "policy": { + "name": "external_resources_signature_modification", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "signature-sets": [ + { + "name": "All Signatures", + "block": true, + "alarm": true + } + ] + }, + "modificationsReference": { + "link": "file:///modifications.txt" + } +} +``` + +Content of the referenced file `modifications.txt`: + +```json +{ + "modifications": [ + { + "entityChanges": { + "enabled": false + }, + "entity": { + "signatureId": 200001834 + }, + "entityType": "signature", + "action": "add-or-update" + } + ] +} +``` + +If, for any reason, the configuration was done incorrectly, the policy compilation process will fail with the following error: +```shell +APP_PROTECT { "event": "configuration_load_failure" ... +``` + +The error details that follow will depend on the exact situation causing the policy compilation to fail. If the policy compilation process fails, the compiler will revert to the last working policy and all the changes for the last policy compilation attempt will be lost. + +## OpenAPI specification file reference + +The OpenAPI Specification defines the spec file format needed to describe RESTful APIs. The spec file can be written either in JSON or YAML. Using a spec file simplifies the work of implementing API protection. Refer to the OpenAPI Specification (formerly called Swagger) for details. + +The simplest way to create an API protection policy is using an OpenAPI Specification file to import the details of the APIs. If you use an OpenAPI Specification file, F5 WAF for NGINX will automatically create a policy for the following properties (depending on what's included in the spec file): +* Methods +* URLs +* Parameters +* JSON profiles + +An OpenAPI-ready policy template is provided with the F5 WAF for NGINX packages and is located in: `/etc/app_protect/conf/NginxApiSecurityPolicy.json` + +It contains violations related to OpenAPI set to blocking (enforced). + +{{< call-out "note" >}} F5 WAF for NGINX supports only one OpenAPI Specification file reference per policy.{{< /call-out >}} + +### Types of OpenAPI References + +There are different ways of referencing OpenAPI Specification files. The configuration is similar to [External References](#external-references). + +{{< call-out "note" >}} Any update of an OpenAPI Specification file referenced in the policy will not trigger a policy compilation. This action needs to be done actively by reloading the NGINX configuration. {{< /call-out >}} + +#### URL Reference + +URL reference is the method of referencing an external source by providing its full URL. + +Make sure to configure certificates prior to using the HTTPS protocol - see the [HTTPS References](#https-reference) under the External References section for more details. + +{{< call-out "note" >}} You need to make sure that the server where the resource files are located is always available when you are compiling your policy. {{< /call-out >}} + +##### Example Configuration + +In this example, we are adding an OpenAPI Specification file reference using the link `http://127.0.0.1:8088/myapi.yaml`. This will configure allowed data types for `query_int` and `query_str` parameters values. + +**Policy configuration:** + +```json +{ + "policy": { + "name": "petstore_api_security_policy", + "description": "F5 WAF for NGINX API Security Policy for the Petstore API", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "open-api-files": [ + { + "link": "http://127.0.0.1:8088/myapi.yaml" + } + ], + "blocking-settings": { + "violations": [ + { + "block": true, + "description": "Disallowed file upload content detected in body", + "name": "VIOL_FILE_UPLOAD_IN_BODY" + }, + { + "block": true, + "description": "Mandatory request body is missing", + "name": "VIOL_MANDATORY_REQUEST_BODY" + }, + { + "block": true, + "description": "Illegal parameter location", + "name": "VIOL_PARAMETER_LOCATION" + }, + { + "block": true, + "description": "Mandatory parameter is missing", + "name": "VIOL_MANDATORY_PARAMETER" + }, + { + "block": true, + "description": "JSON data does not comply with JSON schema", + "name": "VIOL_JSON_SCHEMA" + }, + { + "block": true, + "description": "Illegal parameter array value", + "name": "VIOL_PARAMETER_ARRAY_VALUE" + }, + { + "block": true, + "description": "Illegal Base64 value", + "name": "VIOL_PARAMETER_VALUE_BASE64" + }, + { + "block": true, + "description": "Disallowed file upload content detected", + "name": "VIOL_FILE_UPLOAD" + }, + { + "block": true, + "description": "Illegal request content type", + "name": "VIOL_URL_CONTENT_TYPE" + }, + { + "block": true, + "description": "Illegal static parameter value", + "name": "VIOL_PARAMETER_STATIC_VALUE" + }, + { + "block": true, + "description": "Illegal parameter value length", + "name": "VIOL_PARAMETER_VALUE_LENGTH" + }, + { + "block": true, + "description": "Illegal parameter data type", + "name": "VIOL_PARAMETER_DATA_TYPE" + }, + { + "block": true, + "description": "Illegal parameter numeric value", + "name": "VIOL_PARAMETER_NUMERIC_VALUE" + }, + { + "block": true, + "description": "Parameter value does not comply with regular expression", + "name": "VIOL_PARAMETER_VALUE_REGEXP" + }, + { + "block": true, + "description": "Illegal URL", + "name": "VIOL_URL" + }, + { + "block": true, + "description": "Illegal parameter", + "name": "VIOL_PARAMETER" + }, + { + "block": true, + "description": "Illegal empty parameter value", + "name": "VIOL_PARAMETER_EMPTY_VALUE" + }, + { + "block": true, + "description": "Illegal repeated parameter name", + "name": "VIOL_PARAMETER_REPEATED" + } + ] + } + } +} +``` + +Content of the referenced file `myapi.yaml`: + +```yaml +openapi: 3.0.1 +info: + title: 'Primitive data types' + description: 'Primitive data types.' + version: '2.5.0' +servers: + - url: http://localhost +paths: + /query: + get: + tags: + - query_int_str + description: query_int_str + operationId: query_int_str + parameters: + - name: query_int + in: query + required: false + allowEmptyValue: false + schema: + type: integer + - name: query_str + in: query + required: false + allowEmptyValue: true + schema: + type: string + responses: + 200: + description: OK + 404: + description: NotFound +``` + +In this case the following request will trigger an `Illegal parameter data type` violation, as we expect to have an integer value in the `query_int` parameter: + +``` +http://localhost/query?query_int=abc +``` + +The request will be blocked. + +The link option is also available in the `openApiFileReference` property and synonymous with the one above in `open-api-files` + +**Note**: `openApiFileReference` is not an array. + +##### Example Configuration + +In this example, we reference the same OpenAPI Specification file as in the policy above using the `openApiFileReference` property. + +**Policy configuration:** + +```json +{ + "name": "openapifilereference-yaml", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "openApiFileReference": { + "link": "http://127.0.0.1:8088/ref.txt" + } +} +``` + +Content of the file `ref.txt`: + +```json +[ + { + "link": "http://127.0.0.1:8088/myapi.yaml" + } +] +``` + +#### File Reference + +File reference refers to accessing local resources on the same machine. See the [File References](#file-reference) under the External References section for more details. + +##### Example Configuration + +In this example, we would like to add an OpenAPI Specification file reference to the default policy. + +**Policy Configuration:** + +```json +{ + "name": "openapi-file-reference-json", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "open-api-files": [ + { + "link": "file:///myapi2.json" + } + ] +} +``` + +Content of the referenced file `myapi2.json`: + +```json +{ + "openapi": "3.0.1", + "info": { + "title": "Primitive data types2", + "description": "Primitive data types.", + "version": "2.5.1" + }, + "servers": [ + { + "url": "http://localhost" + } + ], + "paths": { + "/query": { + "get": { + "tags": [ + "query_bool" + ], + "description": "query_bool", + "operationId": "query_bool", + "parameters": [ + { + "name": "query_bool", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "NotFound" + } + } + } + } + } +} +``` + +In this case the following request will trigger an `Illegal repeated parameter name` violation, as the OpenAPI Specification doesn't allow repeated parameters. + +``` +http://localhost/query?a=true&a=false +``` + +The request will not be blocked because this violation is set to alarm in the default policy. \ No newline at end of file From 8471c7ee21778eee89a990535849abe1111af903 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:07:18 +0100 Subject: [PATCH 06/17] feat: Finish editing remaining documents --- content/waf/changelog/_index.md | 6 +- content/waf/logging/custom-dimensions.md | 53 ++++------ content/waf/policies/external-references.md | 110 ++++++++------------ 3 files changed, 70 insertions(+), 99 deletions(-) diff --git a/content/waf/changelog/_index.md b/content/waf/changelog/_index.md index 427b070f2..32d6ba815 100644 --- a/content/waf/changelog/_index.md +++ b/content/waf/changelog/_index.md @@ -3,7 +3,7 @@ title: "Changelog" url: /waf/changelog/ # Weights are assigned in increments of 100: determines sorting order -weight: 500 +weight: 600 # Creates a table of contents and sidebar, useful for large documents nd-landing-page: true # Types have a 1:1 relationship with Hugo archetypes, so you shouldn't need to change this @@ -27,9 +27,9 @@ _September 29th, 2025_ ### Important notes -- Renamed NGINX App Protect WAF to F5 for NGINX +- Renamed NGINX App Protect WAF to F5 WAF for NGINX - Aligned F5 WAF for NGINX versions - - Package and container artefacts now share the same version numbers + - Package and container artifacts now share the same version numbers - Upgrade processes remain the same as earlier releases - No breaking changes - Restructured documentation diff --git a/content/waf/logging/custom-dimensions.md b/content/waf/logging/custom-dimensions.md index 624fc8c67..75f6922cf 100644 --- a/content/waf/logging/custom-dimensions.md +++ b/content/waf/logging/custom-dimensions.md @@ -6,20 +6,25 @@ nd-content-type: reference nd-product: NAP-WAF --- -Custom dimensions log entries feature refers to the new configuration in F5 WAF for NGINX, where the new directive called `app_protect_custom_log_attribute` is assigned to a particular location or server or http level in the `nginx.conf` file. The need is to be able to add custom identifiers to the respective location and/or server and identify requests in the Security Log by those identifiers. +F5 WAF for NGINX can configure custom dimensions for log entries using the directive `app_protect_custom_log_attribute`. -The `app_protect_custom_log_attribute` directive will be used to track the assigned location/server/http dimension of each request by adding the `app_protect_custom_log_attribute` to the **Security Logs** a.k.a **Request Logs**. Since it is a custom attribute a customer can set, that custom attribute will appear for every request log entry that was handled by that location/server. +This directive can be added to the NGINX configuration file in the `http`, `server` and `location` scopes. The custom dimensions become part of every request in the [Security logs]({{< ref "/waf/logging/security-logs.md" >}}) based on the scope used. -### Configuration +The `app_protect_custom_log_attribute` directive takes a key/value pair, such as `app_protect_custom_log_attribute 'customDimension' '1'`. The directive can cascade and override entries based on scope order: _location_, _server_ then. _http_. -A new directive `app_protect_custom_log_attribute` will be added to the `nginx.conf` file. You can set this directive at all scopes: http, server and location. The setting at the location scope overrides the setting in the server and/or http scopes and the server scope overrides the http scope. The `app_protect_custom_log_attribute` directive syntax will consist of a **name/value** or **key/value** pair i.e. "app_protect_custom_log_attribute ". +For example, attributes at the _http_ level applies to all servers and locations unless a specific server or location overrides the same key with a different value. -Example Configuration: +When a custom dimension is assigned to a scope, it will appear in the `json_log` field as a new JSON property called "customLogAttributes" at the top level. This properly only appears if the `app_protect_custom_log_attribute` directive is used. -In the below example, we are configuring the `app_protect_custom_log_attribute` directive at the server and location level where we define the **key/value** pair as one string. +In the configuration example, the "environment" attribute will appear in logs of all locations under that server block. -```nginx +```json +""customLogAttribute"":[{""name"":""component"",""value"":""comp1""},{""name"":""gateway"",""value"":""gway1""}]}" +``` +The following example defines the `app_protect_custom_log_attribute` directive at the server and location level, with key/value pairs as strings. + +```nginx user nginx; load_module modules/ngx_http_app_protect_module.so; error_log /var/log/nginx/error.log debug; @@ -45,13 +50,13 @@ server { } ``` -The **key/value** pair will be 'environment env1', ‘gateway gway1’ and ‘component comp1’ in the above examples, i.e. +The key/value pairs are 'environment env1', ‘gateway gway1’ and ‘component comp1’ in the above examples: - app_protect_custom_log_attribute environment env1; - app_protect_custom_log_attribute gateway gway1; - app_protect_custom_log_attribute component comp1; -The above key/value pair will be parsed as below: +The key/value pairs are parsed as follows: ```shell "customLogAttributes": [ @@ -66,35 +71,21 @@ The above key/value pair will be parsed as below: ] ``` -### Things to Remember While Configuring the Custom Dimensions Log Entries - -The `app_protect_custom_log_attribute` directive has a few limitations which should be kept in mind while configuring this directive: +The `app_protect_custom_log_attribute` directive has constraints you should keep in mind: - Key and value strings are limited to 64 chars -- Maximum possible directive numbers are limited to 10 (in total) in each context i.e. Limit of 10 keys and values +- There are a maximum of 10 key/value pairs in each scope -### Errors and Warnings +An error message beginning with "_'app_protect_custom_log_attribute' directive is invalid_" will be displayed in the security log if: -An error message "`app_protect_custom_log_attribute` directive is invalid" will be displayed in the Security Log if the below conditions are met: +1. The `app_protect_custom_log_attribute` exceeds the maximum number of 10 directives +1. The `app_protect_custom_log_attribute` exceeds the maximum name length of 64 chars +1. The `app_protect_custom_log_attribute` exceeds the maximum value of 64 chars -1. If the `app_protect_custom_log_attribute` exceeds the maximum number of 10 directives -2. If the `app_protect_custom_log_attribute` exceeds the maximum name length of 64 chars -3. If the `app_protect_custom_log_attribute` exceeds the maximum value of 64 chars +The log will specify the precise issue: -Error message example: - -```shell +```text app_protect_custom_log_attribute directive is invalid. Number of app_protect_custom_log_attribute directives exceeds maximum ``` -### Logging and Reporting - -When `app_protect_custom_log_attribute` is assigned to a particular location/server/http context, it will appear in the `json_log` field as a new JSON property called "customLogAttributes" at the top level. The property will not appear if no `app_protect_custom_log_attribute` directive was assigned. - -Attributes at the http level applies to all servers and locations unless a specific server or location overrides the same key with a different value. Same goes for the server level and all locations under it. In the below example, the "environment" attribute will appear in logs of all locations under that server. -Security logging example in json_log: - -```json -""customLogAttribute"":[{""name"":""component"",""value"":""comp1""},{""name"":""gateway"",""value"":""gway1""}]}" -``` \ No newline at end of file diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index 07fb0a6bb..0e0c767ee 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -34,7 +34,7 @@ For example, a `modifications` section could be replaced by `modificationsRefere There are different implementations based on the type of references that are being made. -### External URL reference +### URL references URL reference is the method of referencing an external source by providing its full URL. @@ -43,6 +43,7 @@ This is a useful method when trying to combine or consolidate parts of the polic {{< call-out "note" >}} You need to make sure that the server where the resource files are located is always available when you are compiling your policy. + {{< /call-out >}} This example creates a skeleton policy, enabling the file type violation. @@ -105,18 +106,18 @@ The referenced `file-types.txt` file contains the following code: ] ``` -### HTTPS references +#### HTTPS references HTTPS references are a special case of URL references. It uses the HTTPS protocol instead of the HTTP protocol. Make sure that the webserver you are downloading the resources from does also support HTTPS protocol and has certificates setup properly. - Certificates must be valid in date (not expired) during the policy compilation. - Certificates must be signed by a trusted CA. -- For Self-signed certificates, you need to make sure to add your certificates to the trusted CA of the machine where App Protect is installed. +- For Self-signed certificates, you need to make sure to add your certificates to the trusted CA of the machine where F5 WAF for NGINX is installed. - Certificates must use the exact domain name that the certificate was issued for. For example, SSL will differentiate between domain.com and www.domain.com, considering each a different domain name. -In this configuration, we are completely satisfied with the basic default policy, and we wish to use it as is. However, we wish to define a custom response page using an external file located on an HTTPS web server. The external reference file contains our custom response page configuration. +The following configuration builds on the default policy by defining a custom response page using an external file located on an HTTPS web server. -**Policy configuration:** +The external reference file contains the custom response page configuration. ```json { @@ -145,9 +146,7 @@ Content of the referenced file `response-pages.txt`: ] ``` -In this example, we would like to enable all attack signatures. Yet, we want to exclude specific signatures from being enforced. - -**Policy configuration:** +The next example enables all attack signatures while excluding specific signatures from being enforced: ```json { @@ -193,22 +192,25 @@ Content of the referenced file `modifications.txt`: ### File references -File references refers to accessing local resources on the same machine, as opposed to accessing a remote resource on another server/machine. The user can specify any location that is accessible by App Protect except for the root folder ("/"). If no full path is provided, the default path `/etc/app_protect/conf` will be assumed. Note that file references can only be on the local machine: you cannot use remote hosts! +File references access local resources on the same machine, as opposed to accessing a remote resource on another server/machine. -Here are some examples of the typical cases: +File references do not work with remote hosts. -| Examples | File path | Notes | -| -------- | --------- | ----- | -| | /etc/app_protect/conf/foo.json | Default directory assumed | -| | /etc/app_protect/conf/foo.json | Formally illegal, but tolerated as long as there is no trailing slash. | -| | /etc/app_protect/conf/foo.json | Full path, but still the default one | -| | /bar/foo.json | Non-default path | -| | **Not accepted** | "etc" is interpreted as remote host name | +You can specify any location that is accessible by F5 WAF for NGINX except for the root folder ("/"). +If a full path is not provided, the default path (_/etc/app_protect/conf_) will be used for resolution. -In this example, we would like to enable all attack signatures. Yet, we want to exclude specific signatures from being enforced. To do this, we reference a local file on the machine. +Here are some examples of typical cases: + +| Examples | File path | Notes | +| -------- | --------- | ----- | +| _file:///foo.json_ | /etc/app_protect/conf/foo.json | Default directory assumed | +| _file://foo.json_ | /etc/app_protect/conf/foo.json | Formally illegal, but tolerated as long as there is no trailing slash. | +| _file:///etc/app_protect/conf/foo.json_ | /etc/app_protect/conf/foo.json | Full path, using default directory | +| _file:///bar/foo.json_ | /bar/foo.json | Non-default path | +| _file://etc/app_protect/conf/foo.json_ | **Not accepted** | "etc" is interpreted as remote host name | -**Policy Configuration:** +The next example enable all attack signatures, excluding specific signatures from enforcement using a reference to a local file on the machine. ```json { @@ -233,7 +235,7 @@ In this example, we would like to enable all attack signatures. Yet, we want to } ``` -Content of the referenced file `modifications.txt`: +Content of the referenced `modifications.txt` file: ```json { @@ -252,48 +254,40 @@ Content of the referenced file `modifications.txt`: } ``` -If, for any reason, the configuration was done incorrectly, the policy compilation process will fail with the following error: +If the configuration is incorrect the policy compilation process will fail with the following error: + ```shell APP_PROTECT { "event": "configuration_load_failure" ... ``` -The error details that follow will depend on the exact situation causing the policy compilation to fail. If the policy compilation process fails, the compiler will revert to the last working policy and all the changes for the last policy compilation attempt will be lost. +The error will vary based on the conditions of the failure. If the policy compilation process fails, the compiler will revert to the last working policy and the changes for the last policy compilation attempt will be discarded. + +## OpenAPI references -## OpenAPI specification file reference +F5 WAF for NGINX can reference an OpenAPI specification file used to describe restful APIs, using it for API protection. -The OpenAPI Specification defines the spec file format needed to describe RESTful APIs. The spec file can be written either in JSON or YAML. Using a spec file simplifies the work of implementing API protection. Refer to the OpenAPI Specification (formerly called Swagger) for details. +The specification file can be written in JSON or YAML format: the details can be used to create a protection policy. + +Based on the content of the OpenAPI specification file, F5 WAF for NGINX will automatically create a policy for the following properties: -The simplest way to create an API protection policy is using an OpenAPI Specification file to import the details of the APIs. If you use an OpenAPI Specification file, F5 WAF for NGINX will automatically create a policy for the following properties (depending on what's included in the spec file): * Methods * URLs * Parameters * JSON profiles -An OpenAPI-ready policy template is provided with the F5 WAF for NGINX packages and is located in: `/etc/app_protect/conf/NginxApiSecurityPolicy.json` - -It contains violations related to OpenAPI set to blocking (enforced). - -{{< call-out "note" >}} F5 WAF for NGINX supports only one OpenAPI Specification file reference per policy.{{< /call-out >}} +A policy template is provided with F5 WAF for NGINX packages containing violations related to OpenAPI configured to block (enforced) and is located in on the path _/etc/app_protect/conf/NginxApiSecurityPolicy.json_. -### Types of OpenAPI References - -There are different ways of referencing OpenAPI Specification files. The configuration is similar to [External References](#external-references). +F5 WAF for NGINX supports only one OpenAPI specification file reference per policy. {{< call-out "note" >}} Any update of an OpenAPI Specification file referenced in the policy will not trigger a policy compilation. This action needs to be done actively by reloading the NGINX configuration. {{< /call-out >}} -#### URL Reference - -URL reference is the method of referencing an external source by providing its full URL. - -Make sure to configure certificates prior to using the HTTPS protocol - see the [HTTPS References](#https-reference) under the External References section for more details. - -{{< call-out "note" >}} You need to make sure that the server where the resource files are located is always available when you are compiling your policy. {{< /call-out >}} +Configuring and referencing OpenAPI specification files are similar to other external references. -##### Example Configuration +### URL references -In this example, we are adding an OpenAPI Specification file reference using the link `http://127.0.0.1:8088/myapi.yaml`. This will configure allowed data types for `query_int` and `query_str` parameters values. +This example adds an OpenAPI specification file reference using the link `http://127.0.0.1:8088/myapi.yaml`. -**Policy configuration:** +The referenced file configures the allowed data types for `query_int` and `query_str` parameters values. ```json { @@ -443,23 +437,15 @@ paths: description: NotFound ``` -In this case the following request will trigger an `Illegal parameter data type` violation, as we expect to have an integer value in the `query_int` parameter: +The following request will trigger an `Illegal parameter data type` violation, F5 WAF for NGINX expect to have an integer value in the `query_int` parameter: -``` +```text http://localhost/query?query_int=abc ``` -The request will be blocked. - -The link option is also available in the `openApiFileReference` property and synonymous with the one above in `open-api-files` - -**Note**: `openApiFileReference` is not an array. +The link option is also available with the `openApiFileReference` property and synonymous with the one in `open-api-files`. `openApiFileReference` is not an array. -##### Example Configuration - -In this example, we reference the same OpenAPI Specification file as in the policy above using the `openApiFileReference` property. - -**Policy configuration:** +This next example references the same OpenAPI specification file as the previous one, using the `openApiFileReference` property. ```json { @@ -483,15 +469,9 @@ Content of the file `ref.txt`: ] ``` -#### File Reference - -File reference refers to accessing local resources on the same machine. See the [File References](#file-reference) under the External References section for more details. - -##### Example Configuration - -In this example, we would like to add an OpenAPI Specification file reference to the default policy. +### File references -**Policy Configuration:** +This example adds an OpenAPI specification file reference to the default policy. ```json { @@ -554,10 +534,10 @@ Content of the referenced file `myapi2.json`: } ``` -In this case the following request will trigger an `Illegal repeated parameter name` violation, as the OpenAPI Specification doesn't allow repeated parameters. +The following request will trigger an `Illegal repeated parameter name` violation, as the OpenAPI specification doesn't allow repeated parameters. ``` http://localhost/query?a=true&a=false ``` -The request will not be blocked because this violation is set to alarm in the default policy. \ No newline at end of file +The request will _not be blocked_ because this violation is set to alarm in the default policy. \ No newline at end of file From d542de7a70cb31f21ffcd4b5daa8fae25efbc323 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:40:31 +0100 Subject: [PATCH 07/17] Apply suggestions from code review Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> Co-authored-by: Mike Jang <3287976+mjang@users.noreply.github.com> --- content/waf/logging/custom-dimensions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/waf/logging/custom-dimensions.md b/content/waf/logging/custom-dimensions.md index 75f6922cf..88a14f6c2 100644 --- a/content/waf/logging/custom-dimensions.md +++ b/content/waf/logging/custom-dimensions.md @@ -10,11 +10,11 @@ F5 WAF for NGINX can configure custom dimensions for log entries using the direc This directive can be added to the NGINX configuration file in the `http`, `server` and `location` scopes. The custom dimensions become part of every request in the [Security logs]({{< ref "/waf/logging/security-logs.md" >}}) based on the scope used. -The `app_protect_custom_log_attribute` directive takes a key/value pair, such as `app_protect_custom_log_attribute 'customDimension' '1'`. The directive can cascade and override entries based on scope order: _location_, _server_ then. _http_. +The `app_protect_custom_log_attribute` directive takes a key/value pair, such as `app_protect_custom_log_attribute 'customDimension' '1'`. The directive can cascade and override entries based on scope order: _location_, _server_ then _http_. -For example, attributes at the _http_ level applies to all servers and locations unless a specific server or location overrides the same key with a different value. +For example, attributes at the _http_ level apply to all servers and locations unless a specific server or location overrides the same key with a different value. -When a custom dimension is assigned to a scope, it will appear in the `json_log` field as a new JSON property called "customLogAttributes" at the top level. This properly only appears if the `app_protect_custom_log_attribute` directive is used. +When a custom dimension is assigned to a scope, it appears in the `json_log` field as a new JSON property called "customLogAttributes" at the top level. This properly appears if the `app_protect_custom_log_attribute` directive is used. In the configuration example, the "environment" attribute will appear in logs of all locations under that server block. From f3fa51f8367a0705dc8f9a02ade1e4c41b0dbe22 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:42:21 +0100 Subject: [PATCH 08/17] Apply suggestions from code review Co-authored-by: Mike Jang <3287976+mjang@users.noreply.github.com> Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/includes/waf/table-policy-features.md | 2 +- content/waf/logging/custom-dimensions.md | 8 ++++---- content/waf/policies/directives.md | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/content/includes/waf/table-policy-features.md b/content/includes/waf/table-policy-features.md index 80cc1849e..6a0e821ee 100644 --- a/content/includes/waf/table-policy-features.md +++ b/content/includes/waf/table-policy-features.md @@ -11,7 +11,7 @@ | [Cookie enforcement]({{< ref "/waf/policies/cookie-enforcement.md" >}}) | By default all cookies are allowed and not enforced for integrity. The user can add specific cookies, wildcards or explicit, that will be enforced for integrity. It is also possible to set the cookie attributes: HttpOnly, Secure and SameSite for cookies found in the response. | | [Data guard]({{< ref "/waf/policies/data-guard.md" >}}) | Detects and masks Credit Card Number (CCN) and/or U.S. Social Security Number (SSN) and/or custom patterns in HTTP responses. Disabled by default. | | [Deny and Allow IP lists]({{< ref "/waf/policies/deny-allow-ip.md" >}}) | Manually define denied & allowed IP addresses as well as IP addresses to never log. | -| [Do-nothing]({{< ref "/waf/policies/do-nothing.md" >}}) | Do-nothing allows you to configure the avoidance of inspecting or parsing content of a URL. | +| [Do-nothing]({{< ref "/waf/policies/do-nothing.md" >}}) | Do-nothing allows you to avoid inspecting or parsing a URL. | | [Disallowed file type extensions]({{< ref "/waf/policies/disallowed-extensions.md" >}}) | Support any file type, and includes a predefined list of file types by default | | [Evasion techniques]({{< ref "/waf/policies/evasion-techniques.md" >}}) | All evasion techniques are enabled by default, and can be disabled individually. These include directory traversal, bad escaped characters and more. | | [Geolocation]({{< ref "/waf/policies/geolocation.md" >}}) | The geolocation feature allows you to configure enforcement based on the location of an object using the two-letter ISO code representing a country. | diff --git a/content/waf/logging/custom-dimensions.md b/content/waf/logging/custom-dimensions.md index 88a14f6c2..ac84c3cbb 100644 --- a/content/waf/logging/custom-dimensions.md +++ b/content/waf/logging/custom-dimensions.md @@ -16,10 +16,10 @@ For example, attributes at the _http_ level apply to all servers and locations u When a custom dimension is assigned to a scope, it appears in the `json_log` field as a new JSON property called "customLogAttributes" at the top level. This properly appears if the `app_protect_custom_log_attribute` directive is used. -In the configuration example, the "environment" attribute will appear in logs of all locations under that server block. +In the configuration example, the "environment" attribute appears in logs of all locations under that server block. ```json -""customLogAttribute"":[{""name"":""component"",""value"":""comp1""},{""name"":""gateway"",""value"":""gway1""}]}" +""customLogAttributes"":[{""name"":""component"",""value"":""comp1""},{""name"":""gateway"",""value"":""gway1""}]}" ``` The following example defines the `app_protect_custom_log_attribute` directive at the server and location level, with key/value pairs as strings. @@ -38,7 +38,7 @@ server { server_name localhost; proxy_http_version 1.1; - app_protect_custom_log_attribute ‘environment' 'env1'; + app_protect_custom_log_attribute 'environment' 'env1'; location / { @@ -50,7 +50,7 @@ server { } ``` -The key/value pairs are 'environment env1', ‘gateway gway1’ and ‘component comp1’ in the above examples: +The key/value pairs are 'environment env1', 'gateway gway1' and 'component comp1' in the above examples: - app_protect_custom_log_attribute environment env1; - app_protect_custom_log_attribute gateway gway1; diff --git a/content/waf/policies/directives.md b/content/waf/policies/directives.md index bb071a5f4..8b8602006 100644 --- a/content/waf/policies/directives.md +++ b/content/waf/policies/directives.md @@ -80,7 +80,7 @@ http { ... ``` -The argument for the directive should be a random alphanumeric string of at least 20 characters length (Maximum 1000 characters). +The argument for the directive should be a random alphanumeric string of at least 20 characters (Maximum 1000 characters). This is a seed used by F5 WAF for NGINX to generate the encryption key for the cookies it creates. These cookies are used for various purposes such as validating the integrity of the cookies generated by the application. @@ -116,13 +116,13 @@ http { {{< call-out "warning" >}} -From F5 WAF for NGINX release version 4.6, the `app_protect_compressed_requests_action` directive was removed deprecated from the nginx configuration. +From F5 WAF for NGINX release version 4.6, the `app_protect_compressed_requests_action` directive was removed deprecated from the NGINX configuration. -When configuring this directive in the `nginx.conf` file, F5 WAF for NGINX will disregard any previously used values ("pass" or "drop") and issue a warning. +When configuring this directive in the `nginx.conf` file, F5 WAF for NGINX disregards any previously used values ("pass" or "drop") and issues a warning. {{< /call-out >}} -By default, the enforcer will now decompress all the HTTP compressed payload request and will apply the enforcment. +By default, the enforcer will now decompress the whole HTTP compressed payload request and will apply the enforcement. The supported compression algorithms for this feature are "**gzip**" and "**deflate**". @@ -130,7 +130,7 @@ Decompression may fail under certain conditions: - If the compression method is not supported - If the 'Content-Encoding' header does not match the compression algorithm used while sending a compressed payload in a HTTP request -- If thehe decompressed request is larger than 10 MB +- If the decompressed request is larger than 10 MB If it does exceed this limit, F5 WAF for NGINX will only decompress the first 10 KB, ignoring the remainder, and trigger the `VIOL_REQUEST_MAX_LENGTH` violation, just as it would for an uncompressed request that exceeds 10 MB. From 69fa6d9523fb296e10f307d60da7bcb75d11e5c9 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:50:34 +0100 Subject: [PATCH 09/17] Update content/waf/policies/external-references.md Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/waf/policies/external-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index 0e0c767ee..bd1592226 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -26,7 +26,7 @@ Updating a single file referenced in the policy will not trigger a policy compil {{< /call-out >}} -To use external references, replaced the direct property in the policy file with the _\Reference_ property, where _\_ defines the replacement text for the property changed to singular (if originally plural) and notation converted from snake case to camelCase. +To use external references, replace the direct property in the policy file with the _\Reference_ property, where _\_ defines the replacement text for the property changed to singular (if originally plural) and notation converted from snake case to camelCase. For example, a `modifications` section could be replaced by `modificationsReference` and `data-guard` would could be replaced by `dataGuardReference`. From 64e281256b17aee6824fb54a6d51785093f98883 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:50:43 +0100 Subject: [PATCH 10/17] Update content/waf/policies/external-references.md Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/waf/policies/external-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index bd1592226..60b5b8dbf 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -28,7 +28,7 @@ Updating a single file referenced in the policy will not trigger a policy compil To use external references, replace the direct property in the policy file with the _\Reference_ property, where _\_ defines the replacement text for the property changed to singular (if originally plural) and notation converted from snake case to camelCase. -For example, a `modifications` section could be replaced by `modificationsReference` and `data-guard` would could be replaced by `dataGuardReference`. +For example, a `modifications` section could be replaced by `modificationsReference` and `data-guard` could be replaced by `dataGuardReference`. ## External reference types From acd4650d63edb1446b9346e72b03d5e5c15929ae Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:51:23 +0100 Subject: [PATCH 11/17] Update content/waf/policies/external-references.md Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/waf/policies/external-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index 60b5b8dbf..41f0788c1 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -121,7 +121,7 @@ The external reference file contains the custom response page configuration. ```json { - "name": "external_references_custom_respsonse", + "name": "external_references_custom_response", "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, From 31cdd1bcf6e77479f57f9ce3681e39d01ce9f8a2 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:53:59 +0100 Subject: [PATCH 12/17] Update content/waf/policies/external-references.md Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/waf/policies/external-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index 41f0788c1..0ac55c4ba 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -210,7 +210,7 @@ Here are some examples of typical cases: | _file:///bar/foo.json_ | /bar/foo.json | Non-default path | | _file://etc/app_protect/conf/foo.json_ | **Not accepted** | "etc" is interpreted as remote host name | -The next example enable all attack signatures, excluding specific signatures from enforcement using a reference to a local file on the machine. +The next example enables all attack signatures, excluding specific signatures from enforcement using a reference to a local file on the machine. ```json { From ec5225ed05f27dae6f4216e6504d5120b07033e5 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:55:48 +0100 Subject: [PATCH 13/17] Update content/waf/policies/external-references.md Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/waf/policies/external-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index 0ac55c4ba..cb136c1ac 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -275,7 +275,7 @@ Based on the content of the OpenAPI specification file, F5 WAF for NGINX will au * Parameters * JSON profiles -A policy template is provided with F5 WAF for NGINX packages containing violations related to OpenAPI configured to block (enforced) and is located in on the path _/etc/app_protect/conf/NginxApiSecurityPolicy.json_. +A policy template is provided with F5 WAF for NGINX packages containing violations related to OpenAPI configured to block (enforced) and is located on the path _/etc/app_protect/conf/NginxApiSecurityPolicy.json_. F5 WAF for NGINX supports only one OpenAPI specification file reference per policy. From 7334eb06044a2b7bf463044a469b32069a669fbf Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:58:27 +0100 Subject: [PATCH 14/17] Update content/waf/policies/external-references.md Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/waf/policies/external-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index cb136c1ac..9920642fe 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -254,7 +254,7 @@ Content of the referenced `modifications.txt` file: } ``` -If the configuration is incorrect the policy compilation process will fail with the following error: +If the configuration is incorrect, the policy compilation process will fail with the following error: ```shell APP_PROTECT { "event": "configuration_load_failure" ... From 0ec236faed72f0873691608373a3e2cfd6a52467 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:58:36 +0100 Subject: [PATCH 15/17] Update content/waf/policies/external-references.md Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/waf/policies/external-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index 9920642fe..53cb7d184 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -264,7 +264,7 @@ The error will vary based on the conditions of the failure. If the policy compil ## OpenAPI references -F5 WAF for NGINX can reference an OpenAPI specification file used to describe restful APIs, using it for API protection. +F5 WAF for NGINX can reference an OpenAPI specification file used to describe RESTful APIs, using it for API protection. The specification file can be written in JSON or YAML format: the details can be used to create a protection policy. From e03fbe60b99dd38e97c68c6c9f619cb996b31bb3 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:58:59 +0100 Subject: [PATCH 16/17] Update content/waf/policies/external-references.md Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/waf/policies/external-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index 53cb7d184..de3a0cb83 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -287,7 +287,7 @@ Configuring and referencing OpenAPI specification files are similar to other ext This example adds an OpenAPI specification file reference using the link `http://127.0.0.1:8088/myapi.yaml`. -The referenced file configures the allowed data types for `query_int` and `query_str` parameters values. +The referenced file configures the allowed data types for `query_int` and `query_str` parameter values. ```json { From 5fecf21547b571b646e6f935f0c40e14bfdc65e3 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 7 Oct 2025 15:59:25 +0100 Subject: [PATCH 17/17] Update content/waf/policies/external-references.md Co-authored-by: Jon Torre <78599298+JTorreG@users.noreply.github.com> --- content/waf/policies/external-references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/waf/policies/external-references.md b/content/waf/policies/external-references.md index de3a0cb83..0cdace680 100644 --- a/content/waf/policies/external-references.md +++ b/content/waf/policies/external-references.md @@ -437,7 +437,7 @@ paths: description: NotFound ``` -The following request will trigger an `Illegal parameter data type` violation, F5 WAF for NGINX expect to have an integer value in the `query_int` parameter: +The following request will trigger an `Illegal parameter data type` violation, F5 WAF for NGINX expects to have an integer value in the `query_int` parameter: ```text http://localhost/query?query_int=abc