From c52c7ed4f7784fa58b7f5ece7fbde558be0163f7 Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Wed, 25 Feb 2026 13:20:47 +0000 Subject: [PATCH 1/5] Add new section for configuring CORS in NGF (#1735) --- .../traffic-security/basic-authentication.md | 2 +- content/ngf/traffic-security/cors.md | 228 ++++++++++++++++++ 2 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 content/ngf/traffic-security/cors.md diff --git a/content/ngf/traffic-security/basic-authentication.md b/content/ngf/traffic-security/basic-authentication.md index dbb3af97ba..541a2e017a 100644 --- a/content/ngf/traffic-security/basic-authentication.md +++ b/content/ngf/traffic-security/basic-authentication.md @@ -132,7 +132,7 @@ EOF Confirm the Gateway was assigned an IP address and reports a `Programmed=True` status with `kubectl describe`: ```shell -kubectl describe gateways.gateway.networking.k8s.io cafe-gateway | grep "Addresses:" -A2 +kubectl describe gateways.gateway.networking.k8s.io cafe-gateway ``` ```text diff --git a/content/ngf/traffic-security/cors.md b/content/ngf/traffic-security/cors.md new file mode 100644 index 0000000000..bb84c6a6c3 --- /dev/null +++ b/content/ngf/traffic-security/cors.md @@ -0,0 +1,228 @@ +--- +title: Configure Cross-Origin Request Sharing (CORS) +weight: 900 +toc: true +nd-content-type: how-to +nd-product: FABRIC +--- + + +This document describes how to configure the HTTPCORSFilter in F5 NGINX Gateway Fabric to handle Cross-Origin Resource Sharing (CORS) for your applications. + +CORS is a security feature that allows or denies web applications running at one domain to make requests for resources from a different domain. The HTTPCORSFilter in the gateway API provides a standard way to configure CORS policies. + +## Before you begin + +- [Install]({{< ref "/ngf/install/" >}}) NGINX Gateway Fabric. + +## Deploy sample application + +To deploy the `coffee` application, run the following YAML with `kubectl apply`: + +```yaml +kubectl apply -f - < +``` + +## Deploy a HTTPRoute with the HTTPCORSFilter + +In this example, the filter is applied to the `/coffee` path. Run the following command to apply the route: + +```yaml +kubectl apply -f - < +``` + +## Verify CORS pre-flight check + +{{< call-out "note" >}} + +Your clients should be able to resolve the domain name "cafe.example.com" to the public IP of the NGINX Service. + +This guide simulates it using curl's `--resolve` option. + +{{< /call-out >}} + +Send a preflight request using curl: + +```shell +curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -H "Origin: https://example.com" -X OPTIONS -v +``` + +Response: + +```text +> OPTIONS /coffee HTTP/1.1 +> Host: cafe.example.com:8080 +> User-Agent: curl/8.7.1 +> Accept: */* +> Origin: https://example.com +> +* Request completely sent off +< HTTP/1.1 200 OK +< Server: nginx +< Date: Wed, 18 Feb 2026 11:49:23 GMT +< Content-Type: application/octet-stream +< Content-Length: 0 +< Connection: keep-alive +< Access-Control-Allow-Origin: https://example.com +< Access-Control-Allow-Methods: GET, POST +< Access-Control-Allow-Headers: Keep-Alive, Content-Type, User-Agent, Authorization +< Access-Control-Expose-Headers: Content-Security-Policy +< Access-Control-Allow-Credentials: true +< Access-Control-Max-Age: 10 +< +* Connection #0 to host cafe.example.com left intact +``` + +## Further reading + +- [Example deployment files for HTTPCORSFilter](https://github.com/nginx/nginx-gateway-fabric/tree/main/examples/cors-filter) +- [Gateway API Specification](https://gateway-api.sigs.k8s.io/reference/spec/#httpcorsfilter) \ No newline at end of file From 7bbc1b5b2956c3fc3597d7fd452b0275b3c6ee60 Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Mon, 2 Mar 2026 11:22:10 +0000 Subject: [PATCH 2/5] fix: Update NGF CORS filter example to fix allowedOrigins field (#1758) --- content/ngf/traffic-security/cors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/ngf/traffic-security/cors.md b/content/ngf/traffic-security/cors.md index bb84c6a6c3..f3a29f7c93 100644 --- a/content/ngf/traffic-security/cors.md +++ b/content/ngf/traffic-security/cors.md @@ -129,8 +129,8 @@ spec: - type: CORS cors: allowOrigins: - - "https://foobar*.com" - - "https://example.com" + - "https://www.foo.com" + - "https://*.bar.com" allowMethods: - GET - POST From b28560020c2ce22068c3931bcfa7337e98acc1bf Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Wed, 4 Mar 2026 07:29:26 -0700 Subject: [PATCH 3/5] docs: update NGF docs for GW API 1.5 (#1760) For Gateway API v1.5 support, update the NGF docs for version numbers and experimental fields. --- .../includes/ngf/gateway-api-compat-table.md | 4 +- .../how-to/gateway-api-inference-extension.md | 4 +- .../ngf/overview/gateway-api-compatibility.md | 81 ++++++++++++------- .../traffic-management/https-termination.md | 2 +- .../request-response-headers.md | 2 +- content/ngf/traffic-management/tcp-routing.md | 1 + .../ngf/traffic-management/tls-passthrough.md | 22 ++--- content/ngf/traffic-security/cors.md | 3 +- .../ngf/traffic-security/secure-backend.md | 6 -- 9 files changed, 71 insertions(+), 54 deletions(-) diff --git a/content/includes/ngf/gateway-api-compat-table.md b/content/includes/ngf/gateway-api-compat-table.md index d6fcf6d41b..597b84bbde 100644 --- a/content/includes/ngf/gateway-api-compat-table.md +++ b/content/includes/ngf/gateway-api-compat-table.md @@ -16,8 +16,8 @@ You can view the [Milestone Roadmap](https://github.com/orgs/nginx/projects/10/v | [Gateway]({{< ref "/ngf/overview/gateway-api-compatibility.md#gateway" >}}) | Supported | Partially supported | Not supported | v1 | Standard | | [HTTPRoute]({{< ref "/ngf/overview/gateway-api-compatibility.md#httproute" >}}) | Supported | Partially supported | Not supported | v1 | Standard | | [GRPCRoute]({{< ref "/ngf/overview/gateway-api-compatibility.md#grpcroute" >}}) | Supported | Partially supported | Not supported | v1 | Standard | -| [ReferenceGrant]({{< ref "/ngf/overview/gateway-api-compatibility.md#referencegrant" >}}) | Supported | N/A | Not supported | v1beta1 | Standard | -| [TLSRoute]({{< ref "/ngf/overview/gateway-api-compatibility.md#tlsroute" >}}) | Supported | Not supported | Not supported | v1alpha2 | Experimental | +| [ReferenceGrant]({{< ref "/ngf/overview/gateway-api-compatibility.md#referencegrant" >}}) | Supported | N/A | Not supported | v1 | Standard | +| [TLSRoute]({{< ref "/ngf/overview/gateway-api-compatibility.md#tlsroute" >}}) | Supported | Not supported | Not supported | v1 | Standard | | [TCPRoute]({{< ref "/ngf/overview/gateway-api-compatibility.md#tcproute" >}}) | Supported | Supported | Not supported | v1alpha2 | Experimental | | [UDPRoute]({{< ref "/ngf/overview/gateway-api-compatibility.md#udproute" >}}) | Supported | Supported | Not supported | v1alpha2 | Experimental | | [BackendTLSPolicy]({{< ref "/ngf/overview/gateway-api-compatibility.md#backendtlspolicy" >}}) | Partially supported | Supported | Partially supported | v1 | Standard | diff --git a/content/ngf/how-to/gateway-api-inference-extension.md b/content/ngf/how-to/gateway-api-inference-extension.md index 731f0da99a..6881800c1c 100644 --- a/content/ngf/how-to/gateway-api-inference-extension.md +++ b/content/ngf/how-to/gateway-api-inference-extension.md @@ -16,8 +16,6 @@ The project's goal is to improve and standardize routing to inference workloads Coupled with the provided Endpoint Picker Service, NGINX Gateway Fabric becomes an [Inference Gateway](https://gateway-api-inference-extension.sigs.k8s.io/#concepts-and-definitions), with additional AI specific traffic management features such as model-aware routing, serving priority for models, model rollouts, and more. -{{< call-out "warning" >}} The Gateway API Inference Extension is still in alpha status and should not be used in production yet.{{< /call-out >}} - ## Set up Install the Gateway API Inference Extension CRDs: @@ -67,7 +65,7 @@ Install an InferencePool named `vllm-llama3-8b-instruct` that selects from endpo NGINX will query the Endpoint Picker Extension to determine the appropriate pod endpoint to route traffic to. These pods are selected from a pool of ready pods designated by the assigned InferencePool's Selector field. For more information on the [Endpoint Picker](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/pkg/epp/README.md). -{{< call-out "warning" >}} The Endpoint Picker Extension is a third-party application written and provided by the Gateway API Inference Extension project. Communication between NGINX and the Endpoint Picker uses TLS with certificate verification disabled by default, as the Endpoint Picker does not currently support mounting CA certificates. The Gateway API Inference Extension is in alpha status and should not be used in production. NGINX Gateway Fabric is not responsible for any threats or risks associated with using this third-party Endpoint Picker Extension application. {{< /call-out >}} +{{< call-out "warning" >}} The Endpoint Picker Extension is a third-party application written and provided by the Gateway API Inference Extension project. Communication between NGINX and the Endpoint Picker uses TLS with certificate verification disabled by default, as the Endpoint Picker does not currently support mounting CA certificates. NGINX Gateway Fabric is not responsible for any threats or risks associated with using this third-party Endpoint Picker Extension application. {{< /call-out >}} ```shell export IGW_CHART_VERSION=v1.1.0 diff --git a/content/ngf/overview/gateway-api-compatibility.md b/content/ngf/overview/gateway-api-compatibility.md index bed7cbde01..49453251d3 100644 --- a/content/ngf/overview/gateway-api-compatibility.md +++ b/content/ngf/overview/gateway-api-compatibility.md @@ -57,12 +57,14 @@ NGINX Gateway Fabric supports a single GatewayClass resource configured with the - `status` - `conditions` - supported (Condition/Status/Reason): - `Accepted/True/Accepted` - - `Accepted/False/InvalidParameters` + - `Accepted/True/InvalidParameters` - `Accepted/False/UnsupportedVersion` - - `Accepted/False/GatewayClassConflict`: Custom reason for when the GatewayClass references this controller, but - a different GatewayClass name is provided to the controller via the command-line argument. + - `Accepted/False/GatewayClassConflict` - `SupportedVersion/True/SupportedVersion` - `SupportedVersion/False/UnsupportedVersion` + - `ResolvedRefs/True/ResolvedRefs` + - `ResolvedRefs/False/ParametersRefNotFound` + - `ResolvedRefs/False/ParametersRefInvalid` - `supportedFeatures` - supported. ### Gateway @@ -110,12 +112,22 @@ See the [controller]({{< ref "/ngf/reference/cli-help.md#controller">}}) command - `conditions`: Supported (Condition/Status/Reason): - `Accepted/True/Accepted` - `Accepted/True/ListenersNotValid` + - `Accepted/True/InvalidParameters` + - `Accepted/True/UnsupportedField` - `Accepted/False/ListenersNotValid` - `Accepted/False/Invalid` - - `Accepted/False/UnsupportedValue`: Custom reason for when a value of a field in a Gateway is invalid or not supported. + - `Accepted/False/UnsupportedValue` + - `Accepted/False/UnsupportedAddress` - `Programmed/True/Programmed` - `Programmed/False/Invalid` - - `Accepted/True/UnsupportedField`: Custom reason for when the Gateway is accepted but contains an unsupported field + - `Programmed/False/UnsupportedValue` + - `Programmed/False/AddressNotUsable` + - `Programmed/False/AddressNotAssigned` + - `ResolvedRefs/True/ResolvedRefs` + - `ResolvedRefs/False/ParametersRefNotFound` + - `ResolvedRefs/False/ParametersRefInvalid` + - `ResolvedRefs/False/InvalidClientCertificateRef` + - `ResolvedRefs/False/RefNotPermitted` - `listeners` - `name`: Supported. - `supportedKinds`: Supported. @@ -126,7 +138,8 @@ See the [controller]({{< ref "/ngf/reference/cli-help.md#controller">}}) command - `Accepted/False/InvalidCertificateRef` - `Accepted/False/ProtocolConflict` - `Accpeted/False/HostnameConflict` - - `Accepted/False/UnsupportedValue`: Custom reason for when a value of a field in a Listener is invalid or not supported. + - `Accepted/False/UnsupportedValue` + - `Accepted/False/RefNotPermitted` - `Programmed/True/Programmed` - `Programmed/False/Invalid` - `ResolvedRefs/True/ResolvedRefs` @@ -165,7 +178,8 @@ See the [controller]({{< ref "/ngf/reference/cli-help.md#controller">}}) command - `urlRewrite`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `requestRedirect`. - `responseHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. - `requestMirror`: Supported. Multiple mirrors can be specified. Percent and fraction-based mirroring are supported. - - `extensionRef`: Supported for SnippetsFilters. + - `cors`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. + - `extensionRef`: Supported for SnippetsFilters and AuthenticationFilters. - `backendRefs`: Partially supported. Backend ref `filters` are not supported. - `name`: Not supported. - `timeouts`: Not supported. @@ -177,23 +191,27 @@ See the [controller]({{< ref "/ngf/reference/cli-help.md#controller">}}) command - `controllerName`: Supported. - `conditions`: Partially supported. Supported (Condition/Status/Reason): - `Accepted/True/Accepted` + - `Accepted/True/UnsupportedField` - `Accepted/False/NoMatchingListenerHostname` - `Accepted/False/NoMatchingParent` - `Accepted/False/NotAllowedByListeners` - - `Accepted/False/UnsupportedValue`: Custom reason for when the HTTPRoute includes an invalid or unsupported value. - - `Accepted/False/InvalidListener`: Custom reason for when the HTTPRoute references an invalid listener. - - `Accepted/False/GatewayIgnored`: Custom reason for when the Gateway is ignored by NGINX Gateway Fabric. NGINX Gateway Fabric only supports one Gateway. + - `Accepted/False/UnsupportedValue` + - `Accepted/False/InvalidListener` + - `Accepted/False/HostnameConflict` + - `Accepted/False/MultipleRoutesOnListener` + - `Accepted/False/InvalidGateway` - `ResolvedRefs/True/ResolvedRefs` - `ResolvedRefs/False/InvalidKind` - `ResolvedRefs/False/RefNotPermitted` - `ResolvedRefs/False/BackendNotFound` - - `ResolvedRefs/False/UnsupportedValue`: Custom reason for when one of the HTTPRoute rules has a backendRef with an unsupported value. - - `ResolvedRefs/False/InvalidIPFamily`: Custom reason for when one of the HTTPRoute rules has a backendRef that has an invalid IPFamily. + - `ResolvedRefs/False/UnsupportedValue` + - `ResolvedRefs/False/InvalidIPFamily` - `ResolvedRefs/False/UnsupportedProtocol` + - `ResolvedRefs/False/InvalidFilter` + - `ResolvedRefs/False/InvalidInferencePool` - `PartiallyInvalid/True/UnsupportedValue` - - `Accepted/True/UnsupportedField`: Custom reason for when the HTTPRouteRule is accepted but contains an unsupported field - {{< call-out "note" >}} If `name`, `timeouts`, `retry` or `sessionPersistence` are defined for a HTTPRoute rule, they will be ignored and rule still will be created. {{< /call-out >}} + {{< call-out "note" >}} If `name`, `timeouts`, or `retry` are defined for a HTTPRoute rule, they will be ignored and rule still will be created. {{< /call-out >}} ### GRPCRoute @@ -229,20 +247,27 @@ See the [controller]({{< ref "/ngf/reference/cli-help.md#controller">}}) command - `controllerName`: Supported. - `conditions`: Partially supported. Supported (Condition/Status/Reason): - `Accepted/True/Accepted` + - `Accepted/True/UnsupportedField` - `Accepted/False/NoMatchingListenerHostname` - `Accepted/False/NoMatchingParent` - `Accepted/False/NotAllowedByListeners` - - `Accepted/False/UnsupportedValue`: Custom reason for when the GRPCRoute includes an invalid or unsupported value. - - `Accepted/False/InvalidListener`: Custom reason for when the GRPCRoute references an invalid listener. + - `Accepted/False/UnsupportedValue` + - `Accepted/False/InvalidListener` + - `Accepted/False/HostnameConflict` + - `Accepted/False/MultipleRoutesOnListener` + - `Accepted/False/InvalidGateway` + - `Accepted/False/UnsupportedConfiguration` - `ResolvedRefs/True/ResolvedRefs` - `ResolvedRefs/False/InvalidKind` - `ResolvedRefs/False/RefNotPermitted` - `ResolvedRefs/False/BackendNotFound` - - `ResolvedRefs/False/UnsupportedValue`: Custom reason for when one of the GRPCRoute rules has a backendRef with an unsupported value. + - `ResolvedRefs/False/UnsupportedValue` + - `ResolvedRefs/False/InvalidIPFamily` + - `ResolvedRefs/False/UnsupportedProtocol` + - `ResolvedRefs/False/InvalidFilter` - `PartiallyInvalid/True/UnsupportedValue` - - `Accepted/True/UnsupportedField`: Custom reason for when the GRPCRouteRule is accepted but contains an unsupported field -{{< call-out "note" >}} If `name` or `sessionPersistence` are defined for a GRPCRoute rule, they will be ignored and rule still will be created. {{< /call-out >}} +{{< call-out "note" >}} If `name` is defined for a GRPCRoute rule, it will be ignored and rule still will be created. {{< /call-out >}} ### ReferenceGrant @@ -250,8 +275,7 @@ See the [controller]({{< ref "/ngf/reference/cli-help.md#controller">}}) command | Resource | Core Support Level | Extended Support Level | Implementation-Specific Support Level | API Version | API Release Channel | |----------------|--------------------|------------------------|---------------------------------------|-------------|---------------------| -| ReferenceGrant | Supported | N/A | Not supported | v1beta1 | Standard | - +| ReferenceGrant | Supported | N/A | Not supported | v1 | Standard | {{< /table >}} Fields: @@ -259,11 +283,11 @@ Fields: - `spec` - `to` - `group` - supported. - - `kind` - supports `Secret` and `Service`. + - `kind` - supported. - `name`- supported. - `from` - `group` - supported. - - `kind` - supports `Gateway` and `HTTPRoute`. + - `kind` - supported. - `namespace`- supported. ### TLSRoute @@ -272,8 +296,7 @@ Fields: | Resource | Core Support Level | Extended Support Level | Implementation-Specific Support Level | API Version | API Release Channel | |----------|--------------------|------------------------|---------------------------------------|-------------|---------------------| -| TLSRoute | Supported | Not supported | Not supported | v1alpha2 | Experimental | - +| TLSRoute | Supported | Not supported | Not supported | v1 | Standard | {{< /table >}} **Fields**: @@ -293,14 +316,14 @@ Fields: - `Accepted/False/NoMatchingListenerHostname` - `Accepted/False/NoMatchingParent` - `Accepted/False/NotAllowedByListeners` - - `Accepted/False/UnsupportedValue`: Custom reason for when the TLSRoute includes an invalid or unsupported value. - - `Accepted/False/InvalidListener`: Custom reason for when the TLSRoute references an invalid listener. - - `Accepted/False/HostnameConflict`: Custom reason for when the TLSRoute has a hostname that conflicts with another TLSRoute on the same port. + - `Accepted/False/UnsupportedValue` + - `Accepted/False/InvalidListener` + - `Accepted/False/HostnameConflict` - `ResolvedRefs/True/ResolvedRefs` - `ResolvedRefs/False/InvalidKind` - `ResolvedRefs/False/RefNotPermitted` - `ResolvedRefs/False/BackendNotFound` - - `ResolvedRefs/False/UnsupportedValue`: Custom reason for when one of the TLSRoute rules has a backendRef with an unsupported value. + - `ResolvedRefs/False/UnsupportedValue` - `PartiallyInvalid/True/UnsupportedValue` ### TCPRoute diff --git a/content/ngf/traffic-management/https-termination.md b/content/ngf/traffic-management/https-termination.md index 1722b87939..06e6afbabe 100644 --- a/content/ngf/traffic-management/https-termination.md +++ b/content/ngf/traffic-management/https-termination.md @@ -106,7 +106,7 @@ To create the **access-to-cafe-secret** referencegrant, copy and paste the follo ```yaml kubectl apply -f - <}} TLSRoute is a Gateway API resource from the experimental release channel. {{< /call-out >}} - -{{< include "/ngf/installation/install-gateway-api-experimental-features.md" >}} +In this guide, we will show how to configure TLS passthrough for your application, using a [TLSRoute](https://gateway-api.sigs.k8s.io/reference/spec/#tlsroute). ## Before you begin -- [Install]({{< ref "/ngf/install/" >}}) NGINX Gateway Fabric with experimental features enabled. +- [Install]({{< ref "/ngf/install/" >}}) NGINX Gateway Fabric. ## Set up @@ -129,7 +123,7 @@ Create a Gateway. This will create a TLS listener with the hostname `*.example.c ```yaml kubectl apply -f - <}}To route to a Service in a Namespace different from the TLSRoute Namespace, create a [ReferenceGrant](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1beta1.ReferenceGrant) to permit the cross-namespace reference. {{< /call-out >}} +{{< call-out "note" >}}To route to a Service in a Namespace different from the TLSRoute Namespace, create a [ReferenceGrant](https://gateway-api.sigs.k8s.io/reference/spec/#referencegrant) to permit the cross-namespace reference. {{< /call-out >}} ## Send traffic @@ -270,3 +264,9 @@ hello from pod secure-app-575785644-kzqf6 ``` Note that the server certificate used to terminate the TLS connection has the subject common name of `app.example.com`. This is the server certificate that the `secure-app` is configured with and shows that the TLS connection was terminated by the `secure-app`, not NGINX Gateway Fabric. + +## See also + +To learn more about TLS routing using the Gateway API, see the following resource: + +- [Gateway API TLS routing](https://gateway-api.sigs.k8s.io/guides/tls-routing/) diff --git a/content/ngf/traffic-security/cors.md b/content/ngf/traffic-security/cors.md index f3a29f7c93..82547aa308 100644 --- a/content/ngf/traffic-security/cors.md +++ b/content/ngf/traffic-security/cors.md @@ -225,4 +225,5 @@ Response: ## Further reading - [Example deployment files for HTTPCORSFilter](https://github.com/nginx/nginx-gateway-fabric/tree/main/examples/cors-filter) -- [Gateway API Specification](https://gateway-api.sigs.k8s.io/reference/spec/#httpcorsfilter) \ No newline at end of file +- [Gateway API Specification](https://gateway-api.sigs.k8s.io/reference/spec/#httpcorsfilter) +- [Gateway API CORS](https://gateway-api.sigs.k8s.io/guides/http-cors/) \ No newline at end of file diff --git a/content/ngf/traffic-security/secure-backend.md b/content/ngf/traffic-security/secure-backend.md index a7769c60c2..0fdc89cf9a 100644 --- a/content/ngf/traffic-security/secure-backend.md +++ b/content/ngf/traffic-security/secure-backend.md @@ -36,12 +36,6 @@ sequenceDiagram gw-->>client: Response ``` -## Note on Gateway API Experimental Features - -{{< call-out "important" >}} [GatewayBackendTLS](https://gateway-api.sigs.k8s.io/reference/spec/#gatewaybackendtls) is an experimental field. {{< /call-out >}} - -{{< include "/ngf/installation/install-gateway-api-experimental-features.md" >}} - ## Before you begin - [Install]({{< ref "/ngf/install/" >}}) NGINX Gateway Fabric. From 853e2da499b8d359c4845622e6d2ffd626af030f Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Wed, 11 Mar 2026 08:08:15 -0600 Subject: [PATCH 4/5] docs: NGF SNI configuration (#1764) Add an include for our HTTPS docs that explains how to handle non-SNI based traffic or disable SNI verification. --- content/includes/ngf/sni-https.md | 14 ++++++++++++++ .../ngf/traffic-management/https-termination.md | 4 ++++ .../ngf/traffic-security/integrate-cert-manager.md | 2 ++ 3 files changed, 20 insertions(+) create mode 100644 content/includes/ngf/sni-https.md diff --git a/content/includes/ngf/sni-https.md b/content/includes/ngf/sni-https.md new file mode 100644 index 0000000000..b59cf327c1 --- /dev/null +++ b/content/includes/ngf/sni-https.md @@ -0,0 +1,14 @@ +--- +nd-product: FABRIC +--- + +## HTTPS Traffic without SNI (Server Name Indication) + +Some frontend load balancers strip out SNI information before the traffic reaches the NGINX gateway. In order for NGINX to still process and forward this traffic properly, you must define your HTTPS Listener without a hostname. This instructs NGINX Gateway Fabric to configure a default HTTPS virtual server to handle non-SNI traffic. The TLS configuration on this Listener will be used to verify and terminate TLS for this traffic, before the Host header is then used to forward to the proper virtual server to handle the request. You can attach your HTTPRoutes to this empty Listener. + +By default, NGINX Gateway Fabric verifies that the Listener hostname matches both the SNI and Host header on an incoming client request. This does not require the SNI and Host header to be the same. This is to avoid misdirected requests, and returns a 421 response code. If you run into issues and want to disable this SNI/Host verification, you can update the [NginxProxy CRD]({{< ref "/ngf/how-to/data-plane-configuration.md" >}}) with the following field in the spec: + +```yaml +spec: + disableSNIHostValidation: true +``` diff --git a/content/ngf/traffic-management/https-termination.md b/content/ngf/traffic-management/https-termination.md index 06e6afbabe..9a2d96c6fb 100644 --- a/content/ngf/traffic-management/https-termination.md +++ b/content/ngf/traffic-management/https-termination.md @@ -276,8 +276,12 @@ Server address: 10.244.0.6:80 Server name: coffee-6b8b6d6486-7fc78 ``` +{{< include "ngf/sni-https.md" >}} + ## See also +To set up a production-ready integration with cert-manager for HTTPS traffic, see the [Secure traffic using Let's Encrypt]({{< ref "/ngf/traffic-security/integrate-cert-manager.md" >}}) guide. + To learn more about redirects using the Gateway API, see the following resource: - [Gateway API Redirects](https://gateway-api.sigs.k8s.io/guides/http-redirect-rewrite/) diff --git a/content/ngf/traffic-security/integrate-cert-manager.md b/content/ngf/traffic-security/integrate-cert-manager.md index 00a2a11c88..84919b252f 100644 --- a/content/ngf/traffic-security/integrate-cert-manager.md +++ b/content/ngf/traffic-security/integrate-cert-manager.md @@ -303,6 +303,8 @@ Request ID: e64c54a2ac253375ac085d48980f000a <...> ``` +{{< include "ngf/sni-https.md" >}} + --- ## See also From a025a156d7c2a9119d1cb0c0cdb925a86d3e11cc Mon Sep 17 00:00:00 2001 From: Ciara Stacke <18287516+ciarams87@users.noreply.github.com> Date: Fri, 13 Mar 2026 10:31:20 +0000 Subject: [PATCH 5/5] NGF: Add F5 WAF integration guide --- .../traffic-security/basic-authentication.md | 2 +- content/ngf/traffic-security/cors.md | 2 +- .../traffic-security/f5-waf-integration.md | 650 ++++++++++++++++++ 3 files changed, 652 insertions(+), 2 deletions(-) create mode 100644 content/ngf/traffic-security/f5-waf-integration.md diff --git a/content/ngf/traffic-security/basic-authentication.md b/content/ngf/traffic-security/basic-authentication.md index 541a2e017a..88012ce801 100644 --- a/content/ngf/traffic-security/basic-authentication.md +++ b/content/ngf/traffic-security/basic-authentication.md @@ -1,6 +1,6 @@ --- title: Configure basic authentication -weight: 800 +weight: 300 toc: true nd-content-type: how-to nd-product: FABRIC diff --git a/content/ngf/traffic-security/cors.md b/content/ngf/traffic-security/cors.md index 82547aa308..5b89568b88 100644 --- a/content/ngf/traffic-security/cors.md +++ b/content/ngf/traffic-security/cors.md @@ -1,6 +1,6 @@ --- title: Configure Cross-Origin Request Sharing (CORS) -weight: 900 +weight: 400 toc: true nd-content-type: how-to nd-product: FABRIC diff --git a/content/ngf/traffic-security/f5-waf-integration.md b/content/ngf/traffic-security/f5-waf-integration.md new file mode 100644 index 0000000000..d014f46fb6 --- /dev/null +++ b/content/ngf/traffic-security/f5-waf-integration.md @@ -0,0 +1,650 @@ +--- +title: Configure F5 WAF for NGINX for NGINX Gateway Fabric +weight: 500 +toc: true +nd-content-type: how-to +nd-product: FABRIC +--- + +This document describes how to configure F5 NGINX Gateway Fabric to enable integration with F5 WAF for NGINX and protect your application traffic. + +--- + +## Before you begin + +You need: + +- Administrator access to a Kubernetes cluster. +- [Helm](https://helm.sh) and [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) installed locally. +- **NGINX Plus** is required for F5 WAF support. You need valid NGINX Plus credentials to pull images from `private-registry.nginx.com`. +- A valid **F5 WAF for NGINX** license. + +--- + +## Set up NGINX Plus credentials + +F5 WAF support requires NGINX Plus. Before installing NGINX Gateway Fabric, you need to configure your NGINX Plus credentials as Kubernetes Secrets. See [Install NGINX Gateway Fabric with NGINX Plus]({{< ref "/ngf/install/nginx-plus.md" >}}) for full details. The steps below summarize what is required. + +{{< call-out "note" >}} Create these Secrets in the `nginx-gateway` namespace. If the namespace does not yet exist, create it first with `kubectl create namespace nginx-gateway`. {{< /call-out >}} + +### Download the JWT from MyF5 + +{{< include "/ngf/installation/nginx-plus/download-jwt.md" >}} + +### Create the Docker registry Secret + +{{< include "/ngf/installation/nginx-plus/docker-registry-secret.md" >}} + +### Create the NGINX Plus license Secret + +{{< include "/ngf/installation/nginx-plus/nginx-plus-secret.md" >}} + +--- + +## Install NGINX Gateway Fabric with PLM + +F5 WAF for NGINX relies on the **Policy Lifecycle Manager (PLM)**, which compiles WAF policies and stores them in an in-cluster S3-compatible object store (SeaweedFS). NGINX Gateway Fabric includes PLM as an optional subchart. + +### Bootstrap TLS certificates for SeaweedFS + +PLM's internal SeaweedFS storage requires TLS certificates. The following steps use [cert-manager](https://cert-manager.io) to generate self-signed certificates as a convenient starting point. In production environments, you can manage TLS secrets with your own certificate infrastructure and skip this section, provided you create secrets with the following names in the `nginx-gateway` namespace: + +| Secret name | Purpose | +| --- | --- | +| `ngf-f5-waf-seaweedfs-ca-cert` | CA certificate | +| `ngf-f5-waf-seaweedfs-master-cert` | SeaweedFS master TLS | +| `ngf-f5-waf-seaweedfs-volume-cert` | SeaweedFS volume TLS | +| `ngf-f5-waf-seaweedfs-filer-cert` | SeaweedFS filer TLS | +| `ngf-f5-waf-seaweedfs-client-cert` | Client mTLS certificate | + +The secret names are derived from your Helm release name (`ngf` in this guide). If you use a different release name, adjust the names accordingly: `{release-name}-f5-waf-seaweedfs-{component}`. + +1. Install cert-manager: + + ```shell + kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.4/cert-manager.yaml + ``` + + Wait for the cert-manager pods to be ready before continuing: + + ```shell + kubectl wait --for=condition=Available deployment --all -n cert-manager --timeout=120s + ``` + +2. Apply the SeaweedFS certificate resources. This creates a self-signed CA Issuer and TLS certificates for the SeaweedFS master, volume, filer, and client components: + + ```yaml + kubectl apply -f - <}} The example uses a self-signed CA. For production use, replace these with certificates from your own CA or certificate management solution. {{< /call-out >}} + +### Install NGF with PLM enabled + +Install NGINX Gateway Fabric with the PLM subchart enabled. When `f5-waf-plm.enabled=true`, Helm automatically configures the control plane to connect to PLM's SeaweedFS storage — no additional `plmStorage` values are required. + +```shell +helm install ngf oci://ghcr.io/nginx/charts/nginx-gateway-fabric \ + --namespace nginx-gateway \ + --create-namespace \ + --set nginx.plus=true \ + --set nginx.imagePullSecret=nginx-plus-registry-secret \ + --set f5-waf-plm.enabled=true \ + --set f5-waf-plm.imagePullSecrets[0]=nginx-plus-registry-secret \ + --set f5-waf-plm.seaweedfsOperatorConfig.seaweedfs.certificates.enabled=true +``` + +The `certificates.enabled=true` flag tells PLM to use the TLS secrets created in the previous step. + +Verify that the NGF and PLM pods are running: + +```shell +kubectl get pods -n nginx-gateway +``` + +--- + +## Deploy the sample application + +Deploy a syslog server to receive WAF security event logs, and the sample cafe application. The coffee service is configured to return sensitive data (credit card numbers and SSNs) in its responses — this is intentional, to demonstrate WAF blocking behaviour. + +1. Deploy the syslog server: + + ```yaml + kubectl apply -f - < + ``` + + {{< call-out "note" >}} In a production environment, you should have a DNS record for the external IP address that is exposed, and it should refer to the hostname that the gateway will forward for. {{< /call-out >}} + +3. Create the `HTTPRoute` resources for the coffee and tea services: + + ```yaml + kubectl apply -f - < +``` + +--- + +## Verify WAF is blocking traffic + +Send another request to the coffee service. The WAF policy now blocks the response containing sensitive data: + +```shell +curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee +``` + +The request is rejected by the WAF: + +```text +Request RejectedThe requested URL was rejected. Please consult with your administrator.

Your support ID is: 11294711299894599313

[Go Back] +``` + +The WAF Data Guard policy has successfully blocked the response containing the credit card number and SSN. Security log events are forwarded to the syslog server deployed earlier. + +--- + +TODO: + +1. Add AP* API reference for full configuration options +2. Detail how to configure signature updates (including needing nginx.crt and nginx.key for pulling updates) +3. Document how to configure policies from external resources, if supported, otherwise add note that they are not yet supported as these options are in the CRD