Configuration
This document explains how to use F5 NGINX Ingress Controller to configure NGINX App Protect WAF.
Note:
Check out the complete NGINX Ingress Controller with NGINX App Protect WAF example resources on GitHub for VirtualServer resources and for Ingress resources.
Global Configuration
NGINX Ingress Controller has global configuration parameters that match those in NGINX App Protect WAF. They are found in the ConfigMap resource: the NGINX App Protect WAF parameters are prefixed with app-protect*
.
Enable NGINX App Protect WAF
NGINX App Protect WAF can be enabled and configured for custom resources (VirtualServer, VirtualServerRoute) or Ingress resources.
- For custom resources, you need to create a Policy Custom Resource referencing the
APPolicy
custom resource or bundle, then add it to the VirtualServer definition. Additional detail can be found in the Policy Resource documentation. - For Ingress resources, apply the
app-protect
annotations to each desired resource.
NGINX App Protect WAF Policies
NGINX App Protect WAF Policies can be created for VirtualServer, VirtualServerRoute, or Ingress resources by creating an APPolicy
custom resource. There are some caveats:
- The fields
policy.signature-requirements[].minRevisionDatetime
andpolicy.signature-requirements[].maxRevisionDatetime
are not supported. - The Advanced gRPC Protection for Unary Traffic only supports providing an
idl-file
inline. The fieldspolicy.idl-files[].link
,policy.idl-files[].$ref
, andpolicy.idl-files[].file
are not supported. The IDL file should be provided in fieldpolicy.idl-files[].contents
. The value of this field can be base64 encoded. In this case the fieldpolicy.idl-files[].isBase64
should be set totrue
.
Warning:
External references are deprecated in NGINX Ingress Controller and will not be supported in future releases.
To add an NGINX App Protect WAF policy to an Ingress resource:
- Create an
APPolicy
custom resource manifest. - Add the policy to the
spec
field in theAPPolicy
resource.
A resource specification and its Policy JSON must match. The fields must be identical in name and nesting level. If the resources are defined with YAML, the policy must also be represented in YAML.
As an example, this is a DataGuard policy:
{
"policy": {
"name": "dataguard_blocking",
"template": { "name": "POLICY_TEMPLATE_NGINX_BASE" },
"applicationLanguage": "utf-8",
"enforcementMode": "blocking",
"blocking-settings": {
"violations": [
{
"name": "VIOL_DATA_GUARD",
"alarm": true,
"block": true
}
]
},
"data-guard": {
"enabled": true,
"maskData": true,
"creditCardNumbers": true,
"usSocialSecurityNumbers": true,
"enforcementMode": "ignore-urls-in-list",
"enforcementUrls": []
}
}
}
This is what its corresponding APPolicy
resource defined in the spec
would look like:
apiVersion: appprotect.f5.com/v1beta1
kind: APPolicy
metadata:
name: dataguard-blocking
spec:
policy:
name: dataguard_blocking
template:
name: POLICY_TEMPLATE_NGINX_BASE
applicationLanguage: utf-8
enforcementMode: blocking
blocking-settings:
violations:
- name: VIOL_DATA_GUARD
alarm: true
block: true
data-guard:
enabled: true
maskData: true
creditCardNumbers: true
usSocialSecurityNumbers: true
enforcementMode: ignore-urls-in-list
enforcementUrls: []
Notice that the fields match in name and nesting: NGINX Ingress Controller will transform the YAML into a valid JSON WAF policy config.
NGINX App Protect WAF Logs
Configuring
You can set the NGINX App Protect WAF log configurations by creating an APLogConf
Custom Resource.
To add the log configurations to a VirtualServer or an Ingress resource:
-
Create an
APLogConf
Custom Resource manifest. -
Add the log configuration to the
spec
field in theAPLogConf
resource. -
Add a reference to
APLogConf
in the VirtualServer Policy resource or the Ingress resource as per the documentation.Note: The fields from the JSON must be presented in the YAML exactly the same, in name and level. NGINX Ingress Controller will transform the YAML into a valid JSON WAF log config.
For example, say you want to log state changing requests for your VirtualServer or Ingress resources using NGINX App Protect WAF. The log configuration looks like this:
{
"filter": {
"request_type": "all"
},
"content": {
"format": "default",
"max_message_size": "5k"
}
}
You would define that config in the spec
of your APLogConf
resource as follows:
apiVersion: appprotect.f5.com/v1beta1
kind: APLogConf
metadata:
name: logconf
spec:
filter:
request_type: all
content:
format: default
max_message_size: 5k
NGINX App Protect WAF User Defined Signatures
You can define NGINX App Protect WAF User-Defined Signatures for your VirtualServer or Ingress resources by creating an APUserSig
Custom Resource.
Note: The field
revisionDatetime
is not currently supported.
Note:
APUserSig
resources increase the reload time of NGINX Plus compared withAPPolicy
andAPLogConf
resources. Refer to NGINX Fails to Start or Reload for more information.
To add the User Defined Signatures to a VirtualServer or Ingress resource:
-
Create an
APUserSig
Custom resource manifest. -
Add the desired User defined signature to the
spec
field in theAPUserSig
resource.Note: The fields from the JSON must be presented in the YAML exactly the same, in name and level. The Ingress Controller will transform the YAML into a valid JSON User-Defined signature. There is no need to reference the user defined signature resource in the Policy or Ingress resources.
For example, say you want to create the following user defined signature:
{ "softwareVersion": "15.1.0",
"tag": "Fruits",
"signatures": [
{
"name": "Apple_medium_acc",
"rule": "content:\"apple\"; nocase;",
"signatureType": "request",
"attackType": {
"name": "Brute Force Attack"
},
"systems": [
{"name": "Microsoft Windows"},
{"name": "Unix/Linux"}
],
"risk": "medium",
"accuracy": "medium",
"description": "Medium accuracy user defined signature with tag (Fruits)"
}
]
}
You would add that config in the spec
of your APUserSig
resource as follows:
apiVersion: appprotect.f5.com/v1beta1
kind: APUserSig
metadata:
name: apple
spec:
signatures:
- accuracy: medium
attackType:
name: Brute Force Attack
description: Medium accuracy user defined signature with tag (Fruits)
name: Apple_medium_acc
risk: medium
rule: content:"apple"; nocase;
signatureType: request
systems:
- name: Microsoft Windows
- name: Unix/Linux
softwareVersion: 15.1.0
tag: Fruits
NGINX App Protect WAF Bundles
You can define App Protect WAF bundles for VirtualServer custom resources by creating policy bundles and putting them on a mounted volume accessible from NGINX Ingress Controller.
Before applying a policy, a WAF policy bundle must be created, then copied to a volume mounted to /etc/nginx/waf/bundles
.
Note:
NGINX Ingress Controller supportssecurityLogs
for policy bundles when usingapLogBundle
instead ofapLogConf
. Log bundles must also be copied to a volume mounted to/etc/nginx/waf/bundles
.
This example shows how a policy is configured by referencing a generated WAF Policy Bundle:
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: <policy_name>
spec:
waf:
enable: true
apBundle: "<policy_bundle_name>.tgz"
This example shows the same policy as above but with a log bundle used for security log configuration:
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: <policy_name>
spec:
waf:
enable: true
apBundle: "<policy_bundle_name>.tgz"
securityLogs:
- enable: true
apLogBundle: "<log_bundle_name>.tgz"
logDest: "syslog:server=syslog-svc.default:514"
OpenAPI Specification in NGINX Ingress Controller
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.
NGINX Ingress Controller supports OpenAPI Specification versions 2.0 and 3.0.
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, NGINX App Protect WAF 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 NGINX App Protect WAF packages and is located in: /etc/app_protect/conf/NginxApiSecurityPolicy.json
It contains violations related to OpenAPI set to blocking (enforced).
Types of OpenAPI References
There are different ways of referencing OpenAPI Specification files. The configuration is similar to External References.
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.
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 External References for details.
Configuration in NGINX Ingress Controller
These are the typical steps to deploy an OpenAPI protection Policy in NGINX Ingress Controller:
- Copy the API security policy
/etc/app_protect/conf/NginxApiSecurityPolicy.json
to a different file so that it can be edited. - Add the reference to the desired OpenAPI file.
- Make other custom changes if needed (e.g. enable Data Guard protection).
- Use a tool to convert the result to YAML. There are many, for example:
yq
utility. - Add the YAML properties to create an
APPolicy
Custom Resource putting the policy itself (as in step 4) within thespec
property of the Custom Resource. Refer to the NGINX App Protect Policies section above. - Create a
Policy
object which references theAPPolicy
Custom Resource as in this example. - Finally, attach the
Policy
object to aVirtualServer
resource as in this example.
Note:
You need to make sure that the server where the resource files are located is available while you are compiling your policy.
Example Configuration
In this example, we are adding an OpenAPI Specification file reference to /etc/app_protect/conf/NginxApiSecurityPolicy.yaml
using the link. This will configure allowed data types for query_int
and query_str
parameters values.
Policy configuration:
---
apiVersion: appprotect.f5.com/v1beta1
kind: APPolicy
metadata:
name: petstore_api_security_policy
spec:
policy:
name: petstore_api_security_policy
description: NGINX App Protect WAF API Security Policy for the Petstore API
template:
name: POLICY_TEMPLATE_NGINX_BASE
open-api-files:
- link: https://raw.githubusercontent.com/aws-samples/api-gateway-secure-pet-store/master/src/main/resources/swagger.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
:
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 is synonymous with the open-api-files
property as seen in the policy example above.
Note: openApiFileReference
is not an array.
Configuration in NGINX Plus Ingress Controller using Virtual Server Resource
In this example we deploy NGINX Ingress Controller with NGINX Plus and NGINX App Protect WAF, deploy a simple web application, and then configure load balancing and WAF protection for that application using the VirtualServer resource.
Note:
You can find the example, and the files referenced, on GitHub.
Prerequisites
-
Follow the installation instructions to deploy NGINX Ingress Controller with NGINX Plus and NGINX App Protect WAF.
-
Save the public IP address of NGINX Ingress Controller into a shell variable:
IC_IP=XXX.YYY.ZZZ.III
-
Save the HTTP port of NGINX Ingress Controller into a shell variable:
IC_HTTP_PORT=<port number>
Step 1. Deploy a Web Application
Create the application deployment and service:
kubectl apply -f https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/v3.6.1/examples/custom-resources/app-protect-waf/webapp.yaml
Step 2. Deploy the AP Policy
-
Create the syslog service and pod for the NGINX App Protect WAF security logs:
kubectl apply -f https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/v3.6.1/examples/custom-resources/app-protect-waf/syslog.yaml
-
Create the User-Defined Signature, WAF policy, and log configuration:
kubectl apply -f https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/v3.6.1/examples/custom-resources/app-protect-waf/ap-apple-uds.yaml kubectl apply -f https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/v3.6.1/examples/custom-resources/app-protect-waf/ap-dataguard-alarm-policy.yaml kubectl apply -f https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/v3.6.1/examples/custom-resources/app-protect-waf/ap-logconf.yaml
Step 3 - Deploy the WAF Policy
Create the WAF policy
kubectl apply -f https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/v3.6.1/examples/custom-resources/app-protect-waf/waf.yaml
Note the NGINX App Protect WAF configuration settings in the Policy resource. They enable WAF protection by configuring NGINX App Protect WAF with the policy and log configuration created in the previous step.
Step 4 - Configure Load Balancing
-
Create the VirtualServer Resource:
kubectl apply -f https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/v3.6.1/examples/custom-resources/app-protect-waf/virtual-server.yaml
Note that the VirtualServer references the policy waf-policy created in Step 3.
Step 5 - Test the Application
To access the application, curl the coffee and the tea services. We’ll use the –resolve option to set the Host header of a request with webapp.example.com
- Send a request to the application:
curl --resolve webapp.example.com:$IC_HTTP_PORT:$IC_IP http://webapp.example.com:$IC_HTTP_PORT/
Server address: 10.12.0.18:80
Server name: webapp-7586895968-r26zn
- Now, let’s try to send a request with a suspicious URL:
curl --resolve webapp.example.com:$IC_HTTP_PORT:$IC_IP "http://webapp.example.com:$IC_HTTP_PORT/<script>"
<html><head><title>Request Rejected</title></head><body>
- Lastly, let’s try to send some suspicious data that matches the user defined signature.
curl --resolve webapp.example.com:$IC_HTTP_PORT:$IC_IP -X POST -d "apple" http://webapp.example.com:$IC_HTTP_PORT/
<html><head><title>Request Rejected</title></head><body>
As you can see, the suspicious requests were blocked by NGINX App Protect WAF.
- To check the security logs in the syslog pod:
kubectl exec -it <SYSLOG_POD> -- cat /var/log/messages
Configuration Example of Virtual Server
Refer to GitHub repo for Virtual Server example.
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: webapp
spec:
host: webapp.example.com
policies:
- name: waf-policy
upstreams:
- name: webapp
service: webapp-svc
port: 80
routes:
- path: /
action:
pass: webapp