Skip to content

Deploy WAF containers when enabled in NGINXProxy #3481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ CHART_DIR = $(SELF_DIR)charts/nginx-gateway-fabric
NGINX_CONF_DIR = internal/controller/nginx/conf
NJS_DIR = internal/controller/nginx/modules/src
KIND_CONFIG_FILE = $(SELF_DIR)config/cluster/kind-cluster.yaml
NAP_WAF_ALPINE_VERSION = 3.19
NGINX_DOCKER_BUILD_PLUS_ARGS = --secret id=nginx-repo.crt,src=$(SELF_DIR)nginx-repo.crt --secret id=nginx-repo.key,src=$(SELF_DIR)nginx-repo.key
NGINX_DOCKER_BUILD_NAP_WAF_ARGS = --build-arg ALPINE_VERSION=$(NAP_WAF_ALPINE_VERSION) --build-arg INCLUDE_NAP_WAF=true
BUILD_AGENT = local

PROD_TELEMETRY_ENDPOINT = oss.edge.df.f5.com:443
Expand Down Expand Up @@ -77,6 +79,9 @@ build-images: build-ngf-image build-nginx-image ## Build the NGF and nginx docke
.PHONY: build-images-with-plus
build-images-with-plus: build-ngf-image build-nginx-plus-image ## Build the NGF and NGINX Plus docker images

.PHONY: build-images-nap-waf
build-images-with-nap-waf: build-ngf-image build-nginx-plus-image-with-nap-waf ## Build the NGF and NGINX Plus with WAF docker images

.PHONY: build-prod-ngf-image
build-prod-ngf-image: TELEMETRY_ENDPOINT=$(PROD_TELEMETRY_ENDPOINT)
build-prod-ngf-image: build-ngf-image ## Build the NGF docker image for production
Expand All @@ -99,6 +104,13 @@ build-prod-nginx-plus-image: build-nginx-plus-image ## Build the custom nginx pl
build-nginx-plus-image: check-for-docker ## Build the custom nginx plus image
docker build --platform linux/$(GOARCH) $(strip $(NGINX_DOCKER_BUILD_OPTIONS)) $(strip $(NGINX_DOCKER_BUILD_PLUS_ARGS)) -f $(SELF_DIR)build/Dockerfile.nginxplus -t $(strip $(NGINX_PLUS_PREFIX)):$(strip $(TAG)) $(strip $(SELF_DIR))

.PHONY: build-nginx-plus-image-with-nap-waf
build-nginx-plus-image-with-nap-waf: check-for-docker ## Build the custom nginx plus image with NAP WAF. Note that arm is NOT supported.
@if [ $(GOARCH) = "arm64" ]; then \
echo "\033[0;31mIMPORTANT:\033[0m The nginx-plus-waf image cannot be built for arm64 architecture and will be built for amd64."; \
fi
docker build --platform linux/amd64 $(strip $(NGINX_DOCKER_BUILD_OPTIONS)) $(strip $(NGINX_DOCKER_BUILD_PLUS_ARGS)) $(strip $(NGINX_DOCKER_BUILD_NAP_WAF_ARGS)) -f $(SELF_DIR)build/Dockerfile.nginxplus -t $(strip $(NGINX_PLUS_PREFIX)):$(strip $(TAG)) $(strip $(SELF_DIR))

.PHONY: check-for-docker
check-for-docker: ## Check if Docker is installed
@docker -v || (code=$$?; printf "\033[0;31mError\033[0m: there was a problem with Docker\n"; exit $$code)
Expand Down
69 changes: 69 additions & 0 deletions apis/v1alpha2/nginxproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,35 @@ type NginxProxySpec struct {
//
// +optional
DisableHTTP2 *bool `json:"disableHTTP2,omitempty"`
// WAF enables NGINX App Protect WAF functionality.
// When enabled, NGINX Gateway Fabric will deploy additional WAF containers
// (waf-enforcer and waf-config-mgr) alongside the main NGINX container.
// Default is "disabled".
//
// +optional
// +kubebuilder:default:=disabled
WAF *WAFState `json:"waf,omitempty"`
// Kubernetes contains the configuration for the NGINX Deployment and Service Kubernetes objects.
//
// +optional
Kubernetes *KubernetesSpec `json:"kubernetes,omitempty"`
}

// WAFState defines the state of WAF functionality.
//
// +kubebuilder:validation:Enum=enabled;disabled
type WAFState string

