envoyproxy / envoyproxy/gateway
Unable to authenticate traffic to a specific route through security policy when a general gateway-level security policy exists
- Dominant language
- Go
- Stars
- 3k
- Forks
- 864
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 140
Description
### Description:
I am using envoy gateway in namespace mode and I have various frontends and backends at different subdomains which the single gateway handles traffic across these subdomains.
I have two security policy - `policy a` at gateway level and `policy b` targeting a specific route to a particular backend. Both policies share the same `oidc` and `jwt` configurations but `policy b` has an additional `authorisation` configuration to perform additional check against jwt claims.
When `policy b` is present, the frontend is unable to send request to that particular backend with 302 Found (envoy log: oauth missing credentials). When trying to open the backend url on the browser directly, 401 error with `oauth flow error` message was shown and envoy logs showing `CSRF token validation failed`. Even after modifying `policy b` to be exactly the same as `policy a`, same error exists. After `policy b` is deleted, the frontend can successfully send request to the protected backend with no issue.
This behaviour appears related to, but not fully explained by, this issue: https://github.com/envoyproxy/gateway/issues/7205. The symptoms are similar (unexpected auth redirects and CSRF-related failures), but the setup differs due to the shared OIDC/JWT sessions and the additional authorization check in a route-specific policy.
### Repro steps:
**policy a:**
```yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
name: gateway-security-policy
namespace: {{ .Values.namespace }}
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: internal-gateway
cors:
allowOrigins:
{{- range .Values.instances }}
- "https://{{ .subdomain }}.{{ $.Values.namespace }}.{{ $.Values.domain }}"
{{- end }}
allowMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowHeaders:
- Accept
- Authorization
- Content-Type
- X-CSRF-Token
exposeHeaders:
- Link
allowCredentials: true
oidc:
provider:
issuer: https://auth.{{ .Values.domain }}
clientIDRef:
name: internal-gateway-secret
namespace: {{ .Values.namespace }}
clientSecret:
name: internal-gateway-secret
namespace: {{ .Values.namespace }}
redirectURL: https://auth.{{ .Values.namespace }}.{{ .Values.domain }}/oauth2/callback
scopes:
- openid
- profile
- offline_access
- urn:zitadel:iam:org:project:{{ .Values.gateway.project_id }}:roles
logoutPath: /logout
cookieDomain: "{{ .Values.namespace }}.{{ .Values.domain }}"
forwardAccessToken: true
jwt:
providers:
- name: zitadel
remoteJWKS:
uri: https://auth.{{ .Values.domain }}/oauth/v2/keys
claimToHeaders:
- claim: sub
header: X-Zitadel-Sub
- claim: urn:zitadel:iam:org:project:{{ .Values.gateway.project_id }}:roles
header: X-Zitadel-Roles
```
**policy b (extra config):**
```yaml
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: xxx
authorization:
defaultAction: Deny
rules:
- action: Allow
name: allow-user
principal:
jwt:
claims:
- name: sub
valueType: StringArray
values:
- 'xxx'
provider: zitadel
- action: Allow
name: allow-roles
principal:
jwt:
claims:
- name: urn:zitadel:iam:org:project:xxx:roles
valueType: StringArray
values:
- admin
provider: zitadel
```
### Environment:
> gateway-helm 1.7.1
### Logs:
**302 Found (oauth missing credentials)**
> [pod/internal-gateway-65b697df8b-r8g2m/envoy] {":authority":","bytes_received":0,"bytes_sent":0,"connection_termination_details":null,"downstream_local_address":"10.244.2.217:10080","downstream_remote_address":"10.226.0.6:20089","duration":0,"method":"GET","protocol":"HTTP/1.1","requested_server_name":null,"response_code":302,"response_code_details":"oauth.missing_credentials","response_flags":"-","route_name":"i","start_time":"2026-04-01T14:37:09.644Z","upstream_cluster":"httproute//rule/0","upstream_host":null,"upstream_local_address":null,"upstream_transport_failure_reason":null,"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0","x-envoy-origin-path":"/","x-envoy-upstream-service-time":null,"x-forwarded-for":"","x-request-id":"f2a86c40-f883-4991-b795-dd9fce2ec3cb"}
**401 unauthorised (CSRF token validation failed)**
> [pod/internal-gateway-65b697df8b-r8g2m/envoy] [2026-04-01 14:38:40.746][14][warning][oauth2] [source/extensions/filters/http/oauth2/filter.cc:1390] [Tags: "ConnectionId":"141","StreamId":"4191718425724896979"] Responding with 401 Unauthorized. Cause: CSRF token validation failed
> [pod/internal-gateway-65b697df8b-r8g2m/envoy] {":authority":"","bytes_received":0,"bytes_sent":18,"connection_termination_details":null,"downstream_local_address":"10.244.2.217:10080","downstream_remote_address":"10.226.0.6:44793","duration":0,"method":"GET","protocol":"HTTP/1.1","requested_server_name":null,"response_code":401,"response_code_details":"CSRF_token_validation_failed","response_flags":"-","route_name":"httproute//rule/0/match/0/","start_time":"2026-04-01T14:38:40.746Z","upstream_cluster":null,"upstream_host":null,"upstream_local_address":null,"upstream_transport_failure_reason":null,"user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0","x-envoy-origin-path":"/oauth2/callback?code=some-code","x-envoy-upstream-service-time":null,"x-forwarded-for":"","x-request-id":"e9d381a9-52c0-44f6-85a3-c3d54cc034ac"}
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.