Build NGINX Ingress Controller with F5 WAF for NGINX
This document explains how to build a F5 NGINX Ingress Controller image with F5 WAF for NGINX v5 from source code.
If you’d rather not build your own NGINX Ingress Controller image, see the pre-built image options at the end of this guide.
- To use F5 WAF for NGINX with NGINX Ingress Controller, you must have NGINX Plus.
The following table shows compatibility between NGINX Ingress Controller (NIC) and F5 WAF for NGINX (NAP-WAF) versions:
| NIC Version | NAP-WAF Version | Config Manager | Enforcer |
|---|---|---|---|
| 5.2.1 | 35+5.527 | 5.9.0 | 5.9.0 |
| 5.1.1 | 35+5.498 | 5.8.0 | 5.8.0 |
| 5.0.0 | 34+5.342 | 5.6.0 | 5.6.0 |
| 4.0.1 | 33+5.264 | 5.5.0 | 5.5.0 |
| 3.7.2 | 32+5.1 | 5.3.0 | 5.3.0 |
| 3.6.2 | 32+5.48 | 5.2.0 | 5.2.0 |
Get your system ready for building and pushing the NGINX Ingress Controller image with F5 WAF for NGINX v5.
-
Sign in to your private registry. Replace
<my-docker-registry>with the path to your own private registry.docker login <my-docker-registry> -
Pull the WAF Config Manager image:
docker pull private-registry.nginx.com/nap/waf-config-mgr:<image-tag> -
Pull the WAF Enforcer Docker image
docker pull private-registry.nginx.com/nap/waf-enforcer:<image-tag> -
Clone the NGINX Ingress Controller repository:
console git clone https://github.com/nginx/kubernetes-ingress.git --branch v5.2.1 cd kubernetes-ingress
Follow these steps to build the NGINX Controller Image with F5 WAF for NGINX v5.
-
Place your NGINX Plus license files (nginx-repo.crt and nginx-repo.key) in the project’s root folder. To verify they’re in place, run:
ls nginx-repo.*You should see:
nginx-repo.crt nginx-repo.key -
Build the image. Replace
<makefile target>with your chosen build option and<my-docker-registry>with your private registry’s path. Refer to the Makefile targets table below for the list of build options.make <makefile target> PREFIX=<my-docker-registry>/nginx-plus-ingress TARGET=downloadFor example, to build a Debian-based image with NGINX Plus and F5 WAF for NGINX v5, run:
make debian-image-nap-v5-plus PREFIX=<my-docker-registry>/nginx-plus-ingress TARGET=downloadWhat to expect: The image is built and tagged with a version number, which is derived from the
VERSIONvariable in the Makefile. This version number is used for tracking and deployment purposes.
In the event a patch of NGINX Plus is released, make sure to rebuild your image to get the latest version. If your system is caching the Docker layers and not updating the packages, addDOCKER_BUILD_OPTIONS="--pull --no-cache"to the make command.
Create Docker image for NGINX Ingress Controller (Alpine with NGINX Plus, F5 WAF for NGINX v5 and FIPS)
| Makefile Target | Description | Compatible Systems |
|---|---|---|
| alpine-image-nap-v5-plus-fips | Builds a Alpine-based image with NGINX Plus and the F5 WAF for NGINX v5 module with FIPS. | Alpine |
| debian-image-nap-v5-plus | Builds a Debian-based image with NGINX Plus and the F5 WAF for NGINX v5 module. | Debian |
| ubi-image-nap-v5-plus | Builds a UBI-based image with NGINX Plus and the F5 WAF for NGINX v5 module. | OpenShift |
| ubi-image-nap-dos-v5-plus | Builds a UBI-based image with NGINX Plus, F5 WAF for NGINX v5, and F5 DoS for NGINX. | OpenShift |
For the complete list of Makefile targets and customizable variables, see the Build NGINX Ingress Controller guide.
If you intend to use external references in F5 WAF for NGINX policies, you may want to provide a custom CA certificate to authenticate with the hosting server.
To do so, place the *.crt file in the build folder and uncomment the lines following this comment:
#Uncomment the lines below if you want to install a custom CA certificate
External references are deprecated in NGINX Ingress Controller and will not be supported in future releases.
Once you’ve successfully pulled the WAF v5 manager and enforcer images and built the NGINX Ingress Controller image with F5 WAF for NGINX v5, the next step is to upload them to your private Docker registry. This makes the image available for deployment to your Kubernetes cluster.
To upload the image, run the following command. If you’re using a custom tag, add TAG=your-tag to the end of the command. Replace <my-docker-registry> with your private registry’s path.
make push PREFIX=<my-docker-registry>/nginx-plus-ingressTo upload the WAF config manager and enforcer images run the following commands:
docker push <my-docker-registry>/waf-config-mgr:<your-tag>docker push <my-docker-registry>/waf-enforcer:<your-tag>To make sure your NGINX Ingress Controller pods reach the Ready state, you’ll need to create custom resource definitions (CRDs) for various components.
Alternatively, you can disable this requirement by setting the -enable-custom-resources command-line argument to false.
There are two ways you can install the custom resource definitions:
- Using a URL to apply a single CRD yaml file, which we recommend.
- Applying your local copy of the CRD yaml files, which requires you to clone the repository.
The core custom CRDs are the following:
kubectl apply -f https://raw.githubusercontent.com/nginx/kubernetes-ingress/v5.2.1/deploy/crds.yamlRead the steps outlined in Upgrade from 3.x to 4.x before running the CRD upgrade and perform the steps if applicable.
kubectl apply -f config/crd/bases/k8s.nginx.org_virtualservers.yaml
kubectl apply -f config/crd/bases/k8s.nginx.org_virtualserverroutes.yaml
kubectl apply -f config/crd/bases/k8s.nginx.org_transportservers.yaml
kubectl apply -f config/crd/bases/k8s.nginx.org_policies.yaml
kubectl apply -f config/crd/bases/k8s.nginx.org_globalconfigurations.yamlImportantNGINX Ingress Controller with the AppProtect WAF v5 module works only with policy bundles. You need to modify the Deployment, DaemonSet, or StatefulSet file to include volumes, volume mounts and two WAF 5 docker images:
waf-config-mgrandwaf-enforcer.NGINX Ingress Controller requires the volume mount path to be
/etc/app_protect/bundles.
Below are examples of a PersistentVolume and PersistentVolumeClaim that you can reference in your Helm values:
...
volumes:
- name: <volume_name>
persistentVolumeClaim:
claimName: <claim_name>
...Add volume mounts to the containers section:
...
volumeMounts:
- name: <volume_mount_name>
mountPath: /etc/app_protect/bundles
...Start by setting controller.appprotect.enable to true in your Helm values. This will the standard F5 WAF for NGINX features.
Afterwords, set controller.approtect.v5 to true.
This ensures that both the waf-enforcer and waf-config-mgr containers are deployed alongside the NGINX Ingress Controller containers.
These two additional containers are required when using F5 WAF for NGINX v5.
Your Helm values should look something like this:
controller:
...
## Support for F5 WAF for NGINX
appprotect:
## Enable the F5 WAF for NGINX module in the Ingress Controller.
enable: true
## Enables F5 WAF for NGINX v5.
v5: trueWhether you have created a new PersistentVolume and PersistentVolumeClaim, or you are referencing an existing PersistentVolumeClaim, update the app-protect-bundles volume to reference your PersistentVolumeClaim.
Example helm values:
...
controller:
...
appprotect:
...
volumes:
- name: app-protect-bundles
persistentVolumeClaim:
claimName: <my_claim_name>
...By default,
emptyDirmounts are used. Bundles that are added to these kind of volume mounts will NOT persist across pod restarts.Example default volumes:
yaml ... controller: ... appprotect: ... volumes: - name: app-protect-bundles emptyDir: {} ...
Create required volumes:
volumes:
- name: nginx-etc
emptyDir: {}
- name: nginx-cache # do not set this value in statefulset if volumeclaimtemplate is set
emptyDir: {} # do not set this value in statefulset if volumeclaimtemplate is set
- name: nginx-lib
emptyDir: {}
- name: nginx-log
emptyDir: {}
- name: app-protect-bd-config
emptyDir: {}
- name: app-protect-config
emptyDir: {}
- name: app-protect-bundles
emptyDir: {}Set controller.securityContext.readOnlyRootFilesystem to true.
Example Helm values:
controller:
...
securityContext:
readOnlyRootFilesystem: true
...Set controller.appprotect.enforcer.securityContext.readOnlyRootFilesystem to true.
Example Helm values:
controller:
...
appprotect:
...
enforcer:
securityContext:
readOnlyRootFilesystem: true
...Set controller.appprotect.configManager.securityContext.readOnlyRootFilesystem to true.
Example Helm values:
controller:
...
appprotect:
...
configManager:
securityContext:
readOnlyRootFilesystem: true
...You have two options for deploying NGINX Ingress Controller:
- Deployment. Choose this method for the flexibility to dynamically change the number of NGINX Ingress Controller replicas.
- DaemonSet. Choose this method if you want NGINX Ingress Controller to run on all nodes or a subset of nodes.
- StatefulSet. Choose this method when you need stable, persistent storage and ordered deployment/scaling for your NGINX Ingress Controller pods.
Admin access requiredTo complete these steps you need admin access to your cluster. Refer to to your Kubernetes platform’s documentation to set up admin access. For Google Kubernetes Engine (GKE), you can refer to their Role-Based Access Control guide.
-
Create a namespace and a service account:
kubectl apply -f deployments/common/ns-and-sa.yaml -
Create a cluster role and binding for the service account:
kubectl apply -f deployments/rbac/rbac.yaml
Add a volumes section to deployment template spec:
...
volumes:
- name: <volume_name>
persistentVolumeClaim:
claimName: <claim_name>
...Add volume mounts to the containers section:
...
volumeMounts:
- name: <volume_mount_name>
mountPath: /etc/app_protect/bundles
...Add waf-config-mgr image to the containers section:
...
- name: waf-config-mgr
image: private-registry.nginx.com/nap/waf-config-mgr:<version-tag>
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- all
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: app-protect-bundles
mountPath: /etc/app_protect/bundles
...Add waf-enforcer image to the containers section:
...
- name: waf-enforcer
image: private-registry.nginx.com/nap/waf-enforcer:<version-tag>
imagePullPolicy: IfNotPresent
env:
- name: ENFORCER_PORT
value: "50000"
- name: ENFORCER_CONFIG_TIMEOUT
value: "0"
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
...Add volumeMounts as below:
...
- image: <my_docker_registry>:<version_tag>
imagePullPolicy: IfNotPresent
name: nginx-plus-ingress
volumeMounts:
- name: app-protect-bd-config
mountPath: /opt/app_protect/bd_config
- name: app-protect-config
mountPath: /opt/app_protect/config
- name: app-protect-bundles
mountPath: /etc/app_protect/bundles
...Add readOnlyRootFilesystem to the NIC container and set valut to true as below:
...
- image: <my_docker_registry>:<version_tag>
imagePullPolicy: IfNotPresent
name: nginx-plus-ingress
...
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 101
readOnlyRootFilesystem: true
...
volumeMounts:
- mountPath: /etc/nginx
name: nginx-etc
- mountPath: /var/cache/nginx
name: nginx-cache
- mountPath: /var/lib/nginx
name: nginx-lib
- mountPath: /var/log/nginx
name: nginx-log
- mountPath: /opt/app_protect/bd_config
name: app-protect-bd-config
- mountPath: /opt/app_protect/config
name: app-protect-config
- mountPath: /etc/app_protect/bundles
name: app-protect-bundles
...Add readOnlyRootFilesystem to the waf-config-mgr container and set value to true as below:
...
- name: waf-config-mgr
image: private-registry.nginx.com/nap/waf-config-mgr:<version-tag>
imagePullPolicy: IfNotPresent
...
securityContext:
readOnlyRootFilesystem: true
...
...Add readOnlyRootFilesystem to the waf-enforcer container and set value to true as below:
...
- name: waf-enforcer
image: private-registry.nginx.com/nap/waf-enforcer:<version-tag>
imagePullPolicy: IfNotPresent
...
securityContext:
readOnlyRootFilesystem: true
...
...StatefulSet Volume Configuration: When using StatefulSet deployments, thenginx-cachevolume is automatically provided viavolumeClaimTemplatesfor persistent storage. F5 WAF for NGINX v5 volumes (like app-protect-config, app-protect-bundles) are still configured as regular volumes in thevolumessection. UseemptyDirfor temporary data or PersistentVolumeClaims if you need persistence for App Protect configurations across pod restarts.
For additional context on managing containers using Kubernetes Deployments, refer to the official Kubernetes Deployments documentation.
When you deploy NGINX Ingress Controller as a Deployment, Kubernetes automatically sets up a single NGINX Ingress Controller pod.
-
For NGINX, run:
kubectl apply -f deployments/deployment/nginx-ingress.yaml -
For NGINX Plus, run:
kubectl apply -f deployments/deployment/nginx-plus-ingress.yamlUpdate the
nginx-plus-ingress.yamlfile to include your chosen image from the F5 Container registry or your custom container image.
For additional context on managing containers using Kubernetes DaemonSets, refer to the official Kubernetes DaemonSets documentation.
When you deploy NGINX Ingress Controller as a DaemonSet, Kubernetes creates an Ingress Controller pod on every node in the cluster.
-
For NGINX, run:
kubectl apply -f deployments/daemon-set/nginx-ingress.yaml -
For NGINX Plus, run:
kubectl apply -f deployments/daemon-set/nginx-plus-ingress.yamlUpdate the
nginx-plus-ingress.yamlfile to include your chosen image from the F5 Container registry or your custom container image.
For additional context on managing containers using Kubernetes StatefulSets, refer to the official Kubernetes StatefulSets documentation.
When you deploy NGINX Ingress Controller as a StatefulSet, Kubernetes creates pods with stable network identities and persistent storage.
-
For NGINX, run:
kubectl apply -f deployments/stateful-set/nginx-ingress.yaml -
For NGINX Plus, run:
kubectl apply -f deployments/stateful-set/nginx-plus-ingress.yamlUpdate the
nginx-plus-ingress.yamlfile to include your chosen image from the F5 Container registry or your custom container image.
StatefulSets include persistent volume claims for nginx cache storage viavolumeClaimTemplates. You may need to configure a StorageClass in your cluster or modify the volumeClaimTemplates section in the manifest to match your storage requirements. Other volumes (like those needed for App Protect modules) are configured in the regularvolumessection, not in volumeClaimTemplates.
To enable the F5 DoS for NGINX Module:
- Add the
enable-app-protectcommand-line argument to your Deployment, DaemonSet, or StatefulSet file.
To confirm the NGINX Ingress Controller pods are operational, run:
kubectl get pods --namespace=nginx-ingressFor more information, see the Configuration guide and the NGINX Ingress Controller with App Protect version 5 example resources on GitHub for VirtualServer resources.
If you prefer not to build your own NGINX Ingress Controller image, you can use pre-built images. Here are your options:
- Download the image using your NGINX Ingress Controller subscription certificate and key. View the Download NGINX Ingress Controller from the F5 Registry topic.
- The Add an NGINX Ingress Controller image to your cluster topic describes how to use your subscription JWT token to get the image.