const (
// WAFEnabled enables NGINX App Protect WAF functionality.
// This will deploy additional containers for WAF enforcement and configuration management.
WAFEnabled WAFState = "enabled"

// WAFDisabled disables NGINX App Protect WAF functionality.
// Only the standard NGINX container will be deployed.
WAFDisabled WAFState = "disabled"
)

// Telemetry specifies the OpenTelemetry configuration.
type Telemetry struct {
// DisabledFeatures specifies OpenTelemetry features to be disabled.
Expand Down Expand Up @@ -388,6 +411,12 @@ type DeploymentSpec struct {
// +optional
Replicas *int32 `json:"replicas,omitempty"`

// WAFContainers defines container specifications for NGINX App Protect WAF v5 containers.
// These containers are only deployed when WAF is enabled in the NginxProxy spec.
//
// +optional
WAFContainers *WAFContainerSpec `json:"wafContainers,omitempty"`

// Pod defines Pod-specific fields.
//
// +optional
Expand All @@ -401,6 +430,12 @@ type DeploymentSpec struct {

// DaemonSet is the configuration for the NGINX DaemonSet.
type DaemonSetSpec struct {
// WAFContainers defines container specifications for NGINX App Protect WAF v5 containers.
// These containers are only deployed when WAF is enabled in the NginxProxy spec.
//
// +optional
WAFContainers *WAFContainerSpec `json:"wafContainers,omitempty"`

// Pod defines Pod-specific fields.
//
// +optional
Expand Down Expand Up @@ -485,6 +520,40 @@ type ContainerSpec struct {
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
}

// WAFContainerSpec defines the container specifications for NGINX App Protect WAF v5.
// NAP v5 requires two additional containers: waf-enforcer and waf-config-mgr.
type WAFContainerSpec struct {
// Enforcer defines the configuration for the WAF enforcer container.
// This container performs the actual WAF enforcement and policy application.
//
// +optional
Enforcer *WAFContainerConfig `json:"enforcer,omitempty"`

// ConfigManager defines the configuration for the WAF configuration manager container.
// This container manages policy configuration and communication with the enforcer.
//
// +optional
ConfigManager *WAFContainerConfig `json:"configManager,omitempty"`
}

// WAFContainerConfig defines the configuration for a single WAF container.
type WAFContainerConfig struct {
// Image is the container image to use for this WAF container.
//
// +optional
Image *Image `json:"image,omitempty"`

// Resources describes the compute resource requirements for this WAF container.
//
// +optional
Resources *corev1.ResourceRequirements `json:"resources,omitempty"`

// VolumeMounts describe the mounting of Volumes within the WAF container.
//
// +optional
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
}

// Image is the NGINX image to use.
type Image struct {
// Repository is the image path.
Expand Down
72 changes: 72 additions & 0 deletions apis/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion build/Dockerfile.nginxplus
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
# syntax=docker/dockerfile:1.16

# renovate: datasource=docker depName=alpine
ARG ALPINE_VERSION=3.21

FROM scratch AS nginx-files

# the following links can be replaced with local files if needed, i.e. ADD --chown=101:1001 <local_file> <container_file>
ADD --link --chown=101:1001 https://cs.nginx.com/static/keys/nginx_signing.rsa.pub nginx_signing.rsa.pub

FROM alpine:3.21
FROM alpine:${ALPINE_VERSION}

ARG NGINX_PLUS_VERSION=R34
# renovate: datasource=github-tags depName=nginx/agent extractVersion=^v?(?<version>.*)$
ARG NGINX_AGENT_VERSION=3.0.0
ARG APP_PROTECT_VERSION=34.5.342
ARG INCLUDE_NAP_WAF=false
ARG NJS_DIR
ARG NGINX_CONF_DIR
ARG BUILD_AGENT
Expand All @@ -20,6 +26,10 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/apk/cert.pem,mode=0644 \
&& adduser -S -D -H -u 101 -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx \
&& printf "%s\n" "https://pkgs.nginx.com/plus/${NGINX_PLUS_VERSION}/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \
&& printf "%s\n" "https://pkgs.nginx.com/nginx-agent/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \
&& if [ "${INCLUDE_NAP_WAF}" = "true" ]; then \
printf "%s\n" "https://pkgs.nginx.com/app-protect-x-plus/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \
&& apk add --no-cache app-protect-module-plus~=${APP_PROTECT_VERSION}; \
fi \
&& apk add --no-cache nginx-plus nginx-plus-module-njs nginx-plus-module-otel nginx-agent=${NGINX_AGENT_VERSION}

RUN apk add --no-cache libcap bash \
Expand All @@ -45,4 +55,5 @@ USER 101:1001

LABEL org.nginx.ngf.image.build.agent="${BUILD_AGENT}"

ENV USE_NAP_WAF=${INCLUDE_NAP_WAF}
ENTRYPOINT ["/agent/entrypoint.sh"]
5 changes: 5 additions & 0 deletions build/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ trap 'handle_quit' QUIT

rm -rf /var/run/nginx/*.sock

# Bootstrap the necessary app protect files
if [ "${USE_NAP_WAF:-false}" = "true" ]; then
touch /opt/app_protect/bd_config/policy_path.map
fi

# Launch nginx
echo "starting nginx ..."

Expand Down
3 changes: 2 additions & 1 deletion charts/nginx-gateway-fabric/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
| `certGenerator.serverTLSSecretName` | The name of the Secret containing TLS CA, certificate, and key for the NGINX Gateway Fabric control plane to securely communicate with the NGINX Agent. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway). | string | `"server-tls"` |
| `clusterDomain` | The DNS cluster domain of your Kubernetes cluster. | string | `"cluster.local"` |
| `gateways` | A list of Gateway objects. View https://gateway-api.sigs.k8s.io/reference/spec/#gateway for full Gateway reference. | list | `[]` |
| `nginx` | The nginx section contains the configuration for all NGINX data plane deployments installed by the NGINX Gateway Fabric control plane. | object | `{"config":{},"container":{},"debug":false,"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric/nginx","tag":"edge"},"imagePullSecret":"","imagePullSecrets":[],"kind":"deployment","plus":false,"pod":{},"replicas":1,"service":{"externalTrafficPolicy":"Local","loadBalancerClass":"","loadBalancerIP":"","loadBalancerSourceRanges":[],"nodePorts":[],"type":"LoadBalancer"},"usage":{"caSecretName":"","clientSSLSecretName":"","endpoint":"","resolver":"","secretName":"nplus-license","skipVerify":false}}` |
| `nginx` | The nginx section contains the configuration for all NGINX data plane deployments installed by the NGINX Gateway Fabric control plane. | object | `{"config":{},"container":{},"debug":false,"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric/nginx","tag":"edge"},"imagePullSecret":"","imagePullSecrets":[],"kind":"deployment","plus":false,"pod":{},"replicas":1,"service":{"externalTrafficPolicy":"Local","loadBalancerClass":"","loadBalancerIP":"","loadBalancerSourceRanges":[],"nodePorts":[],"type":"LoadBalancer"},"usage":{"caSecretName":"","clientSSLSecretName":"","endpoint":"","resolver":"","secretName":"nplus-license","skipVerify":false},"wafContainers":{}}` |
| `nginx.config` | The configuration for the data plane that is contained in the NginxProxy resource. This is applied globally to all Gateways managed by this instance of NGINX Gateway Fabric. | object | `{}` |
| `nginx.container` | The container configuration for the NGINX container. This is applied globally to all Gateways managed by this instance of NGINX Gateway Fabric. | object | `{}` |
| `nginx.debug` | Enable debugging for NGINX. Uses the nginx-debug binary. The NGINX error log level should be set to debug in the NginxProxy resource. | bool | `false` |
Expand All @@ -283,6 +283,7 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
| `nginx.usage.resolver` | The nameserver used to resolve the NGINX Plus usage reporting endpoint. Used with NGINX Instance Manager. | string | `""` |
| `nginx.usage.secretName` | The name of the Secret containing the JWT for NGINX Plus usage reporting. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway). | string | `"nplus-license"` |
| `nginx.usage.skipVerify` | Disable client verification of the NGINX Plus usage reporting server certificate. | bool | `false` |
| `nginx.wafContainers` | Configuration for NGINX App Protect WAF v5 containers. These containers are only deployed when WAF is enabled via nginx.config.waf: "Enabled". All settings are optional overrides - defaults are provided by NGF. | object | `{}` |
| `nginxGateway` | The nginxGateway section contains configuration for the NGINX Gateway Fabric control plane deployment. | object | `{"affinity":{},"config":{"logging":{"level":"info"}},"configAnnotations":{},"extraVolumeMounts":[],"extraVolumes":[],"gatewayClassAnnotations":{},"gatewayClassName":"nginx","gatewayControllerName":"gateway.nginx.org/nginx-gateway-controller","gwAPIExperimentalFeatures":{"enable":false},"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric","tag":"edge"},"kind":"deployment","labels":{},"leaderElection":{"enable":true,"lockName":""},"lifecycle":{},"metrics":{"enable":true,"port":9113,"secure":false},"nodeSelector":{},"podAnnotations":{},"productTelemetry":{"enable":true},"readinessProbe":{"enable":true,"initialDelaySeconds":3,"port":8081},"replicas":1,"resources":{},"service":{"annotations":{}},"serviceAccount":{"annotations":{},"imagePullSecret":"","imagePullSecrets":[],"name":""},"snippetsFilters":{"enable":false},"terminationGracePeriodSeconds":30,"tolerations":[],"topologySpreadConstraints":[]}` |
| `nginxGateway.affinity` | The affinity of the NGINX Gateway Fabric control plane pod. | object | `{}` |
| `nginxGateway.config.logging.level` | Log level. | string | `"info"` |
Expand Down
8 changes: 8 additions & 0 deletions charts/nginx-gateway-fabric/templates/nginxproxy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ spec:
{{- if .Values.nginx.debug }}
debug: {{ .Values.nginx.debug }}
{{- end }}
{{- if and .Values.nginx.wafContainers }}
wafContainers:
{{- toYaml .Values.nginx.wafContainers | nindent 8 }}
{{- end }}
{{- end }}
{{- if eq .Values.nginx.kind "daemonSet" }}
daemonSet:
Expand All @@ -42,6 +46,10 @@ spec:
{{- if .Values.nginx.debug }}
debug: {{ .Values.nginx.debug }}
{{- end }}
{{- if and .Values.nginx.wafContainers }}
wafContainers:
{{- toYaml .Values.nginx.wafContainers | nindent 8 }}
{{- end }}
{{- end }}
{{- if .Values.nginx.service }}
service:
Expand Down
15 changes: 15 additions & 0 deletions charts/nginx-gateway-fabric/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,15 @@
},
"required": [],
"type": "object"
},
"waf": {
"description": "WAF enables NGINX App Protect WAF functionality.",
"enum": [
"enabled",
"disabled"
],
"required": [],
"type": "string"
}
},
"required": [],
Expand Down Expand Up @@ -488,6 +497,12 @@
"required": [],
"title": "usage",
"type": "object"
},
"wafContainers": {
"description": "Configuration for NGINX App Protect WAF v5 containers.\nThese containers are only deployed when WAF is enabled via nginx.config.waf: \"Enabled\".\nAll settings are optional overrides - defaults are provided by NGF.",
"required": [],
"title": "wafContainers",
"type": "object"
}
},
"required": [],
Expand Down
33 changes: 33 additions & 0 deletions charts/nginx-gateway-fabric/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,12 @@ nginx:
# - IPAddress
# value:
# type: string
# waf:
# type: string
# description: WAF enables NGINX App Protect WAF functionality.
# enum:
# - enabled
# - disabled
# @schema
# -- The configuration for the data plane that is contained in the NginxProxy resource. This is applied globally to all Gateways
# managed by this instance of NGINX Gateway Fabric.
Expand Down Expand Up @@ -406,6 +412,33 @@ nginx:
# -- extraVolumeMounts are the additional volume mounts for the NGINX container.
# extraVolumeMounts: []

# -- Configuration for NGINX App Protect WAF v5 containers.
# These containers are only deployed when WAF is enabled via nginx.config.waf: "Enabled".
# All settings are optional overrides - defaults are provided by NGF.
wafContainers: {}
# -- WAF Enforcer container configuration.
# enforcer:
# image: {}

# -- The resource requirements of the WAF Enforcer container.
# resources: {}
#
# # -- Additional volume mounts for the WAF enforcer container.
# # NAP v5 shared volumes are automatically configured by NGF.
# volumeMounts: []

# -- WAF Configuration Manager container configuration.
# configManager:
# # -- Container image configuration
# image: {}
#
# # -- The resource requirements of the WAF config manager container.
# resources: {}

# # -- Additional volume mounts for the WAF config manager container.
# # NAP v5 shared volumes are automatically configured by NGF.
# volumeMounts: []

# -- The service configuration for the NGINX data plane. This is applied globally to all Gateways managed by this
# instance of NGINX Gateway Fabric.
service:
Expand Down
Loading
Loading