diff --git a/content/includes/waf/plm-define-policy-bundle-method.md b/content/includes/waf/plm-define-policy-bundle-method.md new file mode 100644 index 000000000..f86bd3c63 --- /dev/null +++ b/content/includes/waf/plm-define-policy-bundle-method.md @@ -0,0 +1,75 @@ +--- +f5-product: F5 WAF for NGINX +f5-files: +- content/ngf/waf-integration/get-started-plm.md +- content/nic/waf-integration/get-started-plm.md +--- + +The precompiled-bundle method lets you reference a `.tgz` policy bundle stored in an artifact registry (for example, Artifactory or Nexus). The Policy Controller imports the bundle and stores it in the SeaweedFS object store without recompiling it. + +Use this method when: + +- Your security team compiles and publishes bundles through an external pipeline. +- You want to decouple policy compilation from cluster operations. + +Create an `APPolicy` resource that references your bundle: + +```yaml +apiVersion: appprotect.f5.com/v1 +kind: APPolicy +metadata: + name: + namespace: plm-system +spec: + policy: + $ref: "https:///.tgz" +``` + +Replace ``, ``, and `` with your values. + +Apply the resource: + +```shell +kubectl apply -f .yaml +``` + +{{< call-out class="note" title="Note" >}} +The Policy Controller must be able to reach the artifact registry host over HTTPS. If the registry uses a private certificate authority, configure the Policy Controller to trust that CA. +{{< /call-out >}} + +#### Confirm the policy is ready + +The Policy Controller processes the bundle and updates the `APPolicy` status. Check the `bundle.state` field: + +```shell +kubectl get appolicy \ + --namespace plm-system \ + --output jsonpath='State: {.status.bundle.state}{"\n"}Bundle: {.status.bundle.location}{"\n"}isCompiled: {.status.processing.isCompiled}{"\n"}' +``` + +When the bundle is ready, the output looks like this: + +```text +State: ready +Bundle: s3://plm-system/bundles/_imported_.tgz +isCompiled: false +``` + +`isCompiled: false` confirms the bundle was imported as-is and not recompiled. + +`bundle.state` can be one of: + +| State | Meaning | +|-------|---------| +| `pending` | The Policy Controller has not yet processed the resource. | +| `processing` | The Policy Controller is importing or storing the bundle. | +| `ready` | The bundle is stored and ready to use. `bundle.location` is populated. | +| `invalid` | The bundle could not be imported. Check the status for error detail. | + +#### Update a precompiled bundle + +The Policy Controller does not poll the artifact registry for changes. To pick up a new version of a bundle, update the `APPolicy` resource to reference the new bundle URL (or bump its revision annotation) and re-apply it: + +```shell +kubectl apply -f .yaml +``` diff --git a/content/includes/waf/plm-deploy-infrastructure.md b/content/includes/waf/plm-deploy-infrastructure.md new file mode 100644 index 000000000..692009dc0 --- /dev/null +++ b/content/includes/waf/plm-deploy-infrastructure.md @@ -0,0 +1,147 @@ +--- +f5-product: F5 WAF for NGINX +f5-files: +- content/ngf/waf-integration/get-started-plm.md +- content/nic/waf-integration/get-started-plm.md +--- + +The Policy Lifecycle Manager (PLM) backend runs as a Kubernetes operator. It watches WAF custom resources and compiles WAF policies into bundles. The Policy Controller delegates compilation to a separate compiler service over gRPC. The resulting bundles are stored in an embedded SeaweedFS S3-compatible object store. + +F5 WAF for NGINX is installed using a separate Helm chart from your NGINX data plane. The steps in this section install only the F5 WAF for NGINX PLM components and do not affect your existing NGINX installation. + +### Install the CRDs + +Install the four custom resource definitions (CRDs) that the Policy Controller manages: + +```shell +kubectl apply -f https://raw.githubusercontent.com/nginx/waf-policy-controller/main/manifests/1-deploy-crds.yaml +``` + +Confirm all four CRDs are present: + +```shell +kubectl get crd | grep appprotect.f5.com +``` + +Expected output: + +```text +appolicies.appprotect.f5.com +aplogconfs.appprotect.f5.com +apsignatures.appprotect.f5.com +apusersigs.appprotect.f5.com +``` + +### Create the registry pull secret + +Create a namespace for the PLM components, then create the registry pull secret using the credentials from the previous section. Replace `` with your F5 WAF for NGINX JWT. + + + +```shell +kubectl create namespace plm-system + +kubectl create secret docker-registry regcred \ + --namespace plm-system \ + --docker-server=private-registry.nginx.com \ + --docker-username= \ + --docker-password=none \ + --dry-run=client --output yaml | kubectl apply -f - +``` + +### Install the Policy Controller + +Create a values file for the Helm installation. Replace `` and `` with the base64-encoded contents of your `nginx-repo.crt` and `nginx-repo.key` files. To encode them, run: + +```shell +base64 --wrap=0 < nginx-repo.crt +base64 --wrap=0 < nginx-repo.key +``` + +Create `/tmp/plm-values.yaml`: + +```yaml +imagePullSecrets: + - name: regcred +securityUpdatesRepo: + cert: "" + key: "" +policyController: + image: + tag: "{{< version-waf-policy-controller >}}" +compiler: + image: + tag: "{{< version-waf-policy-controller >}}" +seaweedfsOperatorConfig: + seaweedfs: + image: + tag: "{{< version-waf-policy-controller >}}" +seaweedfs-operator: + image: + tag: "{{< version-waf-policy-controller >}}" + pullSecrets: regcred +``` + +Add the NGINX Helm repository and install the chart: + +```shell +helm repo add nginx-stable https://helm.nginx.com/stable +helm repo update nginx-stable + +helm upgrade --install plm nginx-stable/f5-waf-policy-controller \ + --version {{< version-waf-policy-controller >}} \ + --namespace plm-system \ + --values /tmp/plm-values.yaml +``` + +### Verify the deployment + +Wait for all PLM components to become ready. The Policy Controller's init container waits for both the compiler service and the SeaweedFS S3 endpoint to be available before it starts, so the controller pod will show `Init:0/1` until SeaweedFS is ready. + +Wait for the SeaweedFS storage backend: + +```shell +kubectl rollout status deployment/plm-seaweedfs-operator \ + --namespace plm-system --timeout=120s + +kubectl wait pods \ + --selector app.kubernetes.io/name=seaweedfs \ + --for=condition=Ready \ + --namespace plm-system \ + --timeout=180s +``` + +Wait for the Policy Controller: + +```shell +kubectl rollout status deployment/plm-f5-waf-policy-controller \ + --namespace plm-system --timeout=180s +``` + +Confirm all eight pods are running: + +```shell +kubectl get pods --namespace plm-system +``` + +Expected output: + +```text +NAME READY STATUS RESTARTS +plm-f5-waf-compiler-service-xxxxx 1/1 Running 0 +plm-f5-waf-policy-controller-xxxxx 1/1 Running 0 +plm-seaweedfs-operator-xxxxx 1/1 Running 0 +plm-f5-waf-seaweed-master-0 1/1 Running 0 +plm-f5-waf-seaweed-filer-0 1/1 Running 0 +plm-f5-waf-seaweed-volume-0 1/1 Running 0 +plm-f5-waf-seaweed-volume-1 1/1 Running 0 +plm-f5-waf-seaweed-volume-2 1/1 Running 0 +``` + +Confirm the four CRDs are present: + +```shell +kubectl get crd | grep appprotect.f5.com +``` + +All eight pods running and all four CRDs present confirms the PLM backend is ready. diff --git a/content/ngf/reference/cli-help.md b/content/ngf/reference/cli-help.md index 6e51e3d5e..d0a575b21 100644 --- a/content/ngf/reference/cli-help.md +++ b/content/ngf/reference/cli-help.md @@ -44,6 +44,11 @@ This command runs the NGINX Gateway Fabric control plane. | _leader-election-lock-name_ | _string_ | The name of the leader election lock. A lease object with this name will be created in the same namespace as the controller (Default: `"nginx-gateway-leader-election-lock"`). | | _product-telemetry-disable_ | _bool_ | Disable the collection of product telemetry (Default: `false`). | | _nginx-docker-secret_ | _list_ | The name of the NGINX docker registry Secret(s). Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway). | +| _plm-storage-url_ | _string_ | The URL of the Policy Lifecycle Manager (PLM) storage service (HTTP or HTTPS). | +| _plm-storage-credentials-secret_ | _string_ | The name of the Secret containing the PLM storage S3 secret access key (`seaweedfs_admin_secret`). Must exist in the same namespace as the NGINX Gateway Fabric control plane (default: `nginx-gateway`), unless prefixed with `/`. | +| _plm-storage-ca-secret_ | _string_ | The name of the Secret containing the CA certificate (`ca.crt`) for verifying the PLM storage TLS server certificate. Must exist in the same namespace as the NGINX Gateway Fabric control plane (default: `nginx-gateway`), unless prefixed with `/`. | +| _plm-storage-client-ssl-secret_ | _string_ | The name of the Secret containing the client certificate and key (`tls.crt`/`tls.key`) for mutual TLS with PLM storage. Must exist in the same namespace as the NGINX Gateway Fabric control plane (default: `nginx-gateway`), unless prefixed with `/`. | +| _plm-storage-skip-verify_ | _bool_ | Disable TLS certificate verification for PLM storage connections. Use for testing only (Default: `false`). | | _usage-report-secret_ | _string_ | 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) | | _usage-report-endpoint_ | _string_ | The endpoint of the NGINX Plus usage reporting server. | | _usage-report-resolver_ | _string_ | The nameserver used to resolve the NGINX Plus usage reporting endpoint. Used with NGINX Instance Manager. | diff --git a/content/ngf/traffic-security/basic-authentication.md b/content/ngf/traffic-security/basic-authentication.md index 47b20f4be..1a5c2d7a3 100644 --- a/content/ngf/traffic-security/basic-authentication.md +++ b/content/ngf/traffic-security/basic-authentication.md @@ -138,7 +138,7 @@ kubectl describe gateways.gateway.networking.k8s.io cafe-gateway ```text Addresses: Type: IPAddress - Value: 10.96.20.187 + Value: 192.0.2.1 ``` Save the public IP address and port(s) of the Gateway into shell variables: diff --git a/content/ngf/traffic-security/cors.md b/content/ngf/traffic-security/cors.md index 67b27a5dc..0d65aa3ec 100644 --- a/content/ngf/traffic-security/cors.md +++ b/content/ngf/traffic-security/cors.md @@ -95,7 +95,7 @@ kubectl describe gateways.gateway.networking.k8s.io gateway ```text Addresses: Type: IPAddress - Value: 10.96.20.187 + Value: 192.0.2.1 ``` Save the public IP address and port(s) of the Gateway into shell variables: diff --git a/content/ngf/traffic-security/jwt-authentication.md b/content/ngf/traffic-security/jwt-authentication.md index f7fe79667..79fe73666 100644 --- a/content/ngf/traffic-security/jwt-authentication.md +++ b/content/ngf/traffic-security/jwt-authentication.md @@ -151,7 +151,7 @@ kubectl describe gateways.gateway.networking.k8s.io cafe-gateway ```text Addresses: Type: IPAddress - Value: 10.96.20.187 + Value: 192.0.2.1 ``` Save the public IP address and port of the Gateway into shell variables: diff --git a/content/ngf/traffic-security/oidc-authentication.md b/content/ngf/traffic-security/oidc-authentication.md index 76214dde3..165bdaf5a 100644 --- a/content/ngf/traffic-security/oidc-authentication.md +++ b/content/ngf/traffic-security/oidc-authentication.md @@ -344,7 +344,7 @@ kubectl describe gateways.gateway.networking.k8s.io gateway ```text Addresses: Type: IPAddress - Value: 10.96.20.187 + Value: 192.0.2.1 ``` Save the IP and port into shell variables: diff --git a/content/ngf/waf-integration/get-started-http.md b/content/ngf/waf-integration/get-started-http.md index a34bdddce..30d8ac7ef 100644 --- a/content/ngf/waf-integration/get-started-http.md +++ b/content/ngf/waf-integration/get-started-http.md @@ -415,7 +415,7 @@ kubectl describe gateways.gateway.networking.k8s.io gateway ```text Addresses: Type: IPAddress - Value: 10.96.20.187 + Value: 192.0.2.1 ``` Save the public IP address and port(s) of the Gateway into shell variables: @@ -530,6 +530,7 @@ SSN: *******6789 ## Next steps +- [Get started with F5 WAF for NGINX using PLM]({{< ref "/ngf/waf-integration/get-started-plm.md" >}}) for a Kubernetes-native policy workflow using PLM. - [Configure policy sources (NIM and NGINX One Console)]({{< ref "/ngf/waf-integration/policy-sources.md" >}}) for managed policy workflows. - [Configure WAF settings]({{< ref "/ngf/waf-integration/configuration.md" >}}) for polling, TLS, authentication, security logging, and fail-open behavior. - [Troubleshoot WAFPolicy status]({{< ref "/ngf/waf-integration/troubleshooting.md" >}}) if a condition is `False`. diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 8466a6c37..fda5ca33a 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -14,24 +14,645 @@ f5-summary: > f5-audience: operator --- -Introduction text goes here. +This tutorial walks through the complete flow of protecting traffic with F5 WAF for NGINX using Policy Lifecycle Management (PLM). By the end, you will have: + +- Deployed the PLM infrastructure (Policy Controller and SeaweedFS storage) +- Connected NGINX Gateway Fabric to PLM storage +- Defined a WAF policy using `APPolicy` and `APLogConf` custom resources +- Attached a `WAFPolicy` to a Gateway and configured HTTPRoutes +- Validated policy compilation and verified that attacks are blocked + +PLM is one of four WAF policy source types. With PLM, you define your security posture as `APPolicy` and `APLogConf` custom resources instead of compiling and hosting bundles yourself. For a comparison with the other source types, see [PLM (Policy Lifecycle Management)]({{< ref "/ngf/waf-integration/overview.md#plm-policy-lifecycle-management" >}}). ## Before you begin +- Have `kubectl` access to a Kubernetes cluster. +- Have a valid F5 WAF for NGINX subscription. F5 WAF for NGINX is a separate add-on to NGINX Plus and isn't included with the NGINX Plus license. +- Have your private registry credentials Secret for `private-registry.nginx.com` available. You'll reference this Secret when you install NGINX Gateway Fabric. + +This tutorial uses the following example values. You can use different values — if you do, replace them consistently throughout. + +| Example value | What it represents | +|---|---| +| `plm-system` | Namespace for the PLM backend components | +| `plm` | Helm release name for the PLM installation | +| `{{< version-waf-policy-controller >}}` | F5 WAF for NGINX Policy Controller chart and image version | +| `security` | Namespace for `APPolicy` and `APLogConf` resources | +| `default` | Namespace for the Gateway and `WAFPolicy` | +| `cafe.example.com` | Example hostname for HTTPRoutes | + ## Deploy PLM infrastructure +{{< include "waf/plm-deploy-infrastructure.md" >}} + ## Configure NGF to connect to PLM storage -## Enable WAF in the NginxProxy resource +NGINX Gateway Fabric fetches compiled bundles from in-cluster PLM storage. You set up storage access once, cluster-wide, at install time. It applies to every `WAFPolicy` that uses `type: PLM`. + +Create a `values.yaml` file that enables WAF and sets the PLM storage connection details under `nginxGateway.plmStorage`: + +```yaml +nginxGateway: + plmStorage: + url: "https://plm-f5-waf-seaweed-filer.plm-system.svc.cluster.local" + credentialsSecretName: "plm-system/plm-f5-waf-seaweedfs-auth" # contains the seaweedfs_admin_secret field + tls: + caSecretName: "plm-ca-secret" # Secret with ca.crt for verifying the storage service + clientSSLSecretName: "plm-client-secret" # Secret with tls.crt/tls.key for mutual TLS + insecureSkipVerify: false # use only for testing +``` + +{{< call-out "caution" >}} Always use HTTPS with TLS verification (`caSecretName`) in production. Add `clientSSLSecretName` for mutual TLS in high-security environments, and never set `insecureSkipVerify: true`. {{< /call-out >}} + +{{< call-out "note" >}} `credentialsSecretName` and `caSecretName` must reference Secrets in the NGINX Gateway Fabric control plane namespace, unless you prefix them with `/`. {{< /call-out >}} + +Install NGINX Gateway Fabric by following [the installation guide]({{< ref "/ngf/install/helm.md" >}}) and using the **NGINX Plus with WAF** tab, and apply this `values.yaml` file in your install or upgrade command, specifying `--values values.yaml`. + +The PLM installation creates the credentials Secret automatically, containing the S3 secret access key in the `seaweedfs_admin_secret` field (access key ID `admin` by default): + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: plm-storage-credentials + namespace: nginx-gateway +type: Opaque +data: + seaweedfs_admin_secret: +``` + +NGINX Gateway Fabric watches the PLM credentials and TLS Secrets and rebuilds its storage client when they change, so you can rotate credentials without restarting the pod. + +## Deploy the sample application + +Deploy the `customers` and `orders` sample applications. The `customers` app returns a response containing fake sensitive data (credit card number and SSN), which you'll use later to demonstrate data guard masking: + +```yaml +kubectl apply -f - <}} + +{{%tab name="Inline"%}} + +Create an `APPolicy` resource with an inline policy that blocks all attack signatures: + +```yaml +kubectl apply -f - <}} The `ReferenceGrant` lives in the `security` namespace and must be created by whoever manages that namespace — typically your security team, not the platform engineer deploying the Gateway. Coordinate with them if you don't have access. Without a matching `ReferenceGrant`, the `WAFPolicy` is rejected with `ResolvedRefs=False` and reason `RefNotPermitted`. If you put the `APPolicy` and `APLogConf` in the same namespace as the `WAFPolicy`, you can skip the `ReferenceGrant`. See [Troubleshoot WAFPolicy status]({{< ref "/ngf/waf-integration/troubleshooting.md" >}}) for details. {{< /call-out >}} + +{{% /tab %}} + +{{%tab name="Git reference"%}} + +Store your policy JSON in a Git repository and reference it from `APPolicy`. + +#### Public repository + +Create an `APPolicy` resource that references the policy file by path: + +```yaml +apiVersion: appprotect.f5.com/v1 +kind: APPolicy +metadata: + name: + namespace: +spec: + policy: + $ref: + externalReferenceDetails: + repositoryDetails: + repository: https://github.com//.git + ref: "" +``` + +Replace ``, ``, ``, ``, ``, and `` with your values. + +{{< call-out class="note" title="Note" >}} +Pin `ref` to a tag or commit SHA rather than a branch name in production environments. +{{< /call-out >}} + +Apply the resource: + +```shell +kubectl apply -f .yaml +``` + +#### Private repository + +For private repositories, create a Kubernetes secret with your personal access token (PAT): + +```shell +kubectl create secret generic git-token-secret \ + --namespace \ + --from-literal=token= +``` + +Then reference the secret in the `APPolicy` resource: + +```yaml +apiVersion: appprotect.f5.com/v1 +kind: APPolicy +metadata: + name: + namespace: +spec: + policy: + $ref: + externalReferenceDetails: + repositoryDetails: + repository: https://github.com//.git + ref: "" + authentication: + token: git-token-secret +``` + +#### Confirm the policy is ready + +Check `bundle.state`: + +```shell +kubectl get appolicy \ + --namespace \ + --output jsonpath='State: {.status.bundle.state}{"\n"}Bundle: {.status.bundle.location}{"\n"}Compiler: {.status.bundle.compilerVersion}{"\n"}' +``` + +#### Update a Git-referenced policy + +The Policy Controller does not poll the Git repository for changes. To pick up changes to the referenced policy file, re-apply the `APPolicy` resource (or update its revision annotation) after you push changes to the repository. + +{{% /tab %}} + +{{%tab name="Precompiled bundle"%}} + +{{< include "waf/plm-define-policy-bundle-method.md" >}} + +{{% /tab %}} + +{{}} + ## Deploy the Gateway and attach WAFPolicy +Create a Gateway. WAF is already enabled globally, so NGINX Gateway Fabric automatically deploys the WAF sidecar containers alongside the NGINX Pod: + +```yaml +kubectl apply -f - <}}) for guidance. + +Verify that the NGINX Pod has all three containers running: + +```shell +kubectl get pods -l app.kubernetes.io/name=gateway-nginx +``` + +Each NGINX Pod should show `3/3` in the `READY` column, indicating the main NGINX container, `waf-enforcer`, and `waf-config-mgr` are all running: + +```text +NAME READY STATUS RESTARTS AGE +gateway-nginx-7f9b8d6c4d-xxxxx 3/3 Running 0 2m +``` + +## Test deployment and policy enforcement + +Confirm the Gateway was assigned an IP address and reports `Programmed=True`: + +```shell +kubectl describe gateways.gateway.networking.k8s.io gateway +``` + +```text +Addresses: + Type: IPAddress + Value: 192.0.2.1 +``` + +Save the public IP address and port of the Gateway into shell variables: + +```text +GW_IP=XXX.YYY.ZZZ.III +GW_PORT= +``` + +**Verify normal traffic flows.** Send a request to the `customers` route — the response contains the fake sensitive data from the `customers` backend: + +{{< call-out "note" >}} If you have a DNS record allocated for `cafe.example.com`, you can send the request directly to that hostname, without needing to resolve. {{< /call-out >}} + +```shell +curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/customers +``` + +```text +Customer List: + +Name: John Doe +Credit Card: 4111-1111-1111-1111 +SSN: 123-45-6789 +``` + +The sensitive data passes through because the gateway-level `attack-signatures` policy only inspects inbound requests for attack patterns — it doesn't mask outbound response data. + +**Verify attacks are blocked.** Send a request with a cross-site scripting (XSS) payload: + +```shell +curl --resolve cafe.example.com:$GW_PORT:$GW_IP "http://cafe.example.com:$GW_PORT/customers?x=" +``` + +The WAF detects the attack signature and rejects the request: + +```text + +Request Rejected +... +``` + +**Verify the `orders` route is also protected.** Since the policy targets the Gateway, all attached routes inherit protection: + +```shell +curl --resolve cafe.example.com:$GW_PORT:$GW_IP "http://cafe.example.com:$GW_PORT/orders?x=" +``` + +```text + +Request Rejected +... +``` + +{{< call-out "note" >}} The exact blocking response depends on your WAF policy configuration. Check the security log for a corresponding blocked event using `kubectl logs -c waf-enforcer`. {{< /call-out >}} + +## Apply a route-level override (optional) + +In the previous step, you saw that the `customers` route returns sensitive data (credit card numbers and SSNs) in the response body. The gateway-level policy blocks inbound attacks, but doesn't inspect outbound responses. + +This pattern is a good example of a SecOps and app team collaboration: the security team defines a stricter policy for a specific service, and the platform engineer or app developer attaches it as a route-level override. The override applies only to the `customers` route — other routes continue using the gateway-level policy. + +To protect sensitive data in responses, define a **data guard** `APPolicy` and apply it as a route-level override on the `customers` route: + +```yaml +kubectl apply -f - <}}) for architecture and policy lifecycle concepts. +- [Configure policy sources]({{< ref "/ngf/waf-integration/policy-sources.md" >}}) for the other policy source types. +- [Configure WAF settings]({{< ref "/ngf/waf-integration/configuration.md" >}}) for TLS, authentication, fail-open behavior, and WAF container settings. +- [Troubleshoot WAFPolicy status]({{< ref "/ngf/waf-integration/troubleshooting.md" >}}) if a condition is `False`. diff --git a/content/ngf/waf-integration/overview.md b/content/ngf/waf-integration/overview.md index e4ab13502..b825330f6 100644 --- a/content/ngf/waf-integration/overview.md +++ b/content/ngf/waf-integration/overview.md @@ -7,7 +7,7 @@ f5-product: NGINX Gateway Fabric f5-description: Architecture, setup, and concepts for F5 WAF for NGINX integration in NGINX Gateway Fabric. --- -F5 NGINX Gateway Fabric integrates with F5 WAF for NGINX to provide enterprise-grade web application firewall protection. WAF policies are compiled externally and deployed to the data plane via the `WAFPolicy` custom resource. +F5 NGINX Gateway Fabric integrates with F5 WAF for NGINX to provide web application firewall protection. WAF policies are compiled externally and deployed to the data plane using the `WAFPolicy` custom resource. {{< call-out class="note" >}} F5 WAF for NGINX requires NGINX Plus and a separate F5 WAF for NGINX subscription. Contact your F5 sales representative for licensing details. {{< /call-out >}} @@ -42,22 +42,22 @@ WAF is enabled by setting `waf.enable: true` on an `NginxProxy` resource. This i You can enable WAF at two levels: -- **All Gateways** — Set WAF on the GatewayClass-level `NginxProxy` so that every Gateway managed by this NGINX Gateway Fabric instance gets WAF sidecars by default. A per-Gateway `NginxProxy` can override this (for example, to disable WAF on a specific Gateway). -- **Per Gateway** — Create an `NginxProxy` and reference it from a Gateway's `spec.infrastructure.parametersRef`. Only that Gateway gets WAF sidecars. +- **All Gateways** -- Set WAF on the GatewayClass-level `NginxProxy` so that every Gateway managed by this NGINX Gateway Fabric instance gets WAF sidecars by default. A per-Gateway `NginxProxy` can override this (for example, to disable WAF on a specific Gateway). +- **Per Gateway** -- Create an `NginxProxy` and reference it from the `spec.infrastructure.parametersRef` field on a Gateway. Only that Gateway gets WAF sidecars. For details on how GatewayClass and Gateway-level NginxProxy settings are merged, see [Data plane configuration]({{< ref "/ngf/how-to/data-plane-configuration.md" >}}). ### Enable WAF for all Gateways -To enable WAF at install time use the **NGINX Plus with WAF** tab in the [Helm install guide]({{< ref "/ngf/install/helm.md" >}}). This sets the WAF-enabled NGINX Plus image (`nginx-plus-f5waf`) and enables WAF on the GatewayClass-level `NginxProxy`, so every Gateway gets WAF sidecars by default. +To enable WAF at install time, use the **NGINX Plus with WAF** tab in the [Helm install guide]({{< ref "/ngf/install/helm.md" >}}). This sets the WAF-enabled NGINX Plus image (`nginx-plus-f5waf`) and enables WAF on the GatewayClass-level `NginxProxy`, so every Gateway gets WAF sidecars by default. To disable WAF for a specific Gateway, create a per-Gateway `NginxProxy` with `waf.enable: false` and reference it from that Gateway. -{{< call-out class="note" >}} For additional WAF-related NginxProxy settings — including `disableCookieSeed`, `bundleFailOpen`, and custom WAF container images — see [Configure WAF settings]({{< ref "/ngf/waf-integration/configuration.md" >}}). {{< /call-out >}} +{{< call-out class="note" >}} For additional WAF-related NginxProxy settings (including `disableCookieSeed`, `bundleFailOpen`, and custom WAF container images), see [Configure WAF settings]({{< ref "/ngf/waf-integration/configuration.md" >}}). {{< /call-out >}} ### Enable WAF per Gateway -If you installed with the standard NGINX Plus image and want WAF on a specific Gateway only, create a per-Gateway `NginxProxy`. You must also set the NGINX image to `nginx-plus-f5waf`, since the standard `nginx-plus` image inherited from the GatewayClass does not include the WAF module: +If you installed with the standard NGINX Plus image and want WAF on a specific Gateway only, create a per-Gateway `NginxProxy`. You must also set the NGINX image to `nginx-plus-f5waf`, because the standard `nginx-plus` image inherited from the GatewayClass doesn't include the WAF module: ```yaml apiVersion: gateway.nginx.org/v1alpha2 @@ -100,24 +100,57 @@ For the full list of available images, see [Supported container images]({{< ref ### Bundles -A WAF bundle is a compiled policy package produced by the [F5 WAF for NGINX compiler]({{< ref "/waf/configure/compiler.md" >}}). It contains the security policy, optional logging profile, [attack signatures]({{< ref "/waf/policies/attack-signatures.md" >}}), [threat campaign]({{< ref "/waf/policies/threat-campaigns.md" >}}) data, [bot signatures]({{< ref "/waf/policies/bot-signatures.md" >}}), and related metadata in a format that the WAF engine can load and enforce at runtime. Pre-compiling policies into bundles enables faster, more reliable WAF startup — policies are resolved and validated at build time rather than on the running data plane. +A WAF bundle is a compiled policy package produced by the [F5 WAF for NGINX compiler]({{< ref "/waf/configure/compiler.md" >}}). It contains the security policy, optional logging profile, [attack signatures]({{< ref "/waf/policies/attack-signatures.md" >}}), [threat campaign]({{< ref "/waf/policies/threat-campaigns.md" >}}) data, [bot signatures]({{< ref "/waf/policies/bot-signatures.md" >}}), and related metadata. The format lets the WAF engine load and enforce the policy at runtime. Pre-compiling policies into bundles results in faster, more reliable WAF startup: policies are resolved and validated at build time rather than on the running data plane. ### Compilation -WAF policies must be compiled before they can be applied. Compilation takes a JSON policy definition (and optionally [global settings]({{< ref "/waf/configure/compiler.md" >}}) such as a cookie seed and [user-defined signatures]({{< ref "/waf/policies/user-signatures.md" >}})) and produces a `.tgz` bundle. NGINX Gateway Fabric does not compile policies — its role begins at fetching a compiled bundle and deploying it to the data plane. +WAF policies must be compiled before they can be applied. Compilation takes a JSON policy definition (and optionally [global settings]({{< ref "/waf/configure/compiler.md" >}}) such as a cookie seed and [user-defined signatures]({{< ref "/waf/policies/user-signatures.md" >}})) and produces a `.tgz` bundle. NGINX Gateway Fabric doesn't compile policies. Its role begins with fetching a compiled bundle and deploying it to the data plane. ### Source types -The following policy source types are supported, selected via the `spec.type` field on the `WAFPolicy` resource: +Set the source type using the `spec.type` field on the `WAFPolicy` resource: -| Type | Description | -|--------|--------------------------------------------------------------------------------------| -| `NIM` | NGINX Instance Manager — fetched by policy name or UID via NGINX Instance Manager API| -| `N1C` | NGINX One Console — fetched by policy name or object ID via NGINX One Console API | -| `HTTP` | Direct HTTP/HTTPS URL to a compiled bundle file | +| Type | Description | +|--------|---------------------------------------------------------------------------------------------------| +| `NIM` | NGINX Instance Manager -- fetched by policy name or UID using the NGINX Instance Manager API | +| `N1C` | NGINX One Console -- fetched by policy name or object ID using the NGINX One Console API | +| `HTTP` | Direct HTTP/HTTPS URL to a compiled bundle file | +| `PLM` | Policy Lifecycle Management -- `APPolicy`/`APLogConf` CRDs, fetched from in-cluster storage | + +The `NIM`, `N1C`, and `HTTP` source types reference an externally compiled bundle through `policySource` (and `logSource` for log profiles). They detect updates by polling. The `PLM` source type is Kubernetes-native and event-driven: it references `APPolicy` and `APLogConf` custom resources through `policyRef` (and `logRef`), and doesn't require polling. See [PLM (Policy Lifecycle Management)](#plm-policy-lifecycle-management) below. For details on configuring each source type, see [Configure policy sources]({{< ref "/ngf/waf-integration/policy-sources.md" >}}). +### PLM (Policy Lifecycle Management) + +Policy Lifecycle Management (PLM) is a Kubernetes-native policy source. Instead of pointing NGINX Gateway Fabric at an externally compiled bundle, you define your WAF security posture as `APPolicy` and `APLogConf` custom resources in the cluster. The PLM controller watches these resources, compiles them automatically, and stores the resulting bundles in in-cluster S3-compatible storage. NGINX Gateway Fabric fetches the bundles from that storage and deploys them to the data plane. + +The following table summarizes how PLM differs from the HTTP, NGINX Instance Manager, and NGINX One Console source types: + +| Aspect | HTTP / NIM / N1C | PLM | +|--------------------|-------------------------------------------------|---------------------------------------------------------| +| Policy definition | Authored externally (file/Git, NIM, or N1C) | Authored in-cluster as `APPolicy`/`APLogConf` CRDs | +| Compilation | External (compiler CLI/CI-CD, NIM, or N1C) | Automatic, by the PLM controller | +| Bundle storage | HTTP server, NIM, or N1C | In-cluster S3-compatible storage | +| `WAFPolicy` fields | `policySource` / `logSource` | `policyRef.apPolicyRef` / `logRef.apLogConfRef` | +| Update detection | Polling (checksum or conditional GET) | Event-driven Kubernetes watch (no polling) | +| Authentication | Per-`WAFPolicy` credentials Secret | Cluster-wide PLM storage credentials, set at install | +| Network access | External egress to the policy source | Fully in-cluster | + +At runtime, the flow is: + +```text +Create APPolicy/APLogConf → PLM compiles and sets status.bundle.state: ready → +NGINX Gateway Fabric detects the ready status via watch → fetches the bundle from +in-cluster storage → deploys to the data plane +``` + +Changes to an `APPolicy` or `APLogConf` spec trigger recompilation and an automatic re-fetch. No polling is required, and no change to the `WAFPolicy` resource is needed. + +When a `WAFPolicy` references an `APPolicy` or `APLogConf` in a different namespace, create a [ReferenceGrant](https://gateway-api.sigs.k8s.io/api-types/referencegrant/) in the target namespace to permit the reference. + +{{< call-out "note" >}} PLM requires the PLM system to be installed in the cluster and PLM storage to be configured on NGINX Gateway Fabric at install time. For a complete walkthrough, see [Get started with F5 WAF for NGINX using PLM]({{< ref "/ngf/waf-integration/get-started-plm.md" >}}). {{< /call-out >}} + --- ## Policy attachment @@ -126,7 +159,7 @@ For details on configuring each source type, see [Configure policy sources]({{< - A **Gateway-level** `WAFPolicy` protects all HTTPRoutes and GRPCRoutes attached to that Gateway automatically. New routes inherit protection without any additional configuration. - A **Route-level** `WAFPolicy` can be applied to a specific HTTPRoute or GRPCRoute to override the Gateway-level policy for that route. -- More specific (route-level) policies take precedence over less specific (gateway-level) policies. The route-level policy completely replaces the gateway-level policy for that route — there is no merging. +- More specific (route-level) policies take precedence over less specific (gateway-level) policies. The route-level policy completely replaces the gateway-level policy for that route. There's no merging. - Only one `WAFPolicy` may target a given resource at a given level. If two policies target the same Gateway or Route, the second is rejected with `Accepted=False` and reason `Conflicted`. ```text @@ -142,6 +175,7 @@ Route-level WAFPolicy → Overrides Gateway-level for that route only ## See also - [Get started with F5 WAF for NGINX]({{< ref "/ngf/waf-integration/get-started-http.md" >}}) +- [Get started with F5 WAF for NGINX using PLM]({{< ref "/ngf/waf-integration/get-started-plm.md" >}}) - [Configure policy sources (NGINX Instance Manager, NGINX One Console, and HTTP)]({{< ref "/ngf/waf-integration/policy-sources.md" >}}) - [Configure WAF settings]({{< ref "/ngf/waf-integration/configuration.md" >}}) - [WAFPolicy and NginxProxy API reference]({{< ref "/ngf/reference/api.md" >}}) diff --git a/content/ngf/waf-integration/policy-sources.md b/content/ngf/waf-integration/policy-sources.md index 75b879626..260cd3050 100644 --- a/content/ngf/waf-integration/policy-sources.md +++ b/content/ngf/waf-integration/policy-sources.md @@ -4,24 +4,24 @@ weight: 300 toc: true f5-content-type: how-to f5-product: NGINX Gateway Fabric -f5-description: Configure WAFPolicy to fetch compiled bundles from F5 NGINX Instance Manager, F5 NGINX One Console, or an HTTP server. +f5-description: Configure WAFPolicy to fetch compiled bundles from F5 NGINX Instance Manager, F5 NGINX One Console, an HTTP server, or Policy Lifecycle Management. --- -NGINX Gateway Fabric supports three policy source types for fetching compiled WAF bundles: F5 NGINX Instance Manager, F5 NGINX One Console, and direct HTTP/HTTPS URLs. For a quick start walkthrough using the HTTP source, see [Get started with F5 WAF for NGINX]({{< ref "/ngf/waf-integration/get-started-http.md" >}}). +NGINX Gateway Fabric supports four policy source types for fetching compiled WAF bundles: F5 NGINX Instance Manager, F5 NGINX One Console, direct HTTP/HTTPS URLs, and Policy Lifecycle Management (PLM). For a quick start walkthrough using the HTTP source, see [Get started with F5 WAF for NGINX]({{< ref "/ngf/waf-integration/get-started-http.md" >}}). For a walkthrough using PLM, see [Get started with F5 WAF for NGINX using PLM]({{< ref "/ngf/waf-integration/get-started-plm.md" >}}). -Before configuring a policy source, ensure that WAF is [enabled on the NginxProxy]({{< ref "/ngf/waf-integration/overview.md#enable-waf-on-the-nginxproxy" >}}) — either per Gateway or globally via Helm values. +Before configuring a policy source, make sure WAF is [enabled on the NginxProxy]({{< ref "/ngf/waf-integration/overview.md#enable-waf-on-the-nginxproxy" >}}), either per Gateway or globally through Helm values. {{< call-out class="tip" >}} By default, NGINX Gateway Fabric retries transient fetch failures up to 3 times with exponential backoff, and each fetch attempt times out after 30 seconds. You can tune these using the `retryAttempts` and `timeout` fields on `policySource` or `logSource`. {{< /call-out >}} --- -## NGINX Instance Manager (NGINX Instance Manager) +## NGINX Instance Manager Use this option when you manage WAF policies through NGINX Instance Manager. For details on creating and compiling policies in NGINX Instance Manager, see [How WAF policy management works]({{< ref "/nim/waf-integration/overview.md" >}}) and [Create a security policy bundle]({{< ref "/nim/waf-integration/policies-and-logs/bundles/create-bundle.md" >}}). **Workflow:** -1. Author and compile a policy in NGINX Instance Manager using the NGINX Instance Manager console or API. Verify that compilation succeeded before proceeding — NGINX Gateway Fabric cannot detect compilation failures in NGINX Instance Manager. +1. Author and compile a policy in NGINX Instance Manager using the NGINX Instance Manager console or API. Verify that compilation succeeded before proceeding. NGINX Gateway Fabric can't detect compilation failures in NGINX Instance Manager. 2. Create a Secret with your NGINX Instance Manager credentials. 3. Create a `WAFPolicy` referencing the compiled policy by name. @@ -98,7 +98,7 @@ Replace `https://nim.example.com` with your NGINX Instance Manager base URL, and ### Apply a route-level override (optional) -To apply a different policy to a specific route — for example, a data-guard policy — create a route-level `WAFPolicy`: +To apply a different policy to a specific route (for example, a data-guard policy), create a route-level `WAFPolicy`: ```yaml kubectl apply -f - <}}). For a complete walkthrough (including PLM storage setup, defining `APPolicy`/`APLogConf` resources, and applying a `WAFPolicy`), see [Get started with F5 WAF for NGINX using PLM]({{< ref "/ngf/waf-integration/get-started-plm.md" >}}). + +--- + ## Management console visibility When using NGINX Instance Manager or NGINX One Console as your policy source, be aware that neither management console currently displays WAF policy deployments to NGINX Gateway Fabric, nor does it show which compiled bundle versions NGINX Gateway Fabric has fetched. -This is by design: NGINX Gateway Fabric pulls compiled bundles from the management plane using a pull model and deploys them directly in Kubernetes using native Kubernetes manifests, rather than through the NGINX Instance Manager or NGINX One Console console. This workflow ensures that policies can be created, compiled, and made available to NGINX Gateway Fabric via API without requiring console-managed deployment flows. +This is by design: NGINX Gateway Fabric pulls compiled bundles from the management plane using a pull model and deploys them directly in Kubernetes using native Kubernetes manifests, rather than through the NGINX Instance Manager or NGINX One Console console. This workflow means policies can be created, compiled, and made available to NGINX Gateway Fabric through an API without requiring console-managed deployment flows. Policy association visibility for NGINX Instance Manager and NGINX One Console will be added in a future release. In the meantime, use `kubectl describe wafpolicy ` to check deployment status. ### Connect NGINX Gateway Fabric to F5 NGINX One Console -Ensure that NGINX Gateway Fabric is configured to connect to NGINX One Console. Follow the guidance at [Connect NGINX Gateway Fabric with Helm]({{< ref "/nginx-one-console/k8s/add-ngf-helm.md" >}}) or [Connect NGINX Gateway Fabric with Manifests]({{< ref "/nginx-one-console/k8s/add-ngf-manifests.md" >}}) before continuing. +Make sure NGINX Gateway Fabric is configured to connect to NGINX One Console. Follow the guidance at [Connect NGINX Gateway Fabric with Helm]({{< ref "/nginx-one-console/k8s/add-ngf-helm.md" >}}) or [Connect NGINX Gateway Fabric with Manifests]({{< ref "/nginx-one-console/k8s/add-ngf-manifests.md" >}}) before continuing. ### Export security logs to F5 NGINX One Console Although the NGINX One Console console does not display which policies are deployed to NGINX Gateway Fabric data planes, you can export WAF security events to the NGINX One Console security dashboard. This gives your security operations team visibility into blocked attacks, violations, and traffic patterns directly in the console. -To enable this, configure a `securityLogs` entry that sends events to the NGINX Agent's built-in OpenTelemetry collector, which forwards them to NGINX One Console. Use a log profile compiled for the NGINX One Console security dashboard: +To set this up, configure a `securityLogs` entry that sends events to the built-in OpenTelemetry collector in NGINX Agent, which forwards them to NGINX One Console. Use a log profile compiled for the NGINX One Console security dashboard: ```yaml kubectl apply -f - <}} The `profileName: "secops_dashboard"` log profile must exist in your NGINX One Console namespace. This profile is required for events to appear correctly in the NGINX One Console security dashboard. {{< /call-out >}} @@ -279,6 +287,8 @@ The `localhost:1514` syslog destination points to the NGINX Agent's OpenTelemetr ## See also - [F5 WAF for NGINX overview]({{< ref "/ngf/waf-integration/overview.md" >}}) +- [Get started with F5 WAF for NGINX]({{< ref "/ngf/waf-integration/get-started-http.md" >}}) +- [Get started with F5 WAF for NGINX using PLM]({{< ref "/ngf/waf-integration/get-started-plm.md" >}}) - [Configure WAF settings]({{< ref "/ngf/waf-integration/configuration.md" >}}) - [Troubleshoot WAFPolicy status]({{< ref "/ngf/waf-integration/troubleshooting.md" >}}) - [WAFPolicy and NginxProxy API reference]({{< ref "/ngf/reference/api.md" >}}) diff --git a/layouts/shortcodes/version-waf-compiler.html b/layouts/shortcodes/version-waf-compiler.html index 0a70cf03c..5d4f567eb 100644 --- a/layouts/shortcodes/version-waf-compiler.html +++ b/layouts/shortcodes/version-waf-compiler.html @@ -1 +1 @@ -5.14.0 +5.14.0 \ No newline at end of file diff --git a/layouts/shortcodes/version-waf-config-mgr.html b/layouts/shortcodes/version-waf-config-mgr.html index 0a70cf03c..5d4f567eb 100644 --- a/layouts/shortcodes/version-waf-config-mgr.html +++ b/layouts/shortcodes/version-waf-config-mgr.html @@ -1 +1 @@ -5.14.0 +5.14.0 \ No newline at end of file diff --git a/layouts/shortcodes/version-waf-enforcer.html b/layouts/shortcodes/version-waf-enforcer.html index 0a70cf03c..5d4f567eb 100644 --- a/layouts/shortcodes/version-waf-enforcer.html +++ b/layouts/shortcodes/version-waf-enforcer.html @@ -1 +1 @@ -5.14.0 +5.14.0 \ No newline at end of file diff --git a/layouts/shortcodes/version-waf-ip-intelligence.html b/layouts/shortcodes/version-waf-ip-intelligence.html index 0a70cf03c..5d4f567eb 100644 --- a/layouts/shortcodes/version-waf-ip-intelligence.html +++ b/layouts/shortcodes/version-waf-ip-intelligence.html @@ -1 +1 @@ -5.14.0 +5.14.0 \ No newline at end of file diff --git a/layouts/shortcodes/version-waf-policy-controller.html b/layouts/shortcodes/version-waf-policy-controller.html index 0a70cf03c..5d4f567eb 100644 --- a/layouts/shortcodes/version-waf-policy-controller.html +++ b/layouts/shortcodes/version-waf-policy-controller.html @@ -1 +1 @@ -5.14.0 +5.14.0 \ No newline at end of file diff --git a/layouts/shortcodes/version-waf.html b/layouts/shortcodes/version-waf.html index 0a70cf03c..5d4f567eb 100644 --- a/layouts/shortcodes/version-waf.html +++ b/layouts/shortcodes/version-waf.html @@ -1 +1 @@ -5.14.0 +5.14.0 \ No newline at end of file