From 131cd88ce57c74a6655a0cf153c467f443a3d8b3 Mon Sep 17 00:00:00 2001 From: Louis Burda Date: Tue, 23 Jun 2026 12:32:22 +0200 Subject: [PATCH 1/5] feat(translator): Support ignoreFailure to tolerate invalid JWT Signed-off-by: Louis Burda --- api/v1alpha1/jwt_types.go | 8 +- api/v1alpha1/zz_generated.deepcopy.go | 5 + ...ateway.envoyproxy.io_securitypolicies.yaml | 9 +- ...ateway.envoyproxy.io_securitypolicies.yaml | 9 +- internal/gatewayapi/securitypolicy.go | 5 +- ...ritypolicy-with-jwt-ignore-failure.in.yaml | 53 +++++ ...itypolicy-with-jwt-ignore-failure.out.yaml | 220 ++++++++++++++++++ internal/ir/xds.go | 4 + internal/xds/translator/jwt.go | 11 +- .../xds-ir/jwt-allow-missing-or-failed.yaml | 49 ++++ .../jwt-allow-missing-or-failed.clusters.yaml | 65 ++++++ ...jwt-allow-missing-or-failed.endpoints.yaml | 12 + ...jwt-allow-missing-or-failed.listeners.yaml | 85 +++++++ .../jwt-allow-missing-or-failed.routes.yaml | 18 ++ release-notes/current.yaml | 62 +++++ site/content/en/latest/api/extension_types.md | 3 +- test/helm/gateway-crds-helm/all.out.yaml | 9 +- test/helm/gateway-crds-helm/e2e.out.yaml | 9 +- .../envoy-gateway-crds.out.yaml | 9 +- 19 files changed, 635 insertions(+), 10 deletions(-) create mode 100644 internal/gatewayapi/testdata/securitypolicy-with-jwt-ignore-failure.in.yaml create mode 100644 internal/gatewayapi/testdata/securitypolicy-with-jwt-ignore-failure.out.yaml create mode 100644 internal/xds/translator/testdata/in/xds-ir/jwt-allow-missing-or-failed.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.clusters.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.endpoints.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.listeners.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.routes.yaml create mode 100644 release-notes/current.yaml diff --git a/api/v1alpha1/jwt_types.go b/api/v1alpha1/jwt_types.go index da28346c15..9162722db8 100644 --- a/api/v1alpha1/jwt_types.go +++ b/api/v1alpha1/jwt_types.go @@ -12,9 +12,15 @@ import ( // JWT defines the configuration for JSON Web Token (JWT) authentication. type JWT struct { // Optional determines whether a missing JWT is acceptable, defaulting to false if not specified. - // Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT is presented. + // Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT + // is presented. See IgnoreFailure if this is necessary for your use case. Optional *bool `json:"optional,omitempty"` + // IgnoreFailure allows a request to pass the JWT filter even when its JWT is + // missing or invalid. Supersedes Optional and maps to Envoy's `allow_missing_or_failed`. + // It does not enforce authentication on its own, so pair it with an Authorization policy. + IgnoreFailure *bool `json:"ignoreFailure,omitempty"` + // Providers defines the JSON Web Token (JWT) authentication provider type. // When multiple JWT providers are specified, the JWT is considered valid if // any of the providers successfully validate the JWT. For additional details, diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a566e3c1df..48e5b5e6d2 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5025,6 +5025,11 @@ func (in *JWT) DeepCopyInto(out *JWT) { *out = new(bool) **out = **in } + if in.IgnoreFailure != nil { + in, out := &in.IgnoreFailure, &out.IgnoreFailure + *out = new(bool) + **out = **in + } if in.Providers != nil { in, out := &in.Providers, &out.Providers *out = make([]JWTProvider, len(*in)) diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml index 6eba7e7eb4..29ab1cd2be 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -3795,10 +3795,17 @@ spec: description: JWT defines the configuration for JSON Web Token (JWT) authentication. properties: + ignoreFailure: + description: |- + IgnoreFailure allows a request to pass the JWT filter even when its JWT is + missing or invalid. Supersedes Optional and maps to Envoy's `allow_missing_or_failed`. + It does not enforce authentication on its own, so pair it with an Authorization policy. + type: boolean optional: description: |- Optional determines whether a missing JWT is acceptable, defaulting to false if not specified. - Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT is presented. + Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT + is presented. See IgnoreFailure if this is necessary for your use case. type: boolean providers: description: |- diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index 3f1ec675ff..dd0137cee8 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -3794,10 +3794,17 @@ spec: description: JWT defines the configuration for JSON Web Token (JWT) authentication. properties: + ignoreFailure: + description: |- + IgnoreFailure allows a request to pass the JWT filter even when its JWT is + missing or invalid. Supersedes Optional and maps to Envoy's `allow_missing_or_failed`. + It does not enforce authentication on its own, so pair it with an Authorization policy. + type: boolean optional: description: |- Optional determines whether a missing JWT is acceptable, defaulting to false if not specified. - Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT is presented. + Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT + is presented. See IgnoreFailure if this is necessary for your use case. type: boolean providers: description: |- diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index 6e796e21ae..f6372ea4d1 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -1388,8 +1388,9 @@ func (t *Translator) buildJWT( } return &ir.JWT{ - AllowMissing: ptr.Deref(policy.Spec.JWT.Optional, false), - Providers: providers, + AllowMissing: ptr.Deref(policy.Spec.JWT.Optional, false), + AllowMissingOrFailed: ptr.Deref(policy.Spec.JWT.IgnoreFailure, false), + Providers: providers, }, nil } diff --git a/internal/gatewayapi/testdata/securitypolicy-with-jwt-ignore-failure.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-jwt-ignore-failure.in.yaml new file mode 100644 index 0000000000..8abc142e53 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-jwt-ignore-failure.in.yaml @@ -0,0 +1,53 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + jwt: + ignoreFailure: true + providers: + - name: example + issuer: https://www.example.com + remoteJWKS: + uri: https://www.example.com/jwt/public-key/jwks.json diff --git a/internal/gatewayapi/testdata/securitypolicy-with-jwt-ignore-failure.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-jwt-ignore-failure.out.yaml new file mode 100644 index 0000000000..5188f5936e --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-jwt-ignore-failure.out.yaml @@ -0,0 +1,220 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/gateway-1 + namespace: envoy-gateway-system +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + name: policy-for-route + namespace: default + spec: + jwt: + ignoreFailure: true + providers: + - issuer: https://www.example.com + name: example + remoteJWKS: + uri: https://www.example.com/jwt/public-key/jwks.json + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: spec.targetRef is deprecated, use spec.targetRefs instead + reason: DeprecatedField + status: "True" + type: Warning + controllerName: gateway.envoyproxy.io/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + security: + jwt: + allowMissingOrFailed: true + providers: + - issuer: https://www.example.com + name: example + remoteJWKS: + uri: https://www.example.com/jwt/public-key/jwks.json + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 2c5d0c78e3..216f08bbc2 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -1203,6 +1203,10 @@ type JWT struct { // AllowMissing determines whether a missing JWT is acceptable. AllowMissing bool `json:"allowMissing,omitempty" yaml:"allowMissing,omitempty"` + // AllowMissingOrFailed determines whether a missing or invalid JWT is tolerated. + // When true it supersedes AllowMissing and maps to Envoy's `allow_missing_or_failed`. + AllowMissingOrFailed bool `json:"allowMissingOrFailed,omitempty" yaml:"allowMissingOrFailed,omitempty"` + // Providers defines a list of JSON Web Token (JWT) authentication providers. Providers []JWTProvider `json:"providers,omitempty" yaml:"providers,omitempty"` } diff --git a/internal/xds/translator/jwt.go b/internal/xds/translator/jwt.go index dbfea8e021..2ea27a835c 100644 --- a/internal/xds/translator/jwt.go +++ b/internal/xds/translator/jwt.go @@ -215,7 +215,16 @@ func buildJWTAuthn(irListener *ir.HTTPListener, jwtAuthn *jwtauthnv3.JwtAuthenti }) } - if route.Security.JWT.AllowMissing { + // AllowMissingOrFailed supersedes AllowMissing: it tolerates a missing or + // invalid token, whereas AllowMissing still rejects an invalid one. + switch { + case route.Security.JWT.AllowMissingOrFailed: + reqs = append(reqs, &jwtauthnv3.JwtRequirement{ + RequiresType: &jwtauthnv3.JwtRequirement_AllowMissingOrFailed{ + AllowMissingOrFailed: &emptypb.Empty{}, + }, + }) + case route.Security.JWT.AllowMissing: reqs = append(reqs, &jwtauthnv3.JwtRequirement{ RequiresType: &jwtauthnv3.JwtRequirement_AllowMissing{ AllowMissing: &emptypb.Empty{}, diff --git a/internal/xds/translator/testdata/in/xds-ir/jwt-allow-missing-or-failed.yaml b/internal/xds/translator/testdata/in/xds-ir/jwt-allow-missing-or-failed.yaml new file mode 100644 index 0000000000..2b2df1be91 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jwt-allow-missing-or-failed.yaml @@ -0,0 +1,49 @@ +http: +- name: "first-listener" + address: "::" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "foo/bar" + security: + jwt: + # allowMissingOrFailed lets a valid token from one source pass even when + # another source carries an invalid token (maps to Envoy's + # allow_missing_or_failed). The two single-source providers are OR-ed. + allowMissingOrFailed: true + providers: + - name: example-cookie + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + cacheDuration: 300s + extractFrom: + cookies: + - session_access_token + - name: example-header + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + cacheDuration: 300s + extractFrom: + headers: + - name: Authorization + valuePrefix: 'Bearer ' + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + name: "first-route-dest/backend/0" diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.clusters.yaml new file mode 100644 index 0000000000..e718e68790 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.clusters.yaml @@ -0,0 +1,65 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + ignoreHealthOnHostRemoval: true + loadBalancingPolicy: + policies: + - typedExtensionConfig: + name: envoy.load_balancing_policies.least_request + typedConfig: + '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest + localityLbConfig: + localityWeightedLbConfig: {} + name: first-route-dest + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + dnsRefreshRate: 30s + ignoreHealthOnHostRemoval: true + loadAssignment: + clusterName: localhost_443 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: localhost + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: localhost_443/backend/-1 + loadBalancingPolicy: + policies: + - typedExtensionConfig: + name: envoy.load_balancing_policies.least_request + typedConfig: + '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest + localityLbConfig: + localityWeightedLbConfig: {} + name: localhost_443 + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/ssl/certs/ca-certificates.crt + sni: localhost + type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.endpoints.yaml new file mode 100644 index 0000000000..3b3f2d0907 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.listeners.yaml new file mode 100644 index 0000000000..28e809654e --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.listeners.yaml @@ -0,0 +1,85 @@ +- address: + socketAddress: + address: '::' + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.jwt_authn + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + first-route/example-cookie: + audiences: + - foo.com + forward: true + fromCookies: + - session_access_token + issuer: https://www.example.com + normalizePayloadInMetadata: + spaceDelimitedClaims: + - scope + - scp + payloadInMetadata: example-cookie + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 10s + uri: https://localhost/jwt/public-key/jwks.json + first-route/example-header: + audiences: + - foo.com + forward: true + fromHeaders: + - name: Authorization + valuePrefix: 'Bearer ' + issuer: https://www.example.com + normalizePayloadInMetadata: + spaceDelimitedClaims: + - scope + - scp + payloadInMetadata: example-header + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 10s + uri: https://localhost/jwt/public-key/jwks.json + requirementMap: + first-route: + requiresAny: + requirements: + - providerName: first-route/example-cookie + - providerName: first-route/example-header + - allowMissingOrFailed: {} + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http-10080 + useRemoteAddress: true + name: first-listener + maxConnectionsToAcceptPerSocketEvent: 1 + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.routes.yaml new file mode 100644 index 0000000000..321ecc1ced --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: foo/bar + name: first-route + route: + cluster: first-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: first-route diff --git a/release-notes/current.yaml b/release-notes/current.yaml new file mode 100644 index 0000000000..bbba293e03 --- /dev/null +++ b/release-notes/current.yaml @@ -0,0 +1,62 @@ +date: Pending + +# Changes that are expected to cause an incompatibility with previous versions, such as deletions or modifications to existing APIs. +breaking changes: | + Moved the Gateway API safe-upgrades ValidatingAdmissionPolicy resources out of the CRD bundle and into the gateway-helm chart templates so tools such as Flux no longer treat them as CRDs. During upgrades, two upgrade cases require action: (1) if you install Gateway API CRDs separately (e.g. with the gateway-crds-helm chart and `helm install --skip-crds`), the safe-upgrades ValidatingAdmissionPolicy and its binding are now rendered by the gateway-helm chart, so add Helm ownership metadata (the `meta.helm.sh/release-name`, `meta.helm.sh/release-namespace` annotations and the `app.kubernetes.io/managed-by=Helm` label) to the `ValidatingAdmissionPolicy/safe-upgrades.gateway.networking.k8s.io` and `ValidatingAdmissionPolicyBinding/safe-upgrades.gateway.networking.k8s.io` resources before upgrading so Helm can manage them (see https://gateway.envoyproxy.io/v1.8/install/install-helm/#installing-crds-separately); (2) if Gateway API CRDs and safe upgrade policy resources are managed by your cloud provider (or any other mechanism outside this chart), note that `--skip-crds` does not skip chart-templated resources, so disable rendering of the safe-upgrades ValidatingAdmissionPolicy by setting `crds.gatewayAPI.safeUpgradePolicy.enabled=false` (see https://gateway.envoyproxy.io/v1.8/install/install-helm/#clusters-with-compatible-provider-managed-gateway-api-crds). + Lua EnvoyExtensionPolicies are now disabled by default. Use the new `enableLua` field in `extensionApis` to explicitly enable them. The `disableLua` field is deprecated and will be removed in a future release. + The `XRateLimitHeadersOptionDisabled` constant in `BackendTrafficPolicy` now correctly holds the value `"Off"` to match the CRD enum (previously `"Disabled"`). Since `"Disabled"` was never a valid CRD enum value and would have been rejected by the API server, no existing manifests are affected. + `SecurityPolicy` `spec.apiKeyAuth.extractFrom` admission validation is now stricter: the list must contain at least one entry, each entry must specify exactly one of `headers`, `params`, or `cookies`, and source names must be non-empty. SecurityPolicies that previously applied with an empty or ambiguous `extractFrom` (which produced no usable API key sources) will now be rejected and must be corrected before upgrading. + +# Updates addressing vulnerabilities, security flaws, or compliance requirements. +security updates: | + Fixed xDS server authentication bypass in GatewayNamespaceMode, adding Unary Interceptor and validating SotW requests. + +# New features or capabilities added in this release. +new features: | + Added support for authorization path match. + Added a `requestBody` field to the HTTP active health checker in `BackendTrafficPolicy`, allowing a request body payload to be sent during HTTP health checking. The field requires the health check `method` to be `POST` or `PUT`. + Added a `xdsNACKTotal` metric to track the number of NACKs received from Envoy, labeled by node ID and resource type URL. A NACK is a DiscoveryRequest carrying an ErrorDetail, indicating that Envoy rejected the last config update. This metric can be used to alert on config issues causing xDS rejections. + Added the `autoSNIFromEndpointHostname` TLS setting to Backends, allowing the SNI value sent to the backend to be automatically derived from the backend endpoint hostname instead of using a fixed SNI value. + Added support for `filterContext` field in Lua EnvoyExtensionPolicy, allowing shared Lua scripts to be parameterized per route via `request_handle:filterContext()`. + Added support for `ListenerSet` as a `targetRef` kind in `ClientTrafficPolicy`, allowing client traffic settings to be applied to a named group of listeners without a gateway-wide policy. + Added a `fromMetadata` field to global rate limit `limit` in `BackendTrafficPolicy`, allowing the limit value to be sourced from per-request dynamic metadata (e.g. set by an upstream ext_proc), falling back to the static `requests`/`unit` when the metadata is absent. + Added support for disabling the `crds` dependency on the gateway-helm chart via `crds.enabled` variable. + Added `spec.clientIPDetection.xForwardedFor.disableXForwardedForAppend` to ClientTrafficPolicy to disable Envoy's automatic X-Forwarded-For append behavior when using XFF-based client IP detection. + Added a `failedRefetchDuration` field to JWT providers in `SecurityPolicy`, configuring how long Envoy waits before re-fetching the JWKS after a failed fetch. If not specified, Envoy's default of 1 second is used. + Added an `ignoreFailure` field to JWT in `SecurityPolicy`, allowing a request to pass the JWT filter even when its JWT is missing or invalid (maps to Envoy's `allow_missing_or_failed`). Verified claims are still forwarded, so it should be paired with an Authorization policy for enforcement. + +bug fixes: | + Fixed HTTPRoute, GRPCRoute, TLSRoute, TCPRoute, and UDPRoute Accepted condition being set to False when an attached listener is not programmed due to a missing TLS certificate ref; listener programmed state is now correctly separated from route acceptance. + Fixed Backend TLS `alpnProtocols: []` to disable upstream ALPN instead of inheriting EnvoyProxy BackendTLS defaults. + Fixed the xDS server in GatewayNamespaceMode serving a stale certificate after cert-manager rotation by re-reading the cert from disk on every TLS handshake. + Fixed controller panic when processing backend tls settings. + Fixed Gateway status reporting `Programmed: False` with reason `AddressNotAssigned` when the Envoy LoadBalancer service has no load balancer ingress (e.g. bare-metal clusters without a load balancer controller) but has addresses configured in `spec.externalIPs`, such as via an EnvoyProxy service patch. The external IPs are now used as a fallback for the Gateway status addresses. + Fixed egctl config commands hanging when Envoy pod port-forwarding fails. + Fixed BackendTLSPolicy selection to prefer section name over wildcard match on the same backend. + Fixed missing deprecated field warning in ClientTrafficPolicy and SecurityPolicy. + Fixed ClientTrafficPolicy TLS cipher validation rejecting supported IANA/RFC cipher suite names. + Fixed the EnvoyProxy resource not allowing IPv6 ranges in loadBalancerSourceRanges when configuring the envoy service. + Fixed Kubernetes provider namespace-scoped watches to always include the controller namespace so Envoy Gateway can read its own infrastructure resources. + Fixed TLS secrets with non-canonical PEM formatting (e.g. unusual line endings) being passed verbatim to Envoy, which could cause BoringSSL errors such as `BAD_END_LINE`. Cert and key PEM data is now re-encoded to a canonical form before being delivered as xDS resources. + Fixed `MaxStreamDuration` not being set on `CommonHttpProtocolOptions` for non-route cluster. + Fixed `egctl x status all`/`xroute`/`xpolicy` failing when a Gateway API CRD (e.g. TCPRoute) is not installed in the cluster; missing CRDs are now skipped silently, or reported on stderr with `-v`. + Fixed Kubernetes Service and ServiceImport `appProtocol` values `kubernetes.io/ws` and `kubernetes.io/wss` to force HTTP/1.1 upstream connections instead of negotiating HTTP/2, avoiding compatibility issues with WebSocket backends that do not support RFC 8441 extended CONNECT. + Fixed an `ExternalName` Service referenced as a route backend producing an invalid xDS cluster (with an empty address) that failed IR validation and stalled config delivery for the whole snapshot. `ExternalName` Services are now explicitly rejected as backends with a `ResolvedRefs: False` route condition; use an Envoy Gateway `Backend` resource with an FQDN endpoint instead. + Fixed API key auth credential ordering to avoid unnecessary xDS updates. + Fixed the generated `install.yaml` creating a duplicate ValidatingAdmissionPolicy and its binding which caused `kustomize build` to fail with a duplicate resource error. + Fixed ListenerSet hostname conflict resolution to apply listener precedence: Gateway-owned listeners win over ListenerSet listeners, and among ListenerSet listeners the first in processing order wins. Conflicted ListenerSet listeners now correctly report Accepted=False with the conflict reason. The Gateway's AttachedListenerSets count now only reflects ListenerSets with at least one accepted listener. + Fixed upstream PROXY protocol clusters to preserve generated HTTP protocol options, including auto protocol detection for Backend TLS. + Fixed EnvoyGateway config hot-reload to apply defaults before validation, so validators always run against a fully-defaulted struct on both the startup and reload paths. + Fixed deduplicate CA certificates in ClientTrafficPolicy mTLS. + Fixed HTTPRoute per-retry timeout (derived from `rule.timeouts.backendRequest`) not being applied when no retry backoff was configured. + Fixed shared global rate limit rules with a `cost` field not working as expected. + +# Enhancements that improve performance. +performance improvements: | + +# Deprecated features or APIs. +deprecations: | + The `disableLua` field in `extensionApis` is deprecated in favor of `enableLua`. + +# Other notable changes not covered by the above sections. +Other changes: | diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 8c78804007..346ead79b8 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -3526,7 +3526,8 @@ _Appears in:_ | Field | Type | Required | Default | Description | | --- | --- | --- | --- | --- | -| `optional` | _boolean_ | true | | Optional determines whether a missing JWT is acceptable, defaulting to false if not specified.
Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT is presented. | +| `optional` | _boolean_ | true | | Optional determines whether a missing JWT is acceptable, defaulting to false if not specified.
Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT
is presented. See IgnoreFailure if this is necessary for your use case. | +| `ignoreFailure` | _boolean_ | true | | IgnoreFailure allows a request to pass the JWT filter even when its JWT is
missing or invalid. Supersedes Optional and maps to Envoy's `allow_missing_or_failed`.
It does not enforce authentication on its own, so pair it with an Authorization policy. | | `providers` | _[JWTProvider](#jwtprovider) array_ | true | | Providers defines the JSON Web Token (JWT) authentication provider type.
When multiple JWT providers are specified, the JWT is considered valid if
any of the providers successfully validate the JWT. For additional details,
see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter.html. | diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index a612e04fae..5a5741f071 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -54157,10 +54157,17 @@ spec: description: JWT defines the configuration for JSON Web Token (JWT) authentication. properties: + ignoreFailure: + description: |- + IgnoreFailure allows a request to pass the JWT filter even when its JWT is + missing or invalid. Supersedes Optional and maps to Envoy's `allow_missing_or_failed`. + It does not enforce authentication on its own, so pair it with an Authorization policy. + type: boolean optional: description: |- Optional determines whether a missing JWT is acceptable, defaulting to false if not specified. - Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT is presented. + Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT + is presented. See IgnoreFailure if this is necessary for your use case. type: boolean providers: description: |- diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml index b8b42a2d53..e0197b477d 100644 --- a/test/helm/gateway-crds-helm/e2e.out.yaml +++ b/test/helm/gateway-crds-helm/e2e.out.yaml @@ -32130,10 +32130,17 @@ spec: description: JWT defines the configuration for JSON Web Token (JWT) authentication. properties: + ignoreFailure: + description: |- + IgnoreFailure allows a request to pass the JWT filter even when its JWT is + missing or invalid. Supersedes Optional and maps to Envoy's `allow_missing_or_failed`. + It does not enforce authentication on its own, so pair it with an Authorization policy. + type: boolean optional: description: |- Optional determines whether a missing JWT is acceptable, defaulting to false if not specified. - Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT is presented. + Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT + is presented. See IgnoreFailure if this is necessary for your use case. type: boolean providers: description: |- diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index 276e43f622..29d24d3216 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -32130,10 +32130,17 @@ spec: description: JWT defines the configuration for JSON Web Token (JWT) authentication. properties: + ignoreFailure: + description: |- + IgnoreFailure allows a request to pass the JWT filter even when its JWT is + missing or invalid. Supersedes Optional and maps to Envoy's `allow_missing_or_failed`. + It does not enforce authentication on its own, so pair it with an Authorization policy. + type: boolean optional: description: |- Optional determines whether a missing JWT is acceptable, defaulting to false if not specified. - Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT is presented. + Note: Even if optional is set to true, JWT authentication will still fail if an invalid JWT + is presented. See IgnoreFailure if this is necessary for your use case. type: boolean providers: description: |- From c67624c2c1b6df963c2e2a320c7da867414c27ca Mon Sep 17 00:00:00 2001 From: Louis Burda Date: Tue, 23 Jun 2026 13:13:10 +0200 Subject: [PATCH 2/5] Test allowMissing and allowMissingOrFailed together to show superseding Signed-off-by: Louis Burda --- .../xds-ir/jwt-allow-missing-or-failed.yaml | 25 +++++++++++++++++++ .../jwt-allow-missing-or-failed.clusters.yaml | 23 +++++++++++++++++ ...jwt-allow-missing-or-failed.endpoints.yaml | 12 +++++++++ ...jwt-allow-missing-or-failed.listeners.yaml | 22 ++++++++++++++++ .../jwt-allow-missing-or-failed.routes.yaml | 11 ++++++++ 5 files changed, 93 insertions(+) diff --git a/internal/xds/translator/testdata/in/xds-ir/jwt-allow-missing-or-failed.yaml b/internal/xds/translator/testdata/in/xds-ir/jwt-allow-missing-or-failed.yaml index 2b2df1be91..6fd9e77d46 100644 --- a/internal/xds/translator/testdata/in/xds-ir/jwt-allow-missing-or-failed.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/jwt-allow-missing-or-failed.yaml @@ -47,3 +47,28 @@ http: - host: "1.2.3.4" port: 50000 name: "first-route-dest/backend/0" + - name: "second-route" + hostname: "*" + pathMatch: + exact: "foo/baz" + security: + jwt: + # allowMissingOrFailed supersedes allowMissing: with both set, only the + # allow_missing_or_failed requirement is emitted. + allowMissing: true + allowMissingOrFailed: true + providers: + - name: example + issuer: https://www.example.com + audiences: + - foo.com + remoteJWKS: + uri: https://localhost/jwt/public-key/jwks.json + cacheDuration: 300s + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "5.6.7.8" + port: 50000 + name: "second-route-dest/backend/0" diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.clusters.yaml index e718e68790..7bba0835f2 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.clusters.yaml @@ -21,6 +21,29 @@ name: first-route-dest perConnectionBufferLimitBytes: 32768 type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_PREFERRED + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + ignoreHealthOnHostRemoval: true + loadBalancingPolicy: + policies: + - typedExtensionConfig: + name: envoy.load_balancing_policies.least_request + typedConfig: + '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest + localityLbConfig: + localityWeightedLbConfig: {} + name: second-route-dest + perConnectionBufferLimitBytes: 32768 + type: EDS - circuitBreakers: thresholds: - maxRetries: 1024 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.endpoints.yaml index 3b3f2d0907..39102da400 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.endpoints.yaml @@ -10,3 +10,15 @@ loadBalancingWeight: 1 locality: region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 5.6.7.8 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.listeners.yaml index 28e809654e..6b79a42d6d 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.listeners.yaml @@ -57,6 +57,23 @@ cluster: localhost_443 timeout: 10s uri: https://localhost/jwt/public-key/jwks.json + second-route/example: + audiences: + - foo.com + forward: true + issuer: https://www.example.com + normalizePayloadInMetadata: + spaceDelimitedClaims: + - scope + - scp + payloadInMetadata: example + remoteJwks: + asyncFetch: {} + cacheDuration: 300s + httpUri: + cluster: localhost_443 + timeout: 10s + uri: https://localhost/jwt/public-key/jwks.json requirementMap: first-route: requiresAny: @@ -64,6 +81,11 @@ - providerName: first-route/example-cookie - providerName: first-route/example-header - allowMissingOrFailed: {} + second-route: + requiresAny: + requirements: + - providerName: second-route/example + - allowMissingOrFailed: {} - name: envoy.filters.http.router typedConfig: '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router diff --git a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.routes.yaml index 321ecc1ced..7c52b6de16 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jwt-allow-missing-or-failed.routes.yaml @@ -16,3 +16,14 @@ envoy.filters.http.jwt_authn: '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig requirementName: first-route + - match: + path: foo/baz + name: second-route + route: + cluster: second-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.jwt_authn: + '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + requirementName: second-route From 674395fbbb5e85c127ec3d2008cd39156076d1c7 Mon Sep 17 00:00:00 2001 From: Louis Burda Date: Wed, 24 Jun 2026 11:38:42 +0200 Subject: [PATCH 3/5] Fixup to conform to new release-notes format Signed-off-by: Louis Burda --- ...issing_or_invalid-jwt_authn-requirement-for-securitypolicy.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 release-notes/current/new_features/9251-support-settting-allow_missing_or_invalid-jwt_authn-requirement-for-securitypolicy.md diff --git a/release-notes/current/new_features/9251-support-settting-allow_missing_or_invalid-jwt_authn-requirement-for-securitypolicy.md b/release-notes/current/new_features/9251-support-settting-allow_missing_or_invalid-jwt_authn-requirement-for-securitypolicy.md new file mode 100644 index 0000000000..4ec7d6ee54 --- /dev/null +++ b/release-notes/current/new_features/9251-support-settting-allow_missing_or_invalid-jwt_authn-requirement-for-securitypolicy.md @@ -0,0 +1 @@ +Added an `ignoreFailure` field to JWT in `SecurityPolicy`, allowing a request to pass the JWT filter even when its JWT is missing or invalid (maps to Envoy's `allow_missing_or_failed`). Verified claims are still forwarded, so it should be paired with an Authorization policy for enforcement. From 4b5c0eeeea9b56e2d3d06dfd64eaa12d72db7687 Mon Sep 17 00:00:00 2001 From: Louis Burda Date: Mon, 29 Jun 2026 13:53:19 +0200 Subject: [PATCH 4/5] Remove release-notes/current.yaml Signed-off-by: Louis Burda --- release-notes/current.yaml | 62 -------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 release-notes/current.yaml diff --git a/release-notes/current.yaml b/release-notes/current.yaml deleted file mode 100644 index bbba293e03..0000000000 --- a/release-notes/current.yaml +++ /dev/null @@ -1,62 +0,0 @@ -date: Pending - -# Changes that are expected to cause an incompatibility with previous versions, such as deletions or modifications to existing APIs. -breaking changes: | - Moved the Gateway API safe-upgrades ValidatingAdmissionPolicy resources out of the CRD bundle and into the gateway-helm chart templates so tools such as Flux no longer treat them as CRDs. During upgrades, two upgrade cases require action: (1) if you install Gateway API CRDs separately (e.g. with the gateway-crds-helm chart and `helm install --skip-crds`), the safe-upgrades ValidatingAdmissionPolicy and its binding are now rendered by the gateway-helm chart, so add Helm ownership metadata (the `meta.helm.sh/release-name`, `meta.helm.sh/release-namespace` annotations and the `app.kubernetes.io/managed-by=Helm` label) to the `ValidatingAdmissionPolicy/safe-upgrades.gateway.networking.k8s.io` and `ValidatingAdmissionPolicyBinding/safe-upgrades.gateway.networking.k8s.io` resources before upgrading so Helm can manage them (see https://gateway.envoyproxy.io/v1.8/install/install-helm/#installing-crds-separately); (2) if Gateway API CRDs and safe upgrade policy resources are managed by your cloud provider (or any other mechanism outside this chart), note that `--skip-crds` does not skip chart-templated resources, so disable rendering of the safe-upgrades ValidatingAdmissionPolicy by setting `crds.gatewayAPI.safeUpgradePolicy.enabled=false` (see https://gateway.envoyproxy.io/v1.8/install/install-helm/#clusters-with-compatible-provider-managed-gateway-api-crds). - Lua EnvoyExtensionPolicies are now disabled by default. Use the new `enableLua` field in `extensionApis` to explicitly enable them. The `disableLua` field is deprecated and will be removed in a future release. - The `XRateLimitHeadersOptionDisabled` constant in `BackendTrafficPolicy` now correctly holds the value `"Off"` to match the CRD enum (previously `"Disabled"`). Since `"Disabled"` was never a valid CRD enum value and would have been rejected by the API server, no existing manifests are affected. - `SecurityPolicy` `spec.apiKeyAuth.extractFrom` admission validation is now stricter: the list must contain at least one entry, each entry must specify exactly one of `headers`, `params`, or `cookies`, and source names must be non-empty. SecurityPolicies that previously applied with an empty or ambiguous `extractFrom` (which produced no usable API key sources) will now be rejected and must be corrected before upgrading. - -# Updates addressing vulnerabilities, security flaws, or compliance requirements. -security updates: | - Fixed xDS server authentication bypass in GatewayNamespaceMode, adding Unary Interceptor and validating SotW requests. - -# New features or capabilities added in this release. -new features: | - Added support for authorization path match. - Added a `requestBody` field to the HTTP active health checker in `BackendTrafficPolicy`, allowing a request body payload to be sent during HTTP health checking. The field requires the health check `method` to be `POST` or `PUT`. - Added a `xdsNACKTotal` metric to track the number of NACKs received from Envoy, labeled by node ID and resource type URL. A NACK is a DiscoveryRequest carrying an ErrorDetail, indicating that Envoy rejected the last config update. This metric can be used to alert on config issues causing xDS rejections. - Added the `autoSNIFromEndpointHostname` TLS setting to Backends, allowing the SNI value sent to the backend to be automatically derived from the backend endpoint hostname instead of using a fixed SNI value. - Added support for `filterContext` field in Lua EnvoyExtensionPolicy, allowing shared Lua scripts to be parameterized per route via `request_handle:filterContext()`. - Added support for `ListenerSet` as a `targetRef` kind in `ClientTrafficPolicy`, allowing client traffic settings to be applied to a named group of listeners without a gateway-wide policy. - Added a `fromMetadata` field to global rate limit `limit` in `BackendTrafficPolicy`, allowing the limit value to be sourced from per-request dynamic metadata (e.g. set by an upstream ext_proc), falling back to the static `requests`/`unit` when the metadata is absent. - Added support for disabling the `crds` dependency on the gateway-helm chart via `crds.enabled` variable. - Added `spec.clientIPDetection.xForwardedFor.disableXForwardedForAppend` to ClientTrafficPolicy to disable Envoy's automatic X-Forwarded-For append behavior when using XFF-based client IP detection. - Added a `failedRefetchDuration` field to JWT providers in `SecurityPolicy`, configuring how long Envoy waits before re-fetching the JWKS after a failed fetch. If not specified, Envoy's default of 1 second is used. - Added an `ignoreFailure` field to JWT in `SecurityPolicy`, allowing a request to pass the JWT filter even when its JWT is missing or invalid (maps to Envoy's `allow_missing_or_failed`). Verified claims are still forwarded, so it should be paired with an Authorization policy for enforcement. - -bug fixes: | - Fixed HTTPRoute, GRPCRoute, TLSRoute, TCPRoute, and UDPRoute Accepted condition being set to False when an attached listener is not programmed due to a missing TLS certificate ref; listener programmed state is now correctly separated from route acceptance. - Fixed Backend TLS `alpnProtocols: []` to disable upstream ALPN instead of inheriting EnvoyProxy BackendTLS defaults. - Fixed the xDS server in GatewayNamespaceMode serving a stale certificate after cert-manager rotation by re-reading the cert from disk on every TLS handshake. - Fixed controller panic when processing backend tls settings. - Fixed Gateway status reporting `Programmed: False` with reason `AddressNotAssigned` when the Envoy LoadBalancer service has no load balancer ingress (e.g. bare-metal clusters without a load balancer controller) but has addresses configured in `spec.externalIPs`, such as via an EnvoyProxy service patch. The external IPs are now used as a fallback for the Gateway status addresses. - Fixed egctl config commands hanging when Envoy pod port-forwarding fails. - Fixed BackendTLSPolicy selection to prefer section name over wildcard match on the same backend. - Fixed missing deprecated field warning in ClientTrafficPolicy and SecurityPolicy. - Fixed ClientTrafficPolicy TLS cipher validation rejecting supported IANA/RFC cipher suite names. - Fixed the EnvoyProxy resource not allowing IPv6 ranges in loadBalancerSourceRanges when configuring the envoy service. - Fixed Kubernetes provider namespace-scoped watches to always include the controller namespace so Envoy Gateway can read its own infrastructure resources. - Fixed TLS secrets with non-canonical PEM formatting (e.g. unusual line endings) being passed verbatim to Envoy, which could cause BoringSSL errors such as `BAD_END_LINE`. Cert and key PEM data is now re-encoded to a canonical form before being delivered as xDS resources. - Fixed `MaxStreamDuration` not being set on `CommonHttpProtocolOptions` for non-route cluster. - Fixed `egctl x status all`/`xroute`/`xpolicy` failing when a Gateway API CRD (e.g. TCPRoute) is not installed in the cluster; missing CRDs are now skipped silently, or reported on stderr with `-v`. - Fixed Kubernetes Service and ServiceImport `appProtocol` values `kubernetes.io/ws` and `kubernetes.io/wss` to force HTTP/1.1 upstream connections instead of negotiating HTTP/2, avoiding compatibility issues with WebSocket backends that do not support RFC 8441 extended CONNECT. - Fixed an `ExternalName` Service referenced as a route backend producing an invalid xDS cluster (with an empty address) that failed IR validation and stalled config delivery for the whole snapshot. `ExternalName` Services are now explicitly rejected as backends with a `ResolvedRefs: False` route condition; use an Envoy Gateway `Backend` resource with an FQDN endpoint instead. - Fixed API key auth credential ordering to avoid unnecessary xDS updates. - Fixed the generated `install.yaml` creating a duplicate ValidatingAdmissionPolicy and its binding which caused `kustomize build` to fail with a duplicate resource error. - Fixed ListenerSet hostname conflict resolution to apply listener precedence: Gateway-owned listeners win over ListenerSet listeners, and among ListenerSet listeners the first in processing order wins. Conflicted ListenerSet listeners now correctly report Accepted=False with the conflict reason. The Gateway's AttachedListenerSets count now only reflects ListenerSets with at least one accepted listener. - Fixed upstream PROXY protocol clusters to preserve generated HTTP protocol options, including auto protocol detection for Backend TLS. - Fixed EnvoyGateway config hot-reload to apply defaults before validation, so validators always run against a fully-defaulted struct on both the startup and reload paths. - Fixed deduplicate CA certificates in ClientTrafficPolicy mTLS. - Fixed HTTPRoute per-retry timeout (derived from `rule.timeouts.backendRequest`) not being applied when no retry backoff was configured. - Fixed shared global rate limit rules with a `cost` field not working as expected. - -# Enhancements that improve performance. -performance improvements: | - -# Deprecated features or APIs. -deprecations: | - The `disableLua` field in `extensionApis` is deprecated in favor of `enableLua`. - -# Other notable changes not covered by the above sections. -Other changes: | From 552ed70c4e0ff9cb69a4a20b47e21ea2ae246133 Mon Sep 17 00:00:00 2001 From: Louis Burda Date: Tue, 30 Jun 2026 19:00:50 +0000 Subject: [PATCH 5/5] Rename feature file Signed-off-by: Louis Burda --- ...y.md => 9313-support-ignorefailure-to-tolerate-invalid-jwt.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename release-notes/current/new_features/{9251-support-settting-allow_missing_or_invalid-jwt_authn-requirement-for-securitypolicy.md => 9313-support-ignorefailure-to-tolerate-invalid-jwt.md} (100%) diff --git a/release-notes/current/new_features/9251-support-settting-allow_missing_or_invalid-jwt_authn-requirement-for-securitypolicy.md b/release-notes/current/new_features/9313-support-ignorefailure-to-tolerate-invalid-jwt.md similarity index 100% rename from release-notes/current/new_features/9251-support-settting-allow_missing_or_invalid-jwt_authn-requirement-for-securitypolicy.md rename to release-notes/current/new_features/9313-support-ignorefailure-to-tolerate-invalid-jwt.md