Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
ddbd331
Added "Get started with F5 WAF for NGINX with PLM"
travisamartin Jul 28, 2026
31def9a
Merge remote-tracking branch 'origin' into ngf-waf-plm
travisamartin Aug 10, 2026
9bd5a76
docs: Add PLM infrastructure and WAF policy sections for NGF tutorial
travisamartin Aug 10, 2026
f186b23
Merge remote-tracking branch 'origin' into techdocs-5345-deploy-plm
travisamartin Aug 10, 2026
94950bb
Merge remote-tracking branch 'origin' into ngf-waf-plm
travisamartin Aug 10, 2026
989aad4
Merge branch 'ngf-waf-plm' into techdocs-5345-deploy-plm
travisamartin Aug 10, 2026
f0536be
style: Make PLM includes product-agnostic for NGF/NIC reuse
travisamartin Aug 10, 2026
04fd349
style: Remove headings from PLM includes
travisamartin Aug 10, 2026
5604ab8
style: Restore headings in PLM includes at correct levels
travisamartin Aug 10, 2026
ee9508a
Add NGF PLM docs
sjberman Aug 10, 2026
51b563f
fix: Use concrete example values in PLM infrastructure include
travisamartin Aug 10, 2026
10c6283
style: Copy edit NGF WAF integration docs
travisamartin Aug 10, 2026
1b88338
style: Second copy edit pass on NGF WAF integration docs
travisamartin Aug 10, 2026
fa66d6e
fix: Replace non-RFC-5737 IP address across NGF docs
travisamartin Aug 10, 2026
25c48b1
Fix installation and version files
sjberman Aug 10, 2026
e7055dc
style: Align placeholders in bundle-method include with deploy include
travisamartin Aug 10, 2026
083161c
docs: Add example values table to Before you begin
travisamartin Aug 10, 2026
72ea1f7
docs: Add workflow diagram to tutorial introduction
travisamartin Aug 10, 2026
ea847cb
fix: Switch Mermaid diagram from flowchart to graph syntax
travisamartin Aug 10, 2026
ffe5a3e
docs: Simplify tutorial introduction
travisamartin Aug 10, 2026
3495abf
docs: Add persona context at role-shift points in PLM tutorial
travisamartin Aug 11, 2026
6d05bf6
Add HTTP option for connecting to PLM storage
sjberman Aug 11, 2026
2eb5171
Fix HTTP storage notes
sjberman Aug 11, 2026
b972423
fix: Convert APPolicy apply steps to inline EOF and add verify steps
travisamartin Aug 11, 2026
47f770a
fix: Add PLM chart values reference and correct link placement
travisamartin Aug 11, 2026
239217b
fix: Improve bundle-method include clarity and reuse safety
travisamartin Aug 11, 2026
0539daf
Merge remote-tracking branch 'origin' into nginx-august-releases
travisamartin Aug 11, 2026
e5b60df
Merge branch 'nginx-august-releases' into techdocs-5345-deploy-plm
travisamartin Aug 11, 2026
62a1c01
style edits applied to get-started-plm.md
travisamartin Aug 12, 2026
cd3c38e
Apply suggestions from code review
travisamartin Aug 12, 2026
8a75252
Apply suggestions from code review
travisamartin Aug 12, 2026
567731b
removed TODO from plm-define-policy-bundle-method.md
travisamartin Aug 12, 2026
39d2e9a
Merge branch 'techdocs-5345-deploy-plm' of https://github.com/nginx/d…
travisamartin Aug 12, 2026
992e28b
Merge remote-tracking branch 'origin' into techdocs-5345-deploy-plm
travisamartin Aug 13, 2026
604ad99
fix: Move CRD step to end of PLM infrastructure include
travisamartin Aug 13, 2026
f731e45
fix: Improve PLM registry secret setup and JWT prerequisite
travisamartin Aug 13, 2026
6c58435
fix: Quote heredoc delimiter to prevent $ref expansion
travisamartin Aug 13, 2026
98cbbb3
fix: Add example output to CRD verification and CRD upgrade steps
travisamartin Aug 13, 2026
2545406
fix: Clarify securityUpdatesRepo cert and key are optional
travisamartin Aug 13, 2026
1c61ff8
docs: Add PLM prerequisites include and copy edit tutorial
travisamartin Aug 13, 2026
2309a38
docs: Document user-provided certificates requirement for PLM TLS
travisamartin Aug 14, 2026
6e46c1f
docs: Replace kubectl wait with polling loop for SeaweedFS pods
travisamartin Aug 14, 2026
afdded7
docs: Clarify heading structure in PLM deploy infrastructure include
travisamartin Aug 14, 2026
2af0092
refactor: Extract reusable PLM sections into shared includes
travisamartin Aug 19, 2026
781f798
style: Remove NIC f5-files entries from PLM includes
travisamartin Aug 19, 2026
d591f4d
docs: Add troubleshooting section to PLM infrastructure include
travisamartin Aug 19, 2026
1aa4cb2
style: Copy edit PLM troubleshooting sections
travisamartin Aug 19, 2026
d45ca96
refactor: Consolidate APPolicy method includes into tabbed include
travisamartin Aug 19, 2026
124e57b
style: Wrap troubleshooting scenarios in details expandos
travisamartin Aug 19, 2026
d2cc1d9
fix: Quote heredoc delimiters in Git-reference APPolicy blocks
travisamartin Aug 19, 2026
83708a1
fix: Fix placeholder formatting across PLM docs
travisamartin Aug 19, 2026
35d25f4
fix: Clarify skip-logging note and move it to section top
travisamartin Aug 19, 2026
3ab3d15
fix: cover PLM refs, logging, and compile failures
sjberman Aug 20, 2026
f0cb586
fix: Move security namespace creation to a required step before optio…
travisamartin Aug 20, 2026
8c14eef
style: Copy edit sjberman's additions to get-started-plm and troubles…
travisamartin Aug 20, 2026
d042f31
fix: Correct namespace and update trigger in precompiled-bundle tab
travisamartin Aug 20, 2026
bcc127b
fix: Address Ohad's review comments on PLM TLS and call-out audit
travisamartin Aug 20, 2026
b70b3a4
style: Copy edit plm-define-policy-methods.md (lines 1–213)
travisamartin Aug 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions content/includes/ngf/installation/nginx-plus/download-jwt.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
f5-product: NGINX Gateway Fabric
f5-files:
- content/ngf/install/helm.md
- content/ngf/install/manifests.md
- content/ngf/install/manifests/plus.md
- content/ngf/install/manifests/plus-experimental.md
- content/ngf/install/nginx-plus.md
- content/ngf/waf-integration/get-started-plm.md
---

