Replicating nginx auth_request

1. The problem I’m having:

I want to use Caddy to reverse proxy my Authentik instance, an oauth2proxy instance and my App. I don’t want to use Authentik Outpost, i am using Authentik OAuth2/OpenID provider.

I need the Authorization JWT header set by Authentik to be passed through to the app.

If i put [caddy] → [oauth2proxy] > [My App] inline this works ok using oauth2proxy upstream configuration, Caddy just passes everything through as does oauth2proxy which ensures the Authorization header is set.

But i dont really want this configuration. I would prefer [caddy] > [My App] so i have tried to use the long form of the forward_auth configuration (as documented here forward_auth (Caddyfile directive) — Caddy Documentation) but i can’t get it to work. I’m expecting to be able to get it to work like nginx auth_request module, essentially doing a sub request to oauth2proxy (with oauth2proxy just acting like an auth server and not proxying anything) which responds with 200 or 401 if authenticated or not based on the presence of a cookie header.

The reverse_proxy for httpbin isn’t actually making the subrequest to check the http://oauth2-proxy-httpbin:4180/oauth2/auth to get a response 200/400 response, its just sending the request directly to httpbin (which gives 404 for the /oauth2/auth path anyway) , any ideas why not or if there is a better way of achieving this?

2. Error messages and/or full log output:

3. Caddy version:

2.7.6

4. How I installed and ran Caddy:

docker image

a. System environment:

linux

b. Command:

n/a

c. Service/unit/compose file:

Docker

d. My complete Caddy config:

auth.domain.tld {
    reverse_proxy authentik-server-1:9000 {
        import trusted_proxy_list
    }
    import proxy_log
}

