From 9bd5a7647c06a02c7b900a2a80b2664c1e07ceb6 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 11:04:53 -0700 Subject: [PATCH 01/21] docs: Add PLM infrastructure and WAF policy sections for NGF tutorial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Satisfies TECHDOCS-5343 (Story 3) and TECHDOCS-5347 (Story 7) for the NGF + F5 WAF for NGINX with PLM tutorial. Story 3 — Deploy PLM infrastructure (shared include): - Add content/includes/waf/plm-deploy-infrastructure.md - Covers PLM architecture (Policy Controller → gRPC → compiler → SeaweedFS/S3), CRD installation, registry pull secret creation, Helm install with plm-values.yaml, and deployment verification - No NGF-specific content; consumable by the NIC tutorial unmodified - Flags open item: registry token credential (TECHDOCS-5342) Story 7 — Define the WAF policy (NGF content + shared include): - Add content/includes/waf/plm-define-policy-bundle-method.md (precompiled-bundle method; product-neutral shared include) - Add inline and Git-reference methods directly to get-started-plm.md as NGF-specific content - Documents correct recompile trigger (spec/annotation change required; controller does not poll Git or Artifactory) - Documents bundle.state values: pending | processing | ready | invalid - Documents isCompiled: false for precompiled bundles Also updates get-started-plm.md with TODO placeholders for all remaining sections (Stories 2, 4, 5, 6, 8, 9, 10, 11). --- .../waf/plm-define-policy-bundle-method.md | 74 ++++++++ .../includes/waf/plm-deploy-infrastructure.md | 150 ++++++++++++++++ .../ngf/waf-integration/get-started-plm.md | 166 +++++++++++++++++- 3 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 content/includes/waf/plm-define-policy-bundle-method.md create mode 100644 content/includes/waf/plm-deploy-infrastructure.md 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..26f630e5d --- /dev/null +++ b/content/includes/waf/plm-define-policy-bundle-method.md @@ -0,0 +1,74 @@ +--- +f5-product: F5 WAF for NGINX +f5-files: +- content/ngf/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: +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 \ + --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:///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..90c3c21f9 --- /dev/null +++ b/content/includes/waf/plm-deploy-infrastructure.md @@ -0,0 +1,150 @@ +--- +f5-product: F5 WAF for NGINX +f5-files: +- content/ngf/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 and NGINX Gateway Fabric are installed using separate Helm charts. The steps in this section install only the F5 WAF for NGINX PLM components and do not affect your existing NGINX Gateway Fabric 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 chosen namespace name, and `` with your F5 WAF for NGINX JWT. + + + +```shell +kubectl create namespace + +kubectl create secret docker-registry regcred \ + --namespace \ + --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 your values. `` is the Helm chart version (for example, `5.14.0`). `` is the container image tag, which typically matches the chart version. + +`` and `` are 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: "" +compiler: + image: + tag: "" +seaweedfsOperatorConfig: + seaweedfs: + image: + tag: "" +seaweedfs-operator: + image: + tag: "" + 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 nginx-stable/f5-waf-policy-controller \ + --version \ + --namespace \ + --values /tmp/plm-values.yaml +``` + +Replace `` with a name for the Helm release (for example, `plm`). + +### 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/-seaweedfs-operator \ + --namespace --timeout=120s + +kubectl wait pods \ + --selector app.kubernetes.io/name=seaweedfs \ + --for=condition=Ready \ + --namespace \ + --timeout=180s +``` + +Wait for the Policy Controller: + +```shell +kubectl rollout status deployment/-f5-waf-policy-controller \ + --namespace --timeout=180s +``` + +Confirm all eight pods are running: + +```shell +kubectl get pods --namespace +``` + +Expected output: + +```text +NAME READY STATUS RESTARTS +-f5-waf-compiler-service-xxxxx 1/1 Running 0 +-f5-waf-policy-controller-xxxxx 1/1 Running 0 +-seaweedfs-operator-xxxxx 1/1 Running 0 +-f5-waf-seaweed-master-0 1/1 Running 0 +-f5-waf-seaweed-filer-0 1/1 Running 0 +-f5-waf-seaweed-volume-0 1/1 Running 0 +-f5-waf-seaweed-volume-1 1/1 Running 0 +-f5-waf-seaweed-volume-2 1/1 Running 0 +``` + +Confirm the four CRDs are present: + +```shell +kubectl get crd | grep appprotect.f5.com +``` diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 8466a6c37..1390cb839 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -14,24 +14,186 @@ f5-summary: > f5-audience: operator --- -Introduction text goes here. + ## Before you begin + + ## Deploy PLM infrastructure +{{< include "waf/plm-deploy-infrastructure.md" >}} + ## Configure NGF to connect to PLM storage + + ## Enable WAF in the NginxProxy resource + + ## Configure security logging (optional) + + ## Define the WAF policy +The `APPolicy` custom resource defines what F5 WAF for NGINX enforces. The `APLogConf` custom resource defines what it logs — see [Configure security logging](#configure-security-logging-optional) for details. The Policy Controller watches both resources and compiles them into bundles stored in the SeaweedFS object store. + +This section covers three ways to define a WAF policy. Choose the method that fits your workflow: + +| Method | When to use | +|--------|-------------| +| [Inline](#inline-method) | You want to define the policy directly in the Kubernetes resource. Good for getting started or for simple policies. | +| [Git reference](#git-reference-method) | Your policy JSON lives in a Git repository and you want the Policy Controller to fetch and compile it. | +| [Precompiled bundle](#precompiled-bundle-method) | Your security team publishes compiled bundles to an artifact registry. The Policy Controller imports the bundle without recompiling. | + +### Inline method + +The inline method embeds the full policy definition in the `APPolicy` resource. The Policy Controller converts and compiles it. + +Create an `APPolicy` resource with the policy embedded under `spec.policy`: + +```yaml +apiVersion: appprotect.f5.com/v1 +kind: APPolicy +metadata: + name: + namespace: +spec: + policy: + name: + template: + name: POLICY_TEMPLATE_NGINX_BASE + applicationLanguage: utf-8 + enforcementMode: blocking +``` + +Replace `` and `` with your values. Extend `spec.policy` with the policy fields you want to enforce. + +Apply the resource: + +```shell +kubectl apply -f .yaml +``` + +#### Confirm the policy is ready + +Check the `bundle.state` field to confirm the Policy Controller compiled the policy: + +```shell +kubectl get appolicy \ + --namespace \ + --output jsonpath='State: {.status.bundle.state}{"\n"}Bundle: {.status.bundle.location}{"\n"}Compiler: {.status.bundle.compilerVersion}{"\n"}' +``` + +When compilation succeeds, the output looks like this: + +```text +State: ready +Bundle: s3:///bundles/.tgz +Compiler: +``` + +`bundle.state` can be one of: + +| State | Meaning | +|-------|---------| +| `pending` | The Policy Controller has not yet processed the resource. | +| `processing` | The Policy Controller is compiling the policy. | +| `ready` | Compilation succeeded. `bundle.location` is populated. | +| `invalid` | Compilation failed. Check the status for error detail. | + +To update an inline policy, edit the `APPolicy` resource and re-apply it. The Policy Controller recompiles the policy when the resource spec changes. + +### Git reference method + +The Git reference method lets you store your policy JSON in a Git repository. The Policy Controller fetches the file and compiles it. + +#### 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` as described in [Confirm the policy is ready](#confirm-the-policy-is-ready). + +#### 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. + +### Precompiled bundle method + +{{< include "waf/plm-define-policy-bundle-method.md" >}} + ## Deploy the Gateway and attach WAFPolicy + + ## Configure HTTPRoutes + + ## Validate policy compilation and application -## Test deployment and policy enforcement \ No newline at end of file + + +## Test deployment and policy enforcement + + \ No newline at end of file From f0536be31bcc4ccb991cbfe7abff3e8c6b459779 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 11:38:39 -0700 Subject: [PATCH 02/21] style: Make PLM includes product-agnostic for NGF/NIC reuse Remove the hard-coded NGINX Gateway Fabric product name from the deploy-infrastructure include. The separate-Helm-charts note now refers to the NGINX data plane generically so both the NGF and NIC tutorials can consume the include unmodified. Also adds the NIC target file to f5-files front matter in both includes. --- content/includes/waf/plm-define-policy-bundle-method.md | 1 + content/includes/waf/plm-deploy-infrastructure.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/content/includes/waf/plm-define-policy-bundle-method.md b/content/includes/waf/plm-define-policy-bundle-method.md index 26f630e5d..1f5e37dd4 100644 --- a/content/includes/waf/plm-define-policy-bundle-method.md +++ b/content/includes/waf/plm-define-policy-bundle-method.md @@ -2,6 +2,7 @@ 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. diff --git a/content/includes/waf/plm-deploy-infrastructure.md b/content/includes/waf/plm-deploy-infrastructure.md index 90c3c21f9..fa04651b4 100644 --- a/content/includes/waf/plm-deploy-infrastructure.md +++ b/content/includes/waf/plm-deploy-infrastructure.md @@ -2,11 +2,12 @@ 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 and NGINX Gateway Fabric are installed using separate Helm charts. The steps in this section install only the F5 WAF for NGINX PLM components and do not affect your existing NGINX Gateway Fabric installation. +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 From 04fd34913ec08c00d980ad1546a8e6838dd3b593 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 11:49:13 -0700 Subject: [PATCH 03/21] style: Remove headings from PLM includes Headings in include files cause TOC and hierarchy issues depending on the level of the section they are embedded in. Replace all ### and #### headings with bold lead-in sentences. --- .../waf/plm-define-policy-bundle-method.md | 8 ++------ .../includes/waf/plm-deploy-infrastructure.md | 16 ++++------------ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/content/includes/waf/plm-define-policy-bundle-method.md b/content/includes/waf/plm-define-policy-bundle-method.md index 1f5e37dd4..43caa352e 100644 --- a/content/includes/waf/plm-define-policy-bundle-method.md +++ b/content/includes/waf/plm-define-policy-bundle-method.md @@ -37,9 +37,7 @@ kubectl apply -f .yaml 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: +**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 \ @@ -66,9 +64,7 @@ isCompiled: false | `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: +**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 index fa04651b4..5c81f676b 100644 --- a/content/includes/waf/plm-deploy-infrastructure.md +++ b/content/includes/waf/plm-deploy-infrastructure.md @@ -9,9 +9,7 @@ The Policy Lifecycle Manager (PLM) backend runs as a Kubernetes operator. It wat 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: +**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 @@ -32,9 +30,7 @@ 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. +**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 chosen namespace name, and `` with your F5 WAF for NGINX JWT. @@ -51,9 +47,7 @@ kubectl create secret docker-registry regcred \ --dry-run=client --output yaml | kubectl apply -f - ``` -### Install the Policy Controller - -Create a values file for the Helm installation. Replace ``, ``, ``, ``, and `` with your values. `` is the Helm chart version (for example, `5.14.0`). `` is the container image tag, which typically matches the chart version. +**Install the Policy Controller.** Create a values file for the Helm installation. Replace ``, ``, ``, ``, and `` with your values. `` is the Helm chart version (for example, `5.14.0`). `` is the container image tag, which typically matches the chart version. `` and `` are the base64-encoded contents of your `nginx-repo.crt` and `nginx-repo.key` files. To encode them, run: @@ -100,9 +94,7 @@ helm upgrade --install nginx-stable/f5-waf-policy-controller \ Replace `` with a name for the Helm release (for example, `plm`). -### 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. +**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: From 5604ab8193123255780876bbbaaeefb190a192ee Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 11:52:48 -0700 Subject: [PATCH 04/21] style: Restore headings in PLM includes at correct levels Match heading levels to the consuming section in each case: - plm-deploy-infrastructure.md: ### (consumed under ##) - plm-define-policy-bundle-method.md: #### (consumed under ###) Ensures TOC entries and visual hierarchy are consistent with the parallel method subsections in get-started-plm.md. --- .../waf/plm-define-policy-bundle-method.md | 8 ++++++-- .../includes/waf/plm-deploy-infrastructure.md | 16 ++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/content/includes/waf/plm-define-policy-bundle-method.md b/content/includes/waf/plm-define-policy-bundle-method.md index 43caa352e..1f5e37dd4 100644 --- a/content/includes/waf/plm-define-policy-bundle-method.md +++ b/content/includes/waf/plm-define-policy-bundle-method.md @@ -37,7 +37,9 @@ kubectl apply -f .yaml 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: +#### 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 \ @@ -64,7 +66,9 @@ isCompiled: false | `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: +#### 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 index 5c81f676b..fa04651b4 100644 --- a/content/includes/waf/plm-deploy-infrastructure.md +++ b/content/includes/waf/plm-deploy-infrastructure.md @@ -9,7 +9,9 @@ The Policy Lifecycle Manager (PLM) backend runs as a Kubernetes operator. It wat 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: +### 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 @@ -30,7 +32,9 @@ 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. +### 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 chosen namespace name, and `` with your F5 WAF for NGINX JWT. @@ -47,7 +51,9 @@ kubectl create secret docker-registry regcred \ --dry-run=client --output yaml | kubectl apply -f - ``` -**Install the Policy Controller.** Create a values file for the Helm installation. Replace ``, ``, ``, ``, and `` with your values. `` is the Helm chart version (for example, `5.14.0`). `` is the container image tag, which typically matches the chart version. +### Install the Policy Controller + +Create a values file for the Helm installation. Replace ``, ``, ``, ``, and `` with your values. `` is the Helm chart version (for example, `5.14.0`). `` is the container image tag, which typically matches the chart version. `` and `` are the base64-encoded contents of your `nginx-repo.crt` and `nginx-repo.key` files. To encode them, run: @@ -94,7 +100,9 @@ helm upgrade --install nginx-stable/f5-waf-policy-controller \ Replace `` with a name for the Helm release (for example, `plm`). -**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. +### 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: From ee9508ad28f3bdac168cefec835ea9396f86e393 Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Mon, 10 Aug 2026 13:49:24 -0600 Subject: [PATCH 05/21] Add NGF PLM docs --- content/ngf/reference/cli-help.md | 5 + .../ngf/waf-integration/get-started-http.md | 1 + .../ngf/waf-integration/get-started-plm.md | 571 ++++++++++++++++-- content/ngf/waf-integration/overview.md | 46 +- content/ngf/waf-integration/policy-sources.md | 12 +- 5 files changed, 572 insertions(+), 63 deletions(-) diff --git a/content/ngf/reference/cli-help.md b/content/ngf/reference/cli-help.md index 6e51e3d5e..f10aee54c 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_ | PLM storage service URL (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 that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway), unless prefixed with `/`. | +| _plm-storage-ca-secret_ | _string_ | The name of the Secret containing the CA certificate (`ca.crt`) for PLM storage TLS server verification. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: 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 to PLM storage. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway), unless prefixed with `/`. | +| _plm-storage-skip-verify_ | _bool_ | Disable TLS certificate verification for PLM storage connections. Use for testing only. | | _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/waf-integration/get-started-http.md b/content/ngf/waf-integration/get-started-http.md index a34bdddce..c82656fe6 100644 --- a/content/ngf/waf-integration/get-started-http.md +++ b/content/ngf/waf-integration/get-started-http.md @@ -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 1390cb839..ae9d9d45b 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -14,11 +14,15 @@ f5-summary: > f5-audience: operator --- - +This guide walks through the complete flow of protecting traffic with F5 WAF for NGINX using Policy Lifecycle Management (PLM): connect NGINX Gateway Fabric to PLM storage, define a WAF policy as Kubernetes custom resources, apply it to a Gateway, and verify 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. ## Deploy PLM infrastructure @@ -26,89 +30,238 @@ f5-audience: operator ## Configure NGF to connect to PLM storage - +NGINX Gateway Fabric fetches compiled bundles from PLM's in-cluster 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 +# values.yaml +nginx: + image: + repository: private-registry.nginx.com/nginx-gateway-fabric/nginx-plus-f5waf + plus: true + config: + waf: + enable: true + imagePullSecret: nginx-plus-registry-secret +nginxGateway: + plmStorage: + url: "https://plm-storage-service.plm-system.svc.cluster.local" + credentialsSecretName: "plm-storage-credentials" # 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. + +--- -## Enable WAF in the NginxProxy resource +## 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 - < +PLM security logging profiles are defined as `APLogConf` custom resources. Create a namespace to hold your security resources, then define a log profile that logs illegal requests: -## Define the WAF policy +```shell +kubectl create namespace security +``` -The `APPolicy` custom resource defines what F5 WAF for NGINX enforces. The `APLogConf` custom resource defines what it logs — see [Configure security logging](#configure-security-logging-optional) for details. The Policy Controller watches both resources and compiles them into bundles stored in the SeaweedFS object store. +```yaml +kubectl apply -f - <}} + +{{%tab name="Inline"%}} + +Create an `APPolicy` resource with an inline policy that blocks all attack signatures: ```yaml +kubectl apply -f - < - namespace: + name: attack-signatures + namespace: security spec: policy: - name: + name: attack-signatures-blocking template: name: POLICY_TEMPLATE_NGINX_BASE applicationLanguage: utf-8 enforcementMode: blocking + signature-sets: + - name: All Signatures + block: true + alarm: true + cookies: + - name: "*" + attackSignaturesCheck: true + enforcementType: enforce + maskValueInLogs: false +EOF ``` -Replace `` and `` with your values. Extend `spec.policy` with the policy fields you want to enforce. - -Apply the resource: +Wait for the bundle to become ready: ```shell -kubectl apply -f .yaml +kubectl wait --for=jsonpath='{.status.bundle.state}'=ready appolicy/attack-signatures -n security --timeout=60s ``` -#### Confirm the policy is ready - -Check the `bundle.state` field to confirm the Policy Controller compiled the policy: +Because the `APPolicy` and `APLogConf` live in the `security` namespace but the `WAFPolicy` you create next targets a Gateway in the `default` namespace, create a `ReferenceGrant` in the `security` namespace to permit the cross-namespace reference: -```shell -kubectl get appolicy \ - --namespace \ - --output jsonpath='State: {.status.bundle.state}{"\n"}Bundle: {.status.bundle.location}{"\n"}Compiler: {.status.bundle.compilerVersion}{"\n"}' -``` - -When compilation succeeds, the output looks like this: - -```text -State: ready -Bundle: s3:///bundles/.tgz -Compiler: +```yaml +kubectl apply -f - <}} 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 >}} -To update an inline policy, edit the `APPolicy` resource and re-apply it. The Policy Controller recompiles the policy when the resource spec changes. +{{% /tab %}} -### Git reference method +{{%tab name="Git reference"%}} -The Git reference method lets you store your policy JSON in a Git repository. The Policy Controller fetches the file and compiles it. +Store your policy JSON in a Git repository and reference it from `APPolicy`. #### Public repository @@ -172,28 +325,334 @@ spec: #### Confirm the policy is ready -Check `bundle.state` as described in [Confirm the policy is ready](#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. -### Precompiled bundle method +{{% /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 - < +Create two HTTPRoutes — `customers` and `orders` — attached to the Gateway. Because the `WAFPolicy` targets the Gateway, both routes inherit WAF protection automatically: + +```yaml +kubectl apply -f - < +Confirm the `APPolicy` and `APLogConf` bundles compiled successfully: + +```shell +kubectl get appolicy attack-signatures -n security -o jsonpath='{.status.bundle.state}{"\n"}' +kubectl get aplogconf log-illegal -n security -o jsonpath='{.status.bundle.state}{"\n"}' +``` + +Both commands should print `ready`. + +Verify the `WAFPolicy` has been accepted and programmed: + +```shell +kubectl describe wafpolicy gateway-base-protection +``` + +Look for three conditions in the output: + +```text +Status: + Conditions: + Message: The Policy is accepted + Observed Generation: 1 + Reason: Accepted + Status: True + Type: Accepted + Message: All references are resolved + Observed Generation: 1 + Reason: ResolvedRefs + Status: True + Type: ResolvedRefs + Message: Policy is programmed in the data plane + Observed Generation: 1 + Reason: Programmed + Status: True + Type: Programmed +``` + +If any condition is `False`, the message field describes the problem. See [Troubleshoot WAFPolicy status]({{< ref "/ngf/waf-integration/troubleshooting.md" >}}) 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 - \ No newline at end of file +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: 10.96.20.187 +``` + +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. + +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..432bac8e6 100644 --- a/content/ngf/waf-integration/overview.md +++ b/content/ngf/waf-integration/overview.md @@ -108,16 +108,49 @@ WAF policies must be compiled before they can be applied. Compilation takes a JS ### 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 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 | +| `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 required, and no change to the `WAFPolicy` resource. + +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 access to be set up 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 @@ -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..9eec47c0d 100644 --- a/content/ngf/waf-integration/policy-sources.md +++ b/content/ngf/waf-integration/policy-sources.md @@ -7,7 +7,7 @@ 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. --- -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. @@ -216,6 +216,14 @@ For production environments, you would typically host compiled bundles on an HTT --- +## Policy Lifecycle Management (PLM) + +Use this option when you manage WAF policies as Kubernetes resources with Policy Lifecycle Management (PLM). With PLM, you define your security posture as `APPolicy` and `APLogConf` custom resources instead of `policySource`/`logSource`; the PLM controller compiles them automatically and stores the resulting bundles in in-cluster storage. NGINX Gateway Fabric fetches those bundles and deploys them to the data plane. Because PLM is event-driven, no per-`WAFPolicy` credentials Secret or polling configuration is needed. + +For a comparison of PLM with the other source types, see [PLM (Policy Lifecycle Management)]({{< ref "/ngf/waf-integration/overview.md#plm-policy-lifecycle-management" >}}). 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. @@ -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" >}}) From 51b563fb4c597b3543d7241ef2804c597a0da62a Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 13:11:12 -0700 Subject: [PATCH 06/21] fix: Use concrete example values in PLM infrastructure include Address PR feedback (sjberman): avoid placeholder values where possible so commands are copy-pasteable. - Namespace: plm-system (matches plm-storage-service.plm-system URL already used in the NGF guide) - Release name: plm - Version: {{< version-waf-policy-controller >}} shortcode (already exists at 5.14.0; auto-updates with future releases) Sensitive values retained as placeholders: , , . --- .../includes/waf/plm-deploy-infrastructure.md | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/content/includes/waf/plm-deploy-infrastructure.md b/content/includes/waf/plm-deploy-infrastructure.md index fa04651b4..692009dc0 100644 --- a/content/includes/waf/plm-deploy-infrastructure.md +++ b/content/includes/waf/plm-deploy-infrastructure.md @@ -34,17 +34,15 @@ 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 chosen namespace name, and `` with your F5 WAF for NGINX JWT. +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 +kubectl create namespace plm-system kubectl create secret docker-registry regcred \ - --namespace \ + --namespace plm-system \ --docker-server=private-registry.nginx.com \ --docker-username= \ --docker-password=none \ @@ -53,9 +51,7 @@ kubectl create secret docker-registry regcred \ ### Install the Policy Controller -Create a values file for the Helm installation. Replace ``, ``, ``, ``, and `` with your values. `` is the Helm chart version (for example, `5.14.0`). `` is the container image tag, which typically matches the chart version. - -`` and `` are the base64-encoded contents of your `nginx-repo.crt` and `nginx-repo.key` files. To encode them, run: +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 @@ -72,17 +68,17 @@ securityUpdatesRepo: key: "" policyController: image: - tag: "" + tag: "{{< version-waf-policy-controller >}}" compiler: image: - tag: "" + tag: "{{< version-waf-policy-controller >}}" seaweedfsOperatorConfig: seaweedfs: image: - tag: "" + tag: "{{< version-waf-policy-controller >}}" seaweedfs-operator: image: - tag: "" + tag: "{{< version-waf-policy-controller >}}" pullSecrets: regcred ``` @@ -92,14 +88,12 @@ Add the NGINX Helm repository and install the chart: helm repo add nginx-stable https://helm.nginx.com/stable helm repo update nginx-stable -helm upgrade --install nginx-stable/f5-waf-policy-controller \ - --version \ - --namespace \ +helm upgrade --install plm nginx-stable/f5-waf-policy-controller \ + --version {{< version-waf-policy-controller >}} \ + --namespace plm-system \ --values /tmp/plm-values.yaml ``` -Replace `` with a name for the Helm release (for example, `plm`). - ### 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. @@ -107,41 +101,41 @@ Wait for all PLM components to become ready. The Policy Controller's init contai Wait for the SeaweedFS storage backend: ```shell -kubectl rollout status deployment/-seaweedfs-operator \ - --namespace --timeout=120s +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 \ + --namespace plm-system \ --timeout=180s ``` Wait for the Policy Controller: ```shell -kubectl rollout status deployment/-f5-waf-policy-controller \ - --namespace --timeout=180s +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 +kubectl get pods --namespace plm-system ``` Expected output: ```text NAME READY STATUS RESTARTS --f5-waf-compiler-service-xxxxx 1/1 Running 0 --f5-waf-policy-controller-xxxxx 1/1 Running 0 --seaweedfs-operator-xxxxx 1/1 Running 0 --f5-waf-seaweed-master-0 1/1 Running 0 --f5-waf-seaweed-filer-0 1/1 Running 0 --f5-waf-seaweed-volume-0 1/1 Running 0 --f5-waf-seaweed-volume-1 1/1 Running 0 --f5-waf-seaweed-volume-2 1/1 Running 0 +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: @@ -149,3 +143,5 @@ 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. From 10c6283bbdfa1ffc776c43ef103fc4df60d91400 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 13:36:40 -0700 Subject: [PATCH 07/21] style: Copy edit NGF WAF integration docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply F5 style guide rules across four files: - get-started-plm.md - overview.md - policy-sources.md - cli-help.md (lines 47-51, PLM storage flags) Changes applied: - em-dash: replace all em dashes with commas, periods, colons, or parentheses; use -- only where unavoidable - latin-abbreviations: via → through/using; since (causal) → because - ensure-vs-make-sure: ensure → make sure - contractions: does not/cannot → doesn't/can't per Modern Voice - possessives: PLM's in-cluster storage → in-cluster PLM storage - word-list: ensures that → means; enables → results in - placeholders: / → / per formatting rules - sensitive-information: non-RFC-5737 IP replaced with 192.0.2.1 - f5-product-names: PLM storage flag descriptions spell out Policy Lifecycle Manager on first mention - Removed redundant heading: NGINX Instance Manager (NGINX Instance Manager) → NGINX Instance Manager - Updated f5-description in policy-sources.md to include PLM --- content/ngf/reference/cli-help.md | 10 +++---- .../ngf/waf-integration/get-started-plm.md | 16 ++++++------ content/ngf/waf-integration/overview.md | 26 +++++++++---------- content/ngf/waf-integration/policy-sources.md | 20 +++++++------- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/content/ngf/reference/cli-help.md b/content/ngf/reference/cli-help.md index f10aee54c..d0a575b21 100644 --- a/content/ngf/reference/cli-help.md +++ b/content/ngf/reference/cli-help.md @@ -44,11 +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_ | PLM storage service URL (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 that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway), unless prefixed with `/`. | -| _plm-storage-ca-secret_ | _string_ | The name of the Secret containing the CA certificate (`ca.crt`) for PLM storage TLS server verification. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: 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 to PLM storage. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway), unless prefixed with `/`. | -| _plm-storage-skip-verify_ | _bool_ | Disable TLS certificate verification for PLM storage connections. Use for testing only. | +| _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/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index ae9d9d45b..edeb777b3 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -30,7 +30,7 @@ PLM is one of four WAF policy source types. With PLM, you define your security p ## Configure NGF to connect to PLM storage -NGINX Gateway Fabric fetches compiled bundles from PLM's in-cluster storage. You set up storage access once, cluster-wide, at install time — it applies to every `WAFPolicy` that uses `type: PLM`. +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`: @@ -56,7 +56,7 @@ nginxGateway: {{< 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 >}} +{{< 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`. @@ -335,7 +335,7 @@ kubectl get appolicy \ #### 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. +The Policy Controller doesn't 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 %}} @@ -397,13 +397,13 @@ spec: EOF ``` -This `WAFPolicy` protects every route attached to the Gateway. Later changes to the `APPolicy` or `APLogConf` spec trigger recompilation and an automatic re-fetch — no change to the `WAFPolicy` is required. +This `WAFPolicy` protects every route attached to the Gateway. Later changes to the `APPolicy` or `APLogConf` spec trigger recompilation and an automatic re-fetch. No change to the `WAFPolicy` is required. --- ## Configure HTTPRoutes -Create two HTTPRoutes — `customers` and `orders` — attached to the Gateway. Because the `WAFPolicy` targets the Gateway, both routes inherit WAF protection automatically: +Create two HTTPRoutes (`customers` and `orders`) attached to the Gateway. Because the `WAFPolicy` targets the Gateway, both routes inherit WAF protection automatically: ```yaml kubectl apply -f - <" diff --git a/content/ngf/waf-integration/overview.md b/content/ngf/waf-integration/overview.md index 432bac8e6..5ea6ff0f2 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 a Gateway's `spec.infrastructure.parametersRef`. 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,11 +100,11 @@ 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 in a format that the WAF engine can load and enforce 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 at fetching a compiled bundle and deploying it to the data plane. ### Source types @@ -112,10 +112,10 @@ 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 | +| `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 | +| `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. @@ -145,7 +145,7 @@ NGINX Gateway Fabric detects the ready status via watch → fetches the bundle f in-cluster storage → deploys to the data plane ``` -Changes to an `APPolicy` or `APLogConf` spec trigger recompilation and an automatic re-fetch — no polling required, and no change to the `WAFPolicy` resource. +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. @@ -159,7 +159,7 @@ When a `WAFPolicy` references an `APPolicy` or `APLogConf` in a different namesp - 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 diff --git a/content/ngf/waf-integration/policy-sources.md b/content/ngf/waf-integration/policy-sources.md index 9eec47c0d..2b14363b5 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 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" >}}). +For a comparison of PLM with the other source types, see [PLM (Policy Lifecycle Management)]({{< ref "/ngf/waf-integration/overview.md#plm-policy-lifecycle-management" >}}). 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" >}}). --- @@ -228,19 +228,19 @@ For a comparison of PLM with the other source types, see [PLM (Policy Lifecycle 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 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: ```yaml kubectl apply -f - < Date: Mon, 10 Aug 2026 14:00:48 -0700 Subject: [PATCH 08/21] style: Second copy edit pass on NGF WAF integration docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - possessives: fix PLM's, NGINX Agent's (product name possessives recasted using 'of' or restructured) - em-dash: fix reverted em dash in get-started-plm.md - sentence-length: split long Bundles sentence in overview.md - grammar: 'begins at fetching' → 'begins with fetching' - active-voice: recast passive 'to be set up' → 'to be configured' in overview.md call-out - possessives: Gateway's parametersRef → parametersRef field on a Gateway (inanimate object restructured) --- content/ngf/waf-integration/get-started-plm.md | 12 ++++++------ content/ngf/waf-integration/overview.md | 8 ++++---- content/ngf/waf-integration/policy-sources.md | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index edeb777b3..1b97c044a 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -335,7 +335,7 @@ kubectl get appolicy \ #### Update a Git-referenced policy -The Policy Controller doesn't 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. +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 %}} @@ -397,13 +397,13 @@ spec: EOF ``` -This `WAFPolicy` protects every route attached to the Gateway. Later changes to the `APPolicy` or `APLogConf` spec trigger recompilation and an automatic re-fetch. No change to the `WAFPolicy` is required. +This `WAFPolicy` protects every route attached to the Gateway. Later changes to the `APPolicy` or `APLogConf` spec trigger recompilation and an automatic re-fetch — no change to the `WAFPolicy` is required. --- ## Configure HTTPRoutes -Create two HTTPRoutes (`customers` and `orders`) attached to the Gateway. Because the `WAFPolicy` targets the Gateway, both routes inherit WAF protection automatically: +Create two HTTPRoutes — `customers` and `orders` — attached to the Gateway. Because the `WAFPolicy` targets the Gateway, both routes inherit WAF protection automatically: ```yaml kubectl apply -f - <" diff --git a/content/ngf/waf-integration/overview.md b/content/ngf/waf-integration/overview.md index 5ea6ff0f2..b825330f6 100644 --- a/content/ngf/waf-integration/overview.md +++ b/content/ngf/waf-integration/overview.md @@ -43,7 +43,7 @@ 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. +- **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" >}}). @@ -100,11 +100,11 @@ 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 results in 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 doesn't 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 @@ -149,7 +149,7 @@ Changes to an `APPolicy` or `APLogConf` spec trigger recompilation and an automa 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 access to be set up 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 >}} +{{< 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 >}} --- diff --git a/content/ngf/waf-integration/policy-sources.md b/content/ngf/waf-integration/policy-sources.md index 2b14363b5..260cd3050 100644 --- a/content/ngf/waf-integration/policy-sources.md +++ b/content/ngf/waf-integration/policy-sources.md @@ -240,7 +240,7 @@ Make sure NGINX Gateway Fabric is configured to connect to NGINX One Console. Fo 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 set this up, 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 >}} From fa66d6e1fc2d0a44e1a30a83f40db80fc9a423dd Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 14:03:00 -0700 Subject: [PATCH 09/21] fix: Replace non-RFC-5737 IP address across NGF docs Replace 10.96.20.187 with 192.0.2.1 (RFC 5737 documentation range) in all six NGF files where it appeared as a Gateway IP example. Files updated: - content/ngf/traffic-security/basic-authentication.md - content/ngf/traffic-security/cors.md - content/ngf/traffic-security/jwt-authentication.md - content/ngf/traffic-security/oidc-authentication.md - content/ngf/waf-integration/get-started-http.md - content/ngf/waf-integration/get-started-plm.md --- content/ngf/traffic-security/basic-authentication.md | 2 +- content/ngf/traffic-security/cors.md | 2 +- content/ngf/traffic-security/jwt-authentication.md | 2 +- content/ngf/traffic-security/oidc-authentication.md | 2 +- content/ngf/waf-integration/get-started-http.md | 2 +- content/ngf/waf-integration/get-started-plm.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) 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 c82656fe6..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: diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 1b97c044a..c156e0ce5 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -516,7 +516,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 of the Gateway into shell variables: From 25c48b1d98c604cc2f5f63c0f197c487efe94ff7 Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Mon, 10 Aug 2026 15:39:59 -0600 Subject: [PATCH 10/21] Fix installation and version files --- content/ngf/waf-integration/get-started-plm.md | 13 ++----------- layouts/shortcodes/version-waf-compiler.html | 2 +- layouts/shortcodes/version-waf-config-mgr.html | 2 +- layouts/shortcodes/version-waf-enforcer.html | 2 +- layouts/shortcodes/version-waf-ip-intelligence.html | 2 +- .../shortcodes/version-waf-policy-controller.html | 2 +- layouts/shortcodes/version-waf.html | 2 +- 7 files changed, 8 insertions(+), 17 deletions(-) diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index c156e0ce5..b7adc22f0 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -35,19 +35,10 @@ NGINX Gateway Fabric fetches compiled bundles from in-cluster PLM storage. You s Create a `values.yaml` file that enables WAF and sets the PLM storage connection details under `nginxGateway.plmStorage`: ```yaml -# values.yaml -nginx: - image: - repository: private-registry.nginx.com/nginx-gateway-fabric/nginx-plus-f5waf - plus: true - config: - waf: - enable: true - imagePullSecret: nginx-plus-registry-secret nginxGateway: plmStorage: - url: "https://plm-storage-service.plm-system.svc.cluster.local" - credentialsSecretName: "plm-storage-credentials" # contains the seaweedfs_admin_secret field + 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 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 From e7055dcd98e159269de05ede11ff7af896187a7a Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 15:51:15 -0700 Subject: [PATCH 11/21] style: Align placeholders in bundle-method include with deploy include Use concrete namespace (plm-system) to match the deploy infrastructure include. Normalize manifest filename and output placeholders to ALL_CAPS_UNDERSCORES per style guide. --- .../includes/waf/plm-define-policy-bundle-method.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/content/includes/waf/plm-define-policy-bundle-method.md b/content/includes/waf/plm-define-policy-bundle-method.md index 1f5e37dd4..f86bd3c63 100644 --- a/content/includes/waf/plm-define-policy-bundle-method.md +++ b/content/includes/waf/plm-define-policy-bundle-method.md @@ -19,18 +19,18 @@ apiVersion: appprotect.f5.com/v1 kind: APPolicy metadata: name: - namespace: + namespace: plm-system spec: policy: $ref: "https:///.tgz" ``` -Replace ``, ``, ``, and `` with your values. +Replace ``, ``, and `` with your values. Apply the resource: ```shell -kubectl apply -f .yaml +kubectl apply -f .yaml ``` {{< call-out class="note" title="Note" >}} @@ -43,7 +43,7 @@ The Policy Controller processes the bundle and updates the `APPolicy` status. Ch ```shell kubectl get appolicy \ - --namespace \ + --namespace plm-system \ --output jsonpath='State: {.status.bundle.state}{"\n"}Bundle: {.status.bundle.location}{"\n"}isCompiled: {.status.processing.isCompiled}{"\n"}' ``` @@ -51,7 +51,7 @@ When the bundle is ready, the output looks like this: ```text State: ready -Bundle: s3:///bundles/_imported_.tgz +Bundle: s3://plm-system/bundles/_imported_.tgz isCompiled: false ``` @@ -71,5 +71,5 @@ isCompiled: false 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 +kubectl apply -f .yaml ``` From 083161c6065e96607cbcc89323b3361ecf170743 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 16:04:49 -0700 Subject: [PATCH 12/21] docs: Add example values table to Before you begin Add a table mapping all concrete example values used throughout the tutorial so readers know what is configurable before running any commands. Covers: plm-system (PLM namespace), plm (Helm release), version-waf-policy-controller shortcode, security (policy namespace), default (Gateway/WAFPolicy namespace), and cafe.example.com (hostname). Removes the redundant note from the deploy infrastructure include -- the Before you begin table covers both includes in one place. --- content/ngf/waf-integration/get-started-plm.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index b7adc22f0..6b8e1fdf9 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -24,6 +24,17 @@ PLM is one of four WAF policy source types. With PLM, you define your security p - 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" >}} From 72ea1f7cd044ad812e45e2f907ed0811baa3ffb2 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 16:14:06 -0700 Subject: [PATCH 13/21] docs: Add workflow diagram to tutorial introduction Add a Mermaid flowchart showing the six-step tutorial workflow so readers can see the end-to-end picture before starting: 1. Deploy PLM infrastructure 2. Connect NGF to PLM storage 3. Define WAF policy (APPolicy/APLogConf) 4. Attach WAFPolicy to Gateway (with PLM compilation step called out) 5. Configure HTTPRoutes 6. Validate and test --- content/ngf/waf-integration/get-started-plm.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 6b8e1fdf9..88aa359a8 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -18,6 +18,15 @@ This guide walks through the complete flow of protecting traffic with F5 WAF for 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" >}}). +```mermaid +flowchart LR + A([1. Deploy PLM\ninfrastructure]) --> B([2. Connect NGF\nto PLM storage]) + B --> C([3. Define WAF policy\nAPPolicy / APLogConf]) + C -->|PLM compiles\nto bundle| D([4. Attach WAFPolicy\nto Gateway]) + D --> E([5. Configure\nHTTPRoutes]) + E --> F([6. Validate &\ntest]) +``` + ## Before you begin - Have `kubectl` access to a Kubernetes cluster. From ea847cb8ccb7bfa72a3e9ee9e4562f3c085356ff Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 16:20:14 -0700 Subject: [PATCH 14/21] fix: Switch Mermaid diagram from flowchart to graph syntax The Hugo theme's bundled Mermaid version does not support the flowchart directive or stadium node shapes. Switch to graph LR with quoted rectangle nodes and HTML line breaks, matching the syntax used in other working diagrams in the repo. --- content/ngf/waf-integration/get-started-plm.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 88aa359a8..4c7698551 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -19,12 +19,12 @@ This guide walks through the complete flow of protecting traffic with F5 WAF for 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" >}}). ```mermaid -flowchart LR - A([1. Deploy PLM\ninfrastructure]) --> B([2. Connect NGF\nto PLM storage]) - B --> C([3. Define WAF policy\nAPPolicy / APLogConf]) - C -->|PLM compiles\nto bundle| D([4. Attach WAFPolicy\nto Gateway]) - D --> E([5. Configure\nHTTPRoutes]) - E --> F([6. Validate &\ntest]) +graph LR + A["1. Deploy PLM
infrastructure"] --> B["2. Connect NGF
to PLM storage"] + B --> C["3. Define WAF policy
APPolicy / APLogConf"] + C -->|"PLM compiles
to bundle"| D["4. Attach WAFPolicy
to Gateway"] + D --> E["5. Configure
HTTPRoutes"] + E --> F["6. Validate &
test"] ``` ## Before you begin From ffe5a3e3d88cf96ed7f8c61f45f7298a48a0b908 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 16:38:54 -0700 Subject: [PATCH 15/21] docs: Simplify tutorial introduction Replace the failed Mermaid diagram and redundant numbered list with a concise 'By the end, you will have:' bullet list. Rewrite the opening sentence to lead with the tutorial framing rather than a long colon-separated summary. The left-nav TOC already shows the section structure, so the numbered list added no value. --- content/ngf/waf-integration/get-started-plm.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 4c7698551..8821125d2 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -14,18 +14,15 @@ f5-summary: > f5-audience: operator --- -This guide walks through the complete flow of protecting traffic with F5 WAF for NGINX using Policy Lifecycle Management (PLM): connect NGINX Gateway Fabric to PLM storage, define a WAF policy as Kubernetes custom resources, apply it to a Gateway, and verify that attacks are blocked. +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: -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" >}}). +- 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 -```mermaid -graph LR - A["1. Deploy PLM
infrastructure"] --> B["2. Connect NGF
to PLM storage"] - B --> C["3. Define WAF policy
APPolicy / APLogConf"] - C -->|"PLM compiles
to bundle"| D["4. Attach WAFPolicy
to Gateway"] - D --> E["5. Configure
HTTPRoutes"] - E --> F["6. Validate &
test"] -``` +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 From 3495abfcc2aa5fea215a1812227931c69f17940b Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 10 Aug 2026 17:22:25 -0700 Subject: [PATCH 16/21] docs: Add persona context at role-shift points in PLM tutorial The tutorial spans three personas (cluster admin, SecOps, platform engineer/app dev) but gave no signal when ownership shifted between them. Add plain-prose callouts at four points: - Configure security logging: flag as a security team task - Define the WAF policy: explain the security namespace separation and name the handoff artifact (APPolicy name and namespace) - ReferenceGrant note: clarify that the grant lives in the security namespace and requires coordination with whoever owns it - Apply a route-level override: frame the pattern as a SecOps and app team collaboration rather than a generic optional step Also remove six standalone horizontal rules that preceded section headings, per repo convention. --- .../ngf/waf-integration/get-started-plm.md | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 8821125d2..fda5ca33a 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -83,8 +83,6 @@ data: 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: @@ -164,6 +162,8 @@ EOF ## Configure security logging (optional) +This section is typically owned by your security team. If that's not you, share it with them before continuing. + PLM security logging profiles are defined as `APLogConf` custom resources. Create a namespace to hold your security resources, then define a log profile that logs illegal requests: ```shell @@ -195,10 +195,10 @@ kubectl wait --for=jsonpath='{.status.bundle.state}'=ready aplogconf/log-illegal If you skip this section, omit the `securityLogs` field in the `WAFPolicy` resource in the next steps. ---- - ## Define the WAF policy +This section is typically owned by your security team. They define the policy in the `security` namespace, separate from the Gateway namespace, so that security resources are managed independently from routing configuration. If that's not you, share this section with them — you'll need the `APPolicy` name and namespace before continuing to the next section. + The `APPolicy` resource defines the security policy. The PLM controller watches it, compiles it, and writes `status.bundle` with `state: ready` when the bundle is available. Use the **Inline** tab for this guide's primary workflow. The other tabs provide alternate policy-source methods. @@ -263,7 +263,7 @@ spec: EOF ``` -{{< call-out "note" >}} 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 >}} +{{< call-out "note" >}} 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 %}} @@ -407,8 +407,6 @@ EOF This `WAFPolicy` protects every route attached to the Gateway. Later changes to the `APPolicy` or `APLogConf` spec trigger recompilation and an automatic re-fetch — no change to the `WAFPolicy` is required. ---- - ## Configure HTTPRoutes Create two HTTPRoutes — `customers` and `orders` — attached to the Gateway. Because the `WAFPolicy` targets the Gateway, both routes inherit WAF protection automatically: @@ -455,8 +453,6 @@ spec: EOF ``` ---- - ## Validate policy compilation and application Confirm the `APPolicy` and `APLogConf` bundles compiled successfully: @@ -511,8 +507,6 @@ 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`: @@ -580,12 +574,12 @@ curl --resolve cafe.example.com:$GW_PORT:$GW_IP "http://cafe.example.com:$GW_POR {{< 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 @@ -656,8 +650,6 @@ Credit Card: ***************1111 SSN: *******6789 ``` ---- - ## Next steps - [F5 WAF for NGINX overview]({{< ref "/ngf/waf-integration/overview.md" >}}) for architecture and policy lifecycle concepts. From 6d05bf6003ed56a99f419955cc42b2187adc9008 Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Tue, 11 Aug 2026 09:53:29 -0600 Subject: [PATCH 17/21] Add HTTP option for connecting to PLM storage --- .../ngf/waf-integration/get-started-plm.md | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index fda5ca33a..75639c53d 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -49,7 +49,11 @@ This tutorial uses the following example values. You can use different values 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`: +Create a `values.yaml` file that enables WAF and sets the PLM storage connection details under `nginxGateway.plmStorage`. + +{{}} + +{{%tab name="HTTPS (secure)"%}} ```yaml nginxGateway: @@ -66,6 +70,25 @@ nginxGateway: {{< call-out "note" >}} `credentialsSecretName` and `caSecretName` must reference Secrets in the NGINX Gateway Fabric control plane namespace, unless you prefix them with `/`. {{< /call-out >}} +{{% /tab %}} + +{{%tab name="HTTP"%}} + +```yaml +nginxGateway: + plmStorage: + url: "http://plm-f5-waf-seaweed-filer.plm-system.svc.cluster.local:8333" + credentialsSecretName: "plm-system/plm-f5-waf-seaweedfs-auth" # contains the seaweedfs_admin_secret field +``` + +For an HTTP endpoint, do not include a `tls` section. + +{{< call-out "note" >}} `credentialsSecretName` must reference a Secret in the NGINX Gateway Fabric control plane namespace, unless you prefix it with `/`. {{< /call-out >}} + +{{% /tab %}} + +{{}} + 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): From 2eb517157ec4b9de2532bfe491e42c97f78189a3 Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Tue, 11 Aug 2026 09:54:42 -0600 Subject: [PATCH 18/21] Fix HTTP storage notes --- content/ngf/waf-integration/get-started-plm.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 75639c53d..6f0a5cde5 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -81,10 +81,6 @@ nginxGateway: credentialsSecretName: "plm-system/plm-f5-waf-seaweedfs-auth" # contains the seaweedfs_admin_secret field ``` -For an HTTP endpoint, do not include a `tls` section. - -{{< call-out "note" >}} `credentialsSecretName` must reference a Secret in the NGINX Gateway Fabric control plane namespace, unless you prefix it with `/`. {{< /call-out >}} - {{% /tab %}} {{}} From b9724238ac211a84108c202822f9ddaaf81a93e5 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Tue, 11 Aug 2026 09:11:25 -0700 Subject: [PATCH 19/21] fix: Convert APPolicy apply steps to inline EOF and add verify steps Address reviewer feedback from PR #2197: - Git-reference tab (public and private): replace standalone YAML block and 'kubectl apply -f ' with a single inline 'kubectl apply -f - <`, ``, and `` with your values: -```yaml +```shell +kubectl apply -f - </.tgz" -``` - -Replace ``, ``, and `` with your values. - -Apply the resource: - -```shell -kubectl apply -f .yaml +EOF ``` {{< call-out class="note" title="Note" >}} diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 6f0a5cde5..093bb40c5 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -85,7 +85,7 @@ nginxGateway: {{}} -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`. +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`. For all available `plmStorage` options, see the [NGINX Gateway Fabric Helm chart reference]({{< ref "/ngf/reference/helm.md" >}}). 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): @@ -292,9 +292,10 @@ 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: +Create an `APPolicy` resource that references the policy file by path. Replace ``, ``, ``, ``, ``, and `` with your values: -```yaml +```shell +kubectl apply -f - </.git ref: "" +EOF ``` -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: +Check that the bundle compiled successfully: ```shell -kubectl apply -f .yaml +kubectl get appolicy \ + --namespace \ + --output jsonpath='State: {.status.bundle.state}{"\n"}Bundle: {.status.bundle.location}{"\n"}Compiler: {.status.bundle.compilerVersion}{"\n"}' ``` +The output should show `State: ready`. + #### Private repository For private repositories, create a Kubernetes secret with your personal access token (PAT): @@ -333,7 +337,8 @@ kubectl create secret generic git-token-secret \ Then reference the secret in the `APPolicy` resource: -```yaml +```shell +kubectl apply -f - <" 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"}' +EOF ``` #### Update a Git-referenced policy From 47f770a339b6c6cf12c70a4fb6159780e3d99f5a Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Tue, 11 Aug 2026 09:26:19 -0700 Subject: [PATCH 20/21] fix: Add PLM chart values reference and correct link placement Address @ciarams87 comment on plm-deploy-infrastructure.md: - Add 'helm show values' command after the helm install step so users can discover all available PLM chart configuration options; no public values.yaml URL exists for this chart, so the command is the authoritative substitute - Remove the NGF chart values.yaml link added in error to get-started-plm.md (wrong chart, wrong file) --- content/includes/waf/plm-deploy-infrastructure.md | 6 ++++++ content/ngf/waf-integration/get-started-plm.md | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/content/includes/waf/plm-deploy-infrastructure.md b/content/includes/waf/plm-deploy-infrastructure.md index 692009dc0..fd277924f 100644 --- a/content/includes/waf/plm-deploy-infrastructure.md +++ b/content/includes/waf/plm-deploy-infrastructure.md @@ -94,6 +94,12 @@ helm upgrade --install plm nginx-stable/f5-waf-policy-controller \ --values /tmp/plm-values.yaml ``` +To see all available configuration options for the PLM chart, run: + +```shell +helm show values nginx-stable/f5-waf-policy-controller --version {{< version-waf-policy-controller >}} +``` + ### 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. diff --git a/content/ngf/waf-integration/get-started-plm.md b/content/ngf/waf-integration/get-started-plm.md index 093bb40c5..96a4184e3 100644 --- a/content/ngf/waf-integration/get-started-plm.md +++ b/content/ngf/waf-integration/get-started-plm.md @@ -85,7 +85,7 @@ nginxGateway: {{}} -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`. For all available `plmStorage` options, see the [NGINX Gateway Fabric Helm chart reference]({{< ref "/ngf/reference/helm.md" >}}). +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): From 239217b62c5fc214f62626a363667df244a31655 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Tue, 11 Aug 2026 09:45:50 -0700 Subject: [PATCH 21/21] fix: Improve bundle-method include clarity and reuse safety - Add maintainer HTML comment noting the APPolicy CRD prerequisite and directing future reusers to add a parent-document note - Convert the 'Update a precompiled bundle' step to inline EOF pattern, consistent with the initial apply step - Add placeholder explanation ('Replace , , and with your values') before the update code block, following the pattern used elsewhere in the include --- .../waf/plm-define-policy-bundle-method.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/content/includes/waf/plm-define-policy-bundle-method.md b/content/includes/waf/plm-define-policy-bundle-method.md index 70cfc74b3..7627f620d 100644 --- a/content/includes/waf/plm-define-policy-bundle-method.md +++ b/content/includes/waf/plm-define-policy-bundle-method.md @@ -5,6 +5,8 @@ f5-files: - 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: @@ -62,8 +64,17 @@ isCompiled: false #### 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: +The Policy Controller does not poll the artifact registry for changes. To pick up a new version of a bundle, update the `$ref` URL in your `APPolicy` resource (or bump its revision annotation) and re-apply it. Replace ``, ``, and `` with your values: ```shell -kubectl apply -f .yaml +kubectl apply -f - < + namespace: plm-system +spec: + policy: + $ref: "https:///.tgz" +EOF ```