Ext Auth with Istio 1.9.1 and Oauth-proxy redirect issue once authenticated

Hi,

I have setup a test environment with oauth2-proxy ,dex and istio 1.9.1 to test the ext_authz functionality.

All code can be found here : istio-on-kind/mesh.yaml at 4178bad9614de33d1b215d7c0cb7fe32ad08b839 · primeroz/istio-on-kind · GitHub

This is almost working other than the final redirect to the original URL Requested

  • setup the cluster as described in README.md
  • visit http://podinfo.127.0.0.1.nip.io
  • get redirected to dex by istio ingress due to oauth2-proxy and the request no having the cookie
  • authenticate with admin@example.com and password
  • client get redirected to oauth2-proxy.127.0.0.1/oauth2/callback and the client is presented an authenticated page
  • here i would expect to get redirected to the original url http://podinfo.127.0.0.1.nip.io but instead you only get the oauth2-proxy Authenticated Page
  • visit the URL http://podinfo.127.0.0.1.nip.io again
  • client has the cookie, oauth2-proxy return 200 to istio and the request is forwarded to the usptream workload

Is it actually possible to get it to work all in one shot ?

Configuration Snippets

MeshConfig

    extensionProviders: 
    - name: "oauth2-proxy"
      envoyExtAuthzHttp:
        service: "oauth2-proxy.dex.svc.cluster.local"
        port: "4180" # The default port used by oauth2-proxy.
        #includeHeadersInCheck: ["authorization", "cookie"]  # headers sent to the oauth2-proxy in the check request.
        includeHeadersInCheck: # headers sent to the oauth2-proxy in the check request.
            # https://github.com/oauth2-proxy/oauth2-proxy/issues/350#issuecomment-576949334
            - "cookie"
            - "x-forwarded-access-token"
            - "x-forwarded-user"
            - "x-forwarded-email"
            - "authorization"
            - "x-forwarded-proto"
            - "proxy-authorization"
            - "user-agent"
            - "x-forwarded-host"
            - "from"
            - "x-forwarded-for"
            - "accept"
        headersToUpstreamOnAllow: ["authorization", "path", "x-auth-request-user", "x-auth-request-email", "x-auth-request-access-token", "x-auth-request-user-groups"] # headers sent to backend application when request is allowed.
        headersToDownstreamOnDeny: ["content-type", "set-cookie"] # headers sent back to the client when request is denied.

OAUTH proxy is configured as

          - --http-address=0.0.0.0:4180
          - --email-domain="example.com"
          - --cookie-refresh=1h
          - --cookie-secure=false # Set to false for test environment only
          - --set-xauthrequest=true # X-Auth-Request-User, X-Auth-Request-Email, X-Auth-Request-Preferred-Username, X-Auth-Request-Groups
          - --pass-access-token=true # X-Auth-Request-Access-Token, must first enable --set-xauthrequest
          - --set-authorization-header=true # Authorization: Bearer <JWT>
          - --pass-authorization-header=true # pass OIDC IDToken to upstream via Authorization Bearer header
          - --pass-host-header=true # pass the request Host Header to upstream
          - --pass-access-token=true # pass OAuth access_token to upstream via X-Forwarded-Access-Token header. When used with --set-xauthrequest this adds the X-Auth-Request-Access-Token header to the response
          - --upstream=static://200
          - --reverse-proxy
          #- --whitelist-domain=".127.0.0.1.nip.io"
          - --whitelist-domain=".127.0.0.1.nip.io"
          - --cookie-domain=".127.0.0.1.nip.io"
          - --cookie-name=_oauth2_proxy
          - --cookie-samesite=lax
          #- --scope="openid groups profile email"
          - --provider=oidc
          - --oidc-issuer-url=http://dex.127.0.0.1.nip.io
          - --redirect-url=http://oauth2-proxy.127.0.0.1.nip.io/oauth2/callback
          - --skip-provider-button=true

and the AuthPolicy

---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: podinfo
  #namespace: dev
  # Does it need to be in the ingrassgateway namespace?
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: CUSTOM
  provider:
    name: "oauth2-proxy"
  rules:
    - to:
      - operation:
          hosts:
          - "podinfo.127.0.0.1.nip.io"
1 Like

For reference:

The issue was with setting - --redirect-url=http://oauth2-proxy.127.0.0.1.nip.io/oauth2/callback

without that the callback will be to the original HOST/oauth2/callback which envoy will forward to oauth2-proxy for validation and then back to the original HOST

cc @YangminZhu regarding to external authz.

@YangminZhu I’m seeing a similar issue attempting to configure oauth2-proxy as an external authorization provider: The original request to an authaurizationpolicy-protected service gets successfully redirected to the oauth2-proxy, I’m able to authenticate, and the redirect goes back to the oauth2-proxy.