httpbin.domain.tld {
    handle /oauth2/* {
        reverse_proxy http://oauth2-proxy-httpbin:4180 {
            import trusted_proxy_list
        }
    }
    handle {
        reverse_proxy http://httpbin:8080 {
            method GET
            rewrite http://oauth2-proxy-httpbin:4180/oauth2/auth
            header_up X-Forwarded-Method {method}
            header_up X-Forwarded-Uri {uri}
            header_up Cookie _oauth2_proxy_0={http.request.cookie._oauth2_proxy_0};_oauth2_proxy_1={http.request.cookie._oauth2_proxy_1};_oauth2_proxy_1

            @good status 2xx
            handle_response @good {
                request_header rp.header.Remote-User {http.reverse_proxy.header.X-Authentik-Username}
                request_header rp.header.Remote-Email {http.reverse_proxy.header.X-Authentik-Email}
            }

            @bad status 4xx
            handle_response @bad {
                redir https://httpbin.domain.tld/oauth2/start
            }

            import trusted_proxy_list
        }
    }
    import proxy_log
}

5. Links to relevant resources:

I found an example of the long form in the authelia documentation which helped resolve the issue - Caddy - Integration - Authelia

1 Like

What exactly did you change to solve your problem?

2 Likes

Here is the config for reference

(proxy_log) {
    log {
        format console
        level debug
    }
}
(trusted_proxy_list) {
    trusted_proxies 10.0.0.0/8 172.16.0.0/16 192.168.0.0/24 fc00::/7
}

auth.domain.tld {
    reverse_proxy authentik-server-1:9000 {
        import trusted_proxy_list
    }
    import proxy_log
}

httpbin.domain.tld {
    handle /oauth2/* {
        reverse_proxy http://oauth2-proxy-httpbin:4180 {
            import trusted_proxy_list
        }
    }
    handle {
        reverse_proxy http://oauth2-proxy-httpbin:4180 {
            method GET
            rewrite /oauth2/auth
            header_up X-Forwarded-Method {method}
            header_up X-Forwarded-Uri {uri}
            header_up Cookie _oauth2_proxy_0={http.request.cookie._oauth2_proxy_0};_oauth2_proxy_1={http.request.cookie._oauth2_proxy_1};_oauth2_proxy_1

            @good status 2xx
            handle_response @good {
                request_header Authorization {http.reverse_proxy.header.Authorization}
            }

            @bad status 4xx
            handle_response @bad {
                redir https://httpbin.domain.tld/oauth2/start
            }

            import trusted_proxy_list
        }
        reverse_proxy http://httpbin:8080 {
            import trusted_proxy_list
        }
    }
    import proxy_log
}

I suggest you use trusted_proxies global options instead of the snippet, it enables new features (Authelia docs need updating to cover this). See Global options (Caddyfile) — Caddy Documentation

Also you could just use forward_auth at this point to significantly simplify your config:

httpbin.domain.tld {
	handle /oauth2/* {
		reverse_proxy oauth2-proxy-httpbin:4180
	}
	handle {
		forward_auth oauth2-proxy-httpbin:4180 {
			uri /oauth2/auth
			copy_headers Authorization

			@bad status 4xx
			handle_response @bad {
				redir https://httpbin.domain.tld/oauth2/start
			}
		}
		reverse_proxy http://httpbin:8080
	}
	import proxy_log
}
2 Likes

Hi,

I am very interested in this. I was using Authentik with its integrated proxy and forward_auth in caddy, and now I switched to Zitadel which doesn’t have this so I need to use oauth2-proxy.

On caddy side, it looks like the config for forward_auth is very similar to what I was doing with Authentik, thanks a lot for this config @francislavoie .

On oauth2-proxy side though, I’m a bit lost. The only example configuration I’ve found is not using forward_auth but is proxying the app : Add Auth to Any App with OAuth2 Proxy | Okta Developer

Could you please share how you configured oauth2-proxy for forward_auth @nbx3 ?

What oauth2 manager are you using ?

Thanks in advance for any answer.

So firstly in order to support the oauth2proxy auth check at /oauth2/auth, i think you still need the long form of forward_auth as i posted above to ensure the below header is sent upstream in the auth check.

The oauth2proxy config can be kept pretty simple, here’s a snippet of mine, i have added additional scopes in authentik in order to support the requirements of my app

http_address="0.0.0.0:4180"
email_domains="*"
reverse_proxy="false"
upstreams = [ "file:///dev/null" ]
set_xauthrequest="true"
set_authorization_header="true"
cookie_name="_oauth2_proxy"
whitelist_domains=[".domain.tld"] 
pass_host_header="true"
pass_user_headers="true"
pass_authorization_header="true"
pass_access_token="true"
skip_jwt_bearer_tokens="true"
skip_provider_button="true"
skip_auth_preflight="false"

# authentik provider
provider="oidc"
client_secret="yyy"
client_id="httpbin"
redirect_url="https://httpbin.domain.tld/oauth2/callback"
oidc_issuer_url="https://auth.domain.tld/application/o/httpbin/"

cookie_secret="zzz"
cookie_secure="false"
cookie_domains=[".domain.tld"] 

skip_oidc_discovery="false"
scope="openid profile email groups myapp"

No, not needed at all. Caddy passes through all headers transparently. See reverse_proxy (Caddyfile directive) — Caddy Documentation (which applies to forward_auth as well). The whole point of forward_auth is that it passes through headers, including auth headers like Cookie.

Thanks for your answer. I’ve set up oauth2-proxy this way (I am using env variables)

      - OAUTH2_PROXY_EMAIL_DOMAINS=*
      - OAUTH2_PROXY_PROVIDER=oidc
      - OAUTH2_PROXY_REDIRECT_URL=https://radarr.${DOMAIN}/oauth2/callback
      - OAUTH2_PROXY_OIDC_ISSUER_URL=https://auth.${DOMAIN}/application/o/radarr/
      - OAUTH2_PROXY_CLIENT_ID=${RADARR_CLIENT_ID}
      - OAUTH2_PROXY_CLIENT_SECRET=${RADARR_CLIENT_SECRET}
      - OAUTH2_PROXY_SCOPE="openid profile email groups"
      - OAUTH2_PROXY_COOKIE_SECRET=${OAUTH2_PROXY_COOKIE_SECRET}

I’m not sure if this is enough, I’ve tried to let as much default settings as possible.

Additionnaly, I’ve set caddy this way:

radarr.example.com {
        @except not path /api*
        reverse_proxy radarr:7878
        reverse_proxy /oauth2/* oauth2-radarr:4180
        forward_auth @except oauth2-radarr:4180 {
                copy_headers Authorization
                uri /oauth2/auth
        }
}

But I am having this error:
[2024/01/08 21:49:59] [main.go:60] ERROR: Failed to initialise OAuth2 Proxy: error initialising provider: could not create provider data: error building OIDC ProviderVerifier: could not get verifier builder: error while discovery OIDC configuration: failed to discover OIDC configuration: unexpected status "404": {"code":5, "message":"Not Found"}
It looks like the discovery URL of Zitadel does not work.

Do you know how I can set this up ?
How did you set up the app in Zitadel ?
What Redirect URLs and Post Logout URIs did you set in Zitadel ?

Thanks in advance for any answer and have a great day.

EDIT : I’ve been told on Zitadel discord that the discovery url is not specific to an application and is of the form https://auth.com/. So I’ve just set OAUTH2_PROXY_OIDC_ISSUER_URL=https://auth.${DOMAIN}. Now, when trying to access the site, I have an error “Unauthorized”. I’m not sure why.

My config is now the following:

      - OAUTH2_PROXY_EMAIL_DOMAINS=*
      - OAUTH2_PROXY_PROVIDER=oidc
      - OAUTH2_PROXY_REDIRECT_URL=https://radarr.${DOMAIN}/oauth2/callback
      - OAUTH2_PROXY_OIDC_ISSUER_URL=https://auth.${DOMAIN}
      - OAUTH2_PROXY_WHITELIST_DOMAIN=.${DOMAIN}
      - OAUTH2_PROXY_CLIENT_ID=${RADARR_CLIENT_ID}
      - OAUTH2_PROXY_CLIENT_SECRET=${RADARR_CLIENT_SECRET}
      - OAUTH2_PROXY_SCOPE="openid profile email groups"
      - OAUTH2_PROXY_COOKIE_SECRET=${OAUTH2_PROXY_COOKIE_SECRET}

I see from your oauth2-config that you have added multiple tokens (set_xauthrequest,…) why that ? Is that mandatory ?

I’ve no experience with Zitadel sorry, all i can suggest is to check their endpoint docs at OpenID Connect Endpoints in ZITADEL | ZITADEL Docs

and oauth2proxy docs at OAuth 2.0 Proxy | ZITADEL Docs

My brain assumed you were using Zitadel, I don’t know why. Sorry about that. Just noticed it wasn’t the case at all.

What oauth2 provider are you using ? You are the only person I’ve found using forward_auth with oauth2-proxy so I’m very interested in what you did, to try to reproduce it with zitadel.

I’m using Authentik - https://goauthentik.io

My usage scenario is described above in some detail so it should give you a good idea of what i am doing including the config for caddy and oauth2 protecting a httpbin app.

The Authentik config is really very simple, all thats needed for it is an Authentik “Provider” and “Application” configured for “Auth2/OpenID”, grab the clientID and Client Secret and put them into oauth2proxy config and take the oauth2proxy callback url and put it in the Authentik provider config. With the above config that should work. I have additional configuration requirements for my apps but that will work for httpbin.

I agree that set up a proxy with Authentik is very simple, that’s what I was doing for a year. But I’ve never suceeded to make the flows and stages for my users to create their account themselves to work properly. I’ve always had way too much issues and limitations with email validations and such. That’s why I’m trying to move to Zitadel, which doesn’t integrate a proxy like Authentik does.

Which ones of the tokens you’ve added are required for httpbin ? What would be the base configuration of oauth2-proxy for forward_auth with a simple app without additional feature ?

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.