Configure policies
Information architecture noteThe design intention for this page is to as a single source of truth to replace the two Configuration Guides (two separate links).
Outside of the overlapping information for Policy configuration, the existing pages also include general configuration information, such as for F5 WAF for NGINX itself. This detail can be added to a separate page, ensuring that each document acts as a solution for exactly one problem at a time.
This page describes the security features available with F5 WAF for NGINX and how to configure policies.
For better understanding of some contextual nouns, read the Terminology topic.
Feature | Description |
---|---|
Attack signatures | The default policy covers the OWASP top 10 attack patterns. Specific signature sets can be added or disabled. |
IP intelligence | Configure the IP Intelligence feature to customize enforcement based on the source IP of the request, limiting access from IP addresses with questionable reputation. |
User-defined HTTP headers | Handling headers as a special part of requests |
XFF headers and trust | Disabled by default, and can accept an optional list of custom XFF headers. |
F5 WAF for NGINX ships with two reference policies, both with a default enforcement mode set to Blocking:
- The default policy which is identical to the base template and provides OWASP Top 10 and Bot security protection out of the box.
- The strict policy contains more restrictive criteria for blocking traffic than the default policy. It is meant to be used for protecting sensitive applications that require more security but with higher risk of false positives.
You can use these policies as-is, but they are often the starting points for customizations according to the needs of the applications F5 WAF NGINX protects.
The F5 WAF for NGINX security policy configuration uses a declarative format based on a pre-defined base template.
The policy is represented in a JSON file which you can edit to add, modify and remove security capabilities in reference to the base template.
The way the policy is integrated into the NGINX configuration is through referencing the JSON file (Using the full path) in the nginx.conf
file.
F5 WAF for NGINX provides a JSON Schema which can be used to validate a JSON policy file for format compliance.
The schema file can be generated using a script once F5 WAF for NGINX is installed:
sudo /opt/app_protect/bin/generate_json_schema.pl
.This script will output the schema to a file named
policy.json
into the current working directory. Once the schema file is generated, you can use validation tools such as AJV to validate a JSON policy file.This schema is used for the Policy parameter reference.
In the following example, the NGINX configuration file with F5 WAF for NGINX is enabled in the HTTP context and the policy /etc/app_protect/conf/NginxDefaultPolicy.json is used:
user nginx;
worker_processes 4;
load_module modules/ngx_http_app_protect_module.so;
error_log /var/log/nginx/error.log debug;
events {
worker_connections 65536;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
app_protect_enable on; # This is how you enable F5 WAF for NGINX in the relevant context/block
app_protect_policy_file "/etc/app_protect/conf/NginxDefaultPolicy.json"; # This refers to which policy file to use, which falls back to the default policy
app_protect_security_log_enable on; # This section enables logging
app_protect_security_log "/etc/app_protect/conf/log_default.json" syslog:server=127.0.0.1:514; # This is where the remote logger is defined in terms of: logging options (defined in the referenced file), log server IP, log server port
server {
listen 80;
server_name localhost;
proxy_http_version 1.1;
location / {
client_max_body_size 0;
default_type text/html;
proxy_pass http://172.29.38.211:80$request_uri;
}
}
}
The base template is the common starting point for any policy you write.
The default policy reflects the base template without any further modifications, so the terms base template and default policy are used interchangeably.
The default policy appears as follows:
{
"policy" : {
"name": "app_protect_default_policy",
"template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }
}
}
The default policy enforces violations by Violation Rating, the F5 WAF for NGINX computed assessment of the risk of the request based on the triggered violations.
- 0: No violation
- 1-2: False positive
- 3: Needs examination
- 4-5: Threat
The default policy enables most of the violations and signature sets with Alarm turned ON, but not Block.
These violations and signatures, when detected in a request, affect the violation rating. By default, if the violation rating is calculated to be malicious (4-5) the request will be blocked by the VIOL_RATING_THREAT
violation.
This is true even if the other violations and signatures detected in that request have the Block flag turned OFF. It is the VIOL_RATING_THREAT
violation having the Block flag turned ON that caused the blocking, but indirectly the combination of all the other violations and signatures in Alarm caused the request to be blocked.
By default, other requests which have a lower violation rating are not blocked, except for some specific violations described below. This is to minimize false positives. However, you can change the default behavior.
For example, if you want to add blocking on a violation rating of 3 as well, enable blocking for the VIOL_RATING_NEED_EXAMINATION
violation.
The following violations and signature sets have a low chance of being false positives and are, therefore, configured by default to block the request regardless of its Violation Rating:
- High accuracy attack signatures
- Threat campaigns
- Malformed request: unparsable header, malformed cookie and malformed body (JSON or XML).
F5 WAF for NGINX offers prebuilt bundles for security policies:
- app_protect_default_policy
- app_protect_strict_policy
You cannot mix these prebuilt bundles with custom policy bundles within the samenginx.conf
file.
Example:
...
location / {
# F5 WAF for NGINX
app_protect_enable on;
app_protect_policy_file app_protect_strict_policy;
app_protect_security_log_enable on;
app_protect_security_log log_all stderr;
proxy_pass http://127.0.0.1:8080/;
}
This section assumes that you have built a compiler image namedwaf-compiler-1.0.0:custom
.
To generate versions of the default policies that include the latest security updates, use the -factory-policy
option instead of a source policy file.
For instance, to create an updated version of the app_protect_default_policy
, use the following command:
docker run \
-v $(pwd):$(pwd) \
waf-compiler-1.0.0:custom \
-factory-policy default -o $(pwd)/new_default_policy.tgz
To create an updated version of the app_protect_strict_policy
, use:
docker run \
-v $(pwd):$(pwd) \
waf-compiler-1.0.0:custom \
-factory-policy strict -o $(pwd)/new_strict_policy.tgz
After creating the updated version of a policy, reference it in the nginx.conf
file:
app_protect_policy_file /policies_mount/new_default_policy.tgz;
The strict policy is recommended as a starting point for applications requiring a higher level of security.
Similar to policies, it is customized from the base template, so it detects and blocks everything the default policy does.
To obtain the strict policy, execute the following command:
sudo docker run --rm -v $(pwd):$(pwd) --entrypoint='' private-registry.nginx.com/nap/waf-compiler:1.0.0 cat /etc/app_protect/conf/NginxStrictPolicy.json
Replace the 1.0.0
with the actual release version.
In addition the strict policy also blocks the following:
- Requests that have a Violation Rating of 3, “Needs examination”. This occurs because the
VIOL_RATING_NEED_EXAMINATION
violation’s block flag is enabled in the strict policy. - Requests with the
VIOL_EVASION
violation (evasion techniques). - Requests with violations that restrict options in the request and response: HTTP method, response status code and disallowed file types.
Other violations, specifically attack signatures and metacharacters, which are more prone to false positives, still have only Alarm turned on, without blocking, contributing to the Violation Rating as in the Default policy.
In addition, the Strict policy also enables the following features in alarm only mode:
- Data Guard: masking Credit Card Number (CCN), US Social Security Number (SSN) and custom patterns found in HTTP responses.
- HTTP response data leakage signatures: preventing exfiltration of sensitive information from the servers.
- More restrictive limitations: mainly sizing and parsing of JSON and XML payloads.
- Cookie attribute insertion: the Strict policy adds the Secure and SameSite=lax attributes to every cookie set by the application server. These attributes are enforced by the browsers and protect against session hijacking and CSRF attacks respectively.
The policy JSON file specifies the settings that are different from the base template, such as enabling more signatures, disabling some violations, adding server technologies, etc. These will be shown in the next sections.
There are two ways to tune those settings:
- Within the
policy
structure property, the organic structure of the policy. - Within the
modifications
structure property that contains a list of changes expressed in a generic manner.
Both options are equivalent in their semantic expression power, but different syntactically and are designated for different use cases. But before that, let’s look at an example - disabling a specific attack signature.
Signature 200001834 disabled in the policy
property:
{
"policy": {
"name": "signature_exclude_1",
"signatures": [
{
"signatureId": 200001834,
"enabled": false
}
]
}
}
As you can see, this is expressed using the signatures
property that contains configuration of individual signatures in a policy. If you want to modify other parts of the policy, you would use different JSON properties.
The same configuration in the modifications
array looks like this:
{
"policy": {
"name": "signature_exclude_2"
},
"modifications": [
{
"entityChanges": {
"enabled": false
},
"entity": {
"signatureId": 200001834
},
"entityType": "signature",
"action": "add-or-update"
}
]
}
Note the generic schema that can express manipulation in any policy element: entity
, entityType
, action
etc. The modifications
array is a flat list of individual changes applied to the policy after evaluating the policy
block.
So when to use policy
and when to use modifications
? There are some recommended practice guidelines for that:
- Use
policy
to express the security policy as you intended it to be: the features you want to enable, disable, the signature sets, server technologies and other related configuration attributes. This part of the policy is usually determined when the application is deployed and changes at a relatively slow pace. - Use
modifications
to express exceptions to the intended policy. These exceptions are usually the result of fixing false positive incidents and failures in tests applied to those policies. Usually these are granular modifications, typically disabling checks of individual signatures, metacharacters and sub-violations. These changes are more frequent. - Use
modifications
also for removing individual collection elements from the base template, for example disallowed file types.
It is a good practice to separate the modifications
to a different file and have the main policy file reference the former, as the two parts have different lifecycles.
The sections just below review the common policy feature configurations using examples. For the full reference of the policy
JSON properties see the Declarative Policy guide.
A policy’s enforcement mode can be:
- Blocking: Any illegal or suspicious requests are logged and blocked. This is the default enforcement mode for the default policy and any added policy unless changed to Transparent.
- Transparent: Any illegal or suspicious requests are logged but not blocked.
Individual security features can be defined as blocked or transparent in the policy. Here are examples of both:
{
"policy": {
"name": "policy_name",
"template": { "name": "POLICY_TEMPLATE_NGINX_BASE" },
"applicationLanguage": "utf-8",
"enforcementMode": "blocking"
}
}
{
"policy": {
"name": "policy_name",
"template": { "name": "POLICY_TEMPLATE_NGINX_BASE" },
"applicationLanguage": "utf-8",
"enforcementMode": "transparent"
}
}
Adding and enabling additional security features to the policy can be done by specifying the violation name and the alarm
block state to true
.
To set different states for sub-violations within the violation, enable the violation first, then specifying and configure the sub-violations.
A violation may have its own section that provides additional configuration granularity for a specific violation/sub-violation.
The attack signature violation
VIOL_ATTACK_SIGNATURE
cannot be configured.It is determined by the combination of the signature sets on the policy.
In this example, we enable a violation and a sub-violation: VIOL_JSON_FORMAT
and VIOL_PARAMETER_VALUE_METACHAR
.
The example defines the blocking and alarm setting for each violation. These settings override the default configuration set above in the enforcementMode
directive.
Be aware, however, that in a transparent policy no violations are blocked, even if specific violations are set to block: true
in the configuration.
{
"policy": {
"name": "policy_name",
"template": { "name": "POLICY_TEMPLATE_NGINX_BASE" },
"applicationLanguage": "utf-8",
"enforcementMode": "blocking",
"blocking-settings": {
"violations": [
{
"name": "VIOL_JSON_FORMAT",
"alarm": true,
"block": true
},
{
"name": "VIOL_PARAMETER_VALUE_METACHAR",
"alarm": false,
"block": false
}
]
}
}
}