Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
Back to tool/Back to issues
#3519·oauth2-proxy

[Feature]: Gateway API `ExternalAuth` Compatibility

Author: codelloCreated Aug 23, 2026Updated Sep 14, 2026
Labelsenhancement

Motivation

In a recent release Cilium has gained support for the ExternalAuth filter (defined in GEP-1494). The filter is a forward-auth style filter for the HTTPRoute resource in the Kubernetes Gateway API. In Cilium it is implemented via Envoy's ext_authz. Today I tried setting up the filter with oauth2-proxy.

Unfortunately I wasn't able to achieve the setup I wanted: I wanted a single oauth2-proxy that I could then use via the ExternalAuth filter on multiple HTTPRoutes.

Helm values.yaml & Example HTTPRoute
yaml
replicaCount: 2
config:
  existingSecret: oauth2-proxy
  configFile: |-
    redirect_url = "https://auth.public.host/oauth2/callback"
    skip_provider_button = true
    email_domains = [ "*" ]
    cookie_domains = [ ".public.host" ]
    whitelist_domains = [ ".public.host" ]

    # Envoy pods run with hostNetwork, so the source IP range is the node IP range
    reverse_proxy = true
    real_client_ip_header = "X-Forwarded-For"
    trusted_proxy_ips = [ "10.0.10.0/24" ]

    # Logging
    silence_ping_logging = true

alphaConfig:
  enabled: true
  configData:
    providers:
      - id: keycloak
        provider: keycloak-oidc
        clientID: ${OAUTH2_PROXY_CLIENT_ID}
        clientSecret: ${OAUTH2_PROXY_CLIENT_SECRET}
        oidcConfig:
          issuerURL: https://login.public.host/realms/myrealm
    upstreamConfig:
      upstreams:
        - id: ok
          path: /
          static: true
    injectResponseHeaders:
      - name: X-Auth-Request-User
        values: [claimSource: { claim: user }]
      - name: X-Auth-Request-Email
        values: [claimSource: { claim: email }]

gatewayApi:
  enabled: true
  hostnames: [auth.public.host]
  gatewayRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: public
yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: { name: myroute }
spec:
  parentRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: internal
  hostnames: [my.internal.host]
  rules:
    - backendRefs:
        - name: mysvc
          port: 8080
      filters:
        - type: ExternalAuth
          externalAuth:
            protocol: HTTP
            backendRef:
              name: oauth2-proxy
              namespace: ingress
              port: 443
            http:
              path: /oauth2/auth
              allowedHeaders:
                - Cookie
                - X-Forwarded-Proto
                - X-Forwarded-Host
                - X-Forwarded-Uri
              allowedResponseHeaders:
                - X-Auth-Request-User
                - X-Auth-Request-Email
                - Set-Cookie

The Problem

The core problem is that Cilium's ExternalAuth implementation does not send X-Forwarded-* headers (or any other headers that oauth2-proxy understands). The solution in other issues (e.g. #2832, #862) was to modify the Envoy config to include such headers. But in this case the problem is not necessarily implementation-specific. The Gateway API defines the ExternalAuth interface and does not currently include a way to configure the required headers. Although my specific use case involves Cilium, this feature request is related more to compatibility with the Gateway API design.

Possible solution

Context

Gateway API defines the ExternalAuth filter here. The definition includes:

The following headers must always be sent to the authorization server, regardless of this setting: Host, Method, Path, Content-Length, Authorization

This means that the gateway will send basically the same request it received to oauth2-proxy (including the original Host, HTTP method and request path). No headers added.

This is almost compatible with oauth2-proxy when running with reverse_proxy = false. In this mode oauth2-proxy will successfully trigger the OAuth flow and prompt the user to authenticate. The IdP will successfully redirect to the redirect-uri of oauth2-proxy. But now oauth2-proxy assumes (because of reverse_proxy = false) that the final target resource lives on the same host as itself. In practice this means:

  1. User requests GET host.example.com/foobar
  2. ExternalAuth sends GET host.example.com/foobar to oauth2-proxy
  3. oauth2-proxy responds with 302 redirect to IdP
  4. ExternalAuth receives 302 and forwards it to the user.
  5. The user completes authentication and is redirected to oauth2-proxy.example.com/oauth2/callback
  6. oauth2-proxy finishes the OAuth token exchange and redirects to oauth2-proxy.example.com/foobar

Except for the very last step this all works as expected.

Proposed Solution

I traced the redirection behavior down to appDirector.GetRedirect and from there to

https://github.com/oauth2-proxy/oauth2-proxy/blob/81ff034fe2ff3246e670c694b02e3267d1ae46bc/pkg/app/redirect/getters.go#L56-L73

In reverse_proxy = false mode, the redirect URI will be set to the path of the request. I propose the following change:

diff
 if redirect == "" {
-     redirect = req.URL.RequestURI()
+     redirect = req.URL.String()
 }

This includes the full original request URL in the OAuth state and would allow oauth2-proxy to redirect to the correct URL for ExternalAuth. Including a full URL is consistent with the X-Forwarded-Uri header which can also include a full URL.

Additional Thoughts:

  • I haven't thought about the security implications of the proposed change. It may make sense to guard this behind a config flag. This flag would basically enable oauth2-proxy to be a proxy for arbitrary hosts, not just one single host (related: #749)
  • Also from a security POV, maybe fmt.Sprintf("%s://%s%s", req.URL.Scheme, req.URL.Host, req.URL. RequestURI()) may make more sense compared to req.URL.String()
  • An alternative solution to my specific problem could of course also be to open a feature request with Cilium and/or the Gateway API. I decided against that for now because I feel that it's unlikely the ExternalAuth will change in that way.
  • A different alternative could be to run one oauth2-proxy per HTTPRoute (or rather: per host). This is impractical in my case because of a relatively large number of hosts.

Provider

None

Source: oauth2-proxy/oauth2-proxy

View original on GitHubView discussion on GitHub