The problem is, oauth2-proxy requires one of the following to be configured on the request in order to correctly redirect the original request back to the original host:

  • rd querysting parameter
  • X-Auth-Request-Redirect header
  • X-Forwarded-(Proto|Host|Uri) headers (when ReverseProxy mode is enabled)
  • X-Forwarded-(Proto|Host) if Uri has the ProxyPath (i.e. /oauth2/*)
  • X-Forwarded-Uri direct URI path (when ReverseProxy mode is enabled)
  • req.URL.RequestURI if not under the ProxyPath (i.e. /oauth2/*)

Clearly these headers do not exist on the ext_authz initiated requests to the oauth2-proxy, and therefore the oauth2-proxy does not redirect. Are you aware of any solutions to configure the redirect correctly?

oauth2-proxy config:

provider = "oidc"
whitelist_domain = ".internal.com"
cookie_domain = ".internal.com"
redirect_url = "https://oauth2-proxy-url.com/oauth2/callback"
oidc_issuer_url = "https://x.okta.com/oauth2/default"
email_domains = [
    "*"
]
upstreams = [ "static://200" ]
pass_access_token = true
skip_provider_button = true
set_authorization_header = true
pass_authorization_header = true
pass_host_header = true
set_xauthrequest = true
skip_jwt_bearer_tokens = false
reverse_proxy = true
cookie_name = "_oauth2_proxy"
cookie_samesite = "lax"

standard_logging = true
auth_logging = true
request_logging = true
session-cookie-minimal = true

Mesh config:

  meshConfig:
# Add the following content to define the external authorizers.
extensionProviders:
  - name: "oauth2-proxy"
    envoyExtAuthzHttp:
      service: "oauth2-proxy.auth.svc.cluster.local"
      port: "80" # The default port used by oauth2-proxy.
      includeHeadersInCheck: # headers sent to the oauth2-proxy in the check request.  https://github.com/oauth2-proxy/oauth2-proxy/issues/350#issuecomment-576949334
        - "cookie"
        - "x-forwarded-access-token"
        - "x-forwarded-user"
        - "x-forwarded-email"
        - "authorization"
        - "x-forwarded-proto"
        - "proxy-authorization"
        - "user-agent"
        - "x-forwarded-host"
        - "from"
        - "x-forwarded-for"
        - "x-forwarded-uri"
        - "x-auth-request-redirect"
        - "accept"
      headersToUpstreamOnAllow: ["authorization", "path", "x-auth-request-user", "x-forwarded-uri", "x-auth-request-redirect", "x-auth-request-email", "x-auth-request-access-token", "x-auth-request-user-groups"] # headers sent to backend application when request is allowed.
      headersToDownstreamOnDeny: ["content-type", "set-cookie"] # headers sent back to the client when request is denied.
1 Like

The following did the trick for me (note the includeAdditionalHeadersInCheck entry):

    extensionProviders:
    - envoyExtAuthzHttp:
        headersToDownstreamOnDeny:
        - content-type
        - set-cookie
        headersToUpstreamOnAllow:
        - authorization
        - cookie
        includeAdditionalHeadersInCheck:
          X-Auth-Request-Redirect: 'https://%REQ(Host)%'
        includeHeadersInCheck:
        - authorization
        - cookie
        port: 4180
        service: oauth2-proxy.istio-system.svc.cluster.local
      name: oauth2-proxy

This effectively adds another header, X-Auth-Request-Redirect, to the request that gets forwarded to oauth2-proxy. The value of this header is extracted from the pre-existing Host header.

@djfinnoy thanks for the pointer - I’ve tried updating to istio 1.10 and configuring includeAdditionalHeadersInCheck as you’ve suggested. I’m seeing that the X-Auth-Request-Redirect header does get attached on authentication, but the HOST is set to the oauth2-proxy url, not the original HOST.

If possible, would you mind sharing your oauth2-proxy deployment configuration and istio AuthorizationPolicy?

I posted my setup in a separate thread: Adding headers for oauth2-proxy redirect

Check the oauth2-proxy logs now that you’ve got a redirect header, if there are any issues with your redirect URL it will be posted there (note that protocol and quotation marks are required, eg 'https://%REQ(Host)%').

Out of curiosity, how are you seeing the headers sent to oauth2-proxy? They don’t show up in my in my browser.

@djfinnoy Thanks for sharing your config! I’ve ensured I’m using quotes in my Istio Operator definition (as you’ve suggested).

One thing I noticed is that you’re not configuring oauth2-proxy’s redirect_url (which I am currently doing in my config). Is this intentional? If so, how do you define the accepted redirect_urls on your Oauth application (looks like you’re using github)?

Ok - I finally have this working - it seems to have been an issue with the version of oauth2-proxy I was using (originally v5.x). Since updating to v7, the redirect works correctly with istio 1.10

glad you got it working.

I am in fact using --redirect-url, but it appears I accidentally cropped that out of my code example.
without it, the auth flow fails; tested this with both GitHub and Google as the oauth provider.

Thanks for this tip. I noticed the redirect was still landing at the root, for example starting the flow by visiting https://bookinfo.example.com/productpage results in being redirected to https://bookinfo.example.com after authentication.

The following configuration includes the scheme, authority (host:port), path and query parameters. I don’t think it includes anchor fragments though.

includeAdditionalHeadersInCheck:
  X-Auth-Request-Redirect: '%REQ(x-forwarded-proto)%://%REQ(:authority)%%REQ(:path)%'
3 Likes

Hey Jeff,

Did you need a separate gateway and/or virutalservice for your oauth2-proxy? Is your OIDC’s redirect uri pointing to your oauth2-proxy’s domain?

Yes, oauth2-proxy needs to be accessible by the end-user’s browser for the redirect to hand off the token to oauth2-proxy. I’ve used both separate Gateway resources with distinct hosts and also one Gateway host with VirtualService resources to route paths for the IDP and external authorizer.

FYI, this gets complex pretty fast. There are pitfalls with making sure httpsRedirect: true keeps working as expected while also preserving compatibility with cert-manager. I’m available for contract work if you’d like help. https://openinfrastructure.co/