1. Log in to [MyF5](https://my.f5.com/manage/s/).
2. Go to **My Products & Plans > Subscriptions** to see your active subscriptions.
3. Find your NGINX products or services subscription, and select the **Subscription ID** for details.
2. Go to **My Products & Plans** > **Subscriptions** to see your active subscriptions.
3. Find your NGINX products or services subscription and select the **Subscription ID** for details.
4. Download the **JSON Web Token (JWT)** from the subscription page.

{{< call-out class="note" >}} The Connectivity Stack for Kubernetes JWT does not work with NGINX Plus reporting. A regular NGINX Plus instance JWT must be used. {{< /call-out >}}
{{< call-out class="note" title="Note" >}} The Connectivity Stack for Kubernetes JWT does not work with NGINX Plus reporting. Use a regular NGINX Plus instance JWT. {{< /call-out >}}
34 changes: 34 additions & 0 deletions content/includes/waf/plm-configure-logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
f5-product: F5 WAF for NGINX
f5-files:
- content/ngf/waf-integration/get-started-plm.md
---

<!-- Maintainer note: This include is product-agnostic. It defines an APLogConf resource. The security namespace is created in the parent tutorial before this include is referenced, in a required step that precedes this optional section. The securityLogs field that references this log profile lives on the product-specific policy attachment resource (WAFPolicy for NGF, Policy for NIC) — keep that configuration in the parent tutorial, not here. -->

This section is typically owned by the security team. If you're not on the security team, share this section with them before continuing.

PLM security logging profiles are defined as `APLogConf` custom resources. Define a log profile that logs illegal requests:

```yaml
kubectl apply -f - <<EOF
apiVersion: appprotect.f5.com/v1
kind: APLogConf
metadata:
name: log-illegal
namespace: security
spec:
filter:
request_type: illegal
content:
format: default
max_request_size: any
max_message_size: 15k
EOF
```

PLM compiles the log profile automatically. Wait for `status.bundle.state` to report `ready` before referencing it:

```shell
kubectl wait --for=jsonpath='{.status.bundle.state}'=ready aplogconf/log-illegal -n security --timeout=60s
```
15 changes: 15 additions & 0 deletions content/includes/waf/plm-create-security-namespace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
f5-product: F5 WAF for NGINX
f5-files:
- content/ngf/waf-integration/get-started-plm.md
---

<!-- Maintainer note: This include creates the security namespace used by APPolicy and APLogConf resources. It is product-agnostic and must be included before any include or section that applies resources to the security namespace. In the NGF and NIC tutorials, include this before the optional security logging section. Note: NGF tutorials also create a ReferenceGrant in this namespace, but that is product-specific and not referenced here. -->

This section is typically owned by the security team. If you're not on the security team, share this section with them before continuing.

Create the `security` namespace. The security team's `APPolicy` and `APLogConf` resources live here. In NGINX Gateway Fabric, the `ReferenceGrant` that permits cross-namespace WAFPolicy references also lives in this namespace.

```shell
kubectl create namespace security
```
213 changes: 213 additions & 0 deletions content/includes/waf/plm-define-policy-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
f5-product: F5 WAF for NGINX
f5-files:
- content/ngf/waf-integration/get-started-plm.md
---

<!-- Maintainer note: This include is product-agnostic. It covers all three APPolicy definition methods (inline, Git reference, precompiled bundle) in a tabbed UI. Content ends before any product-specific policy attachment resource (WAFPolicy for NGF, Policy for NIC). The ReferenceGrant required for cross-namespace WAFPolicy refs in NGF is not included here — add it in the parent tutorial after this include. The security namespace is created in plm-configure-logging.md, which runs before this include in the tutorial. -->

The security team usually owns this section. They define the policy in the `security` namespace, separate from the Gateway namespace, so they can manage security resources independently from routing configuration. If you're not on the security team, share this section with them. You'll need the `APPolicy` name and namespace before continuing.

The `APPolicy` resource defines the security policy. The PLM controller watches the resource, compiles the policy, and writes `status.bundle` with `state: ready` when the bundle is available.

{{<tabs name="plm-policy-definition-methods">}}

{{%tab name="Inline"%}}

Create an `APPolicy` resource with an inline policy that blocks all attack signatures:

```yaml
kubectl apply -f - <<EOF
apiVersion: appprotect.f5.com/v1
kind: APPolicy
metadata:
name: attack-signatures
namespace: security
spec:
policy:
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
```

Wait for the bundle to become ready:

```shell
kubectl wait --for=jsonpath='{.status.bundle.state}'=ready appolicy/attack-signatures -n security --timeout=60s
```

{{% /tab %}}

{{%tab name="Git reference"%}}

Store your policy JSON in a Git repository and reference the file from an `APPolicy` resource.

#### Public repository

Create an `APPolicy` resource that references the policy file by path. Replace `<POLICY_NAME>`, `<NAMESPACE>`, `<PATH/TO/POLICY.JSON>`, `<ORG>`, `<REPO>`, and `<TAG_OR_COMMIT>` with your values:

```shell
kubectl apply -f - <<'EOF'
apiVersion: appprotect.f5.com/v1
kind: APPolicy
metadata:
name: <POLICY_NAME>
namespace: <NAMESPACE>
spec:
policy:
$ref: <PATH/TO/POLICY.JSON>
externalReferenceDetails:
repositoryDetails:
repository: https://github.com/<ORG>/<REPO>.git
ref: "<TAG_OR_COMMIT>"
EOF
```

{{< call-out class="caution" title="Pin ref to a tag in production" >}} Pin `ref` to a tag or commit SHA rather than a branch name in production environments. {{< /call-out >}}

Check that the bundle compiled successfully:

```shell
kubectl get appolicy <POLICY_NAME> \
--namespace <NAMESPACE> \
--output jsonpath='State: {.status.bundle.state}{"\n"}Bundle: {.status.bundle.location}{"\n"}Compiler: {.status.bundle.compilerVersion}{"\n"}'
```

The output shows `State: ready` when compilation succeeds.

#### Private repository

For private repositories, create a Kubernetes Secret with your personal access token (PAT):

```shell
kubectl create secret generic git-token-secret \
--namespace <NAMESPACE> \
--from-literal=token=<GIT_PERSONAL_ACCESS_TOKEN>
```

Then reference the Secret in the `APPolicy` resource:

```shell
kubectl apply -f - <<'EOF'
apiVersion: appprotect.f5.com/v1
kind: APPolicy
metadata:
name: <POLICY_NAME>
namespace: <NAMESPACE>
spec:
policy:
$ref: <PATH/TO/POLICY.JSON>
externalReferenceDetails:
repositoryDetails:
repository: https://github.com/<ORG>/<REPO>.git
ref: "<TAG_OR_COMMIT>"
authentication:
token: git-token-secret
EOF
```

#### Update a Git-referenced policy

The Policy Controller doesn't poll the Git repository for changes. It fetches the policy file when the `APPolicy` spec changes.

To pick up a new version of the policy, push your changes to the repository. Then update `ref` in the `APPolicy` resource to the new tag or commit SHA and reapply the resource. Reapplying an unchanged `APPolicy` doesn't trigger a fetch. Changing an annotation doesn't trigger a fetch either.

If you need to re-fetch the same `ref` (for example, after force-updating a tag), delete the `APPolicy` resource and recreate it.

{{% /tab %}}

{{%tab name="Precompiled bundle"%}}

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 the bundle.

Use this method when:

- Your security team compiles and publishes bundles through an external pipeline.
- You want to separate policy compilation from cluster operations.

Create an `APPolicy` resource that references your bundle. Replace `<POLICY_NAME>`, `<ARTIFACT_REGISTRY_HOST>`, and `<PATH/TO/POLICY_BUNDLE>` with your values:

```shell
kubectl apply -f - <<'EOF'
apiVersion: appprotect.f5.com/v1
kind: APPolicy
metadata:
name: <POLICY_NAME>
namespace: security
spec:
policy:
$ref: "https://<ARTIFACT_REGISTRY_HOST>/<PATH/TO/POLICY_BUNDLE>.tgz"
EOF
```

{{< call-out class="important" title="Configure CA trust for private registries" >}}
The Policy Controller must reach the artifact registry over HTTPS. If the registry uses a private certificate authority (CA), mount the CA certificate into the Policy Controller pod and set the `SSL_CERT_FILE` environment variable to its path. `SSL_CERT_FILE` replaces the system trust store entirely. It doesn't append to the system trust store. If SeaweedFS TLS is also turned on, combine both CAs into a single file and reference that file.
{{< /call-out >}}

If the `APPolicy` status shows `x509: certificate signed by unknown authority`, the Policy Controller doesn't trust the artifact registry CA. Check the status for the full error:

```shell
kubectl describe appolicy <POLICY_NAME> --namespace security
```

#### 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 <POLICY_NAME> \
--namespace security \
--output jsonpath='State: {.status.bundle.state}{"\n"}Bundle: {.status.bundle.location}{"\n"}isCompiled: {.status.processing.isCompiled}{"\n"}'
```

When the bundle is ready, the output looks like this:

```text
State: ready
Bundle: s3://plm-system/bundles/<POLICY_NAME>_imported_<HASH>.tgz
isCompiled: false
```

`isCompiled: false` confirms the bundle was imported without recompilation.

`bundle.state` can be one of:

| State | Meaning |
|-------|---------|
| `pending` | The Policy Controller hasn't yet processed the resource. |
| `processing` | The Policy Controller is importing or storing the bundle. |
| `ready` | The bundle is stored and ready to use. The Policy Controller has populated `bundle.location`. |
| `invalid` | The Policy Controller couldn't import the bundle. Check the status for error detail. |

#### Update a precompiled bundle

The Policy Controller doesn't poll the artifact registry for changes. To pick up a new version of a bundle, update the `$ref` URL in your `APPolicy` resource and reapply the resource. Changing an annotation doesn't trigger a new download. If you need to re-fetch the same URL, delete the `APPolicy` resource and recreate it. Replace `<POLICY_NAME>`, `<ARTIFACT_REGISTRY_HOST>`, and `<PATH/TO/UPDATED_POLICY_BUNDLE>` with your values:

```shell
kubectl apply -f - <<'EOF'
apiVersion: appprotect.f5.com/v1
kind: APPolicy
metadata:
name: <POLICY_NAME>
namespace: security
spec:
policy:
$ref: "https://<ARTIFACT_REGISTRY_HOST>/<PATH/TO/UPDATED_POLICY_BUNDLE>.tgz"
EOF
```

{{% /tab %}}

{{</tabs>}}
Loading
Loading