New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support traefik forwardAuth #762
Conversation
This PR is missing tests and docs. I'll have a look at it the coming days. |
Glad to see this :] Seems like you're adding the absolute URL, have you tried if that allows for the "auth host mode" style? Small detail on the new endpoint. It's called auth or start, but doesn't behave like start. An alternative approach I can think of is to add a config option so we can modify
|
How I understand your comment in the issue, oauth2_proxy handles the requests in an "auth host mode". From a clients perspective:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides two small notes it looks okey codewise.
That being said:
I prefer one word endpoints personally.
So I think @Beanow's suggestion would be cleaner:
An alternative approach I can think of is to add a config option so we can modify /oauth2/auth's behavior.
Something like --auth-endpoint-sign-in bool default false.
oauthproxy.go
Outdated
@@ -658,6 +668,9 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { | |||
p.AuthenticateOnly(rw, req) | |||
case path == p.UserInfoPath: | |||
p.UserInfo(rw, req) | |||
// originally contributed but not merged by: https://github.com/oauth2-proxy/oauth2-proxy/compare/master...maxlaverse:support_traefik?expand=1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be in-code comment, but a PR comment
oauthproxy.go
Outdated
@@ -840,6 +853,38 @@ func (p *OAuthProxy) SkipAuthProxy(rw http.ResponseWriter, req *http.Request) { | |||
p.serveMux.ServeHTTP(rw, req) | |||
} | |||
|
|||
// originally contributed but not merged by: https://github.com/oauth2-proxy/oauth2-proxy/compare/master...maxlaverse:support_traefik?expand=1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be in-code comment, but a PR comment
if !p.IsValidRedirect(redirect) { | ||
redirect = req.Header.Get("X-Forwarded-Proto") + "://" + req.Header.Get("X-Forwarded-Host") + req.Header.Get("X-Forwarded-Uri") | ||
logger.Printf("X-Forwarded-* headers combined: %s", redirect) | ||
} | ||
if req.Form.Get("rd") != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is Traefik not able to explicitly configure the redirect URL in the rd
param? For instance, this is what my nginx ingress sign-in annotation looks like:
Internal:
nginx.ingress.kubernetes.io/auth-signin: https://<REDACTED_DOMAIN>/oauth2/start?rd=https%3A%2F%2F$host$escaped_request_uri
Behind Proxy w/X-Forwarded-Host
:
nginx.ingress.kubernetes.io/auth-signin: https://<REDACTED_DOMAIN>/oauth2/start?rd=https%3A%2F%2F$http_x_forwarded_host$escaped_request_uri
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might with some custom config (just like we are able to do a lot of what this PR adds with custom traefik configs right now), but the whole goal of this PR is to simplify the process of using forward auth. having to manually set explicit url's really defeats that purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK this isn't possible in Traefik's config. (Rather nginx's config is closer to a scripting language so I'm sure we'll be back here once Caddy ships a forward auth middleware.)
There are various feature requests and future plans that might allow this, but there's also a backwards compatibility issue. Supporting X-Forwarded-*
would add compatibility for Traefik all the way down to v1.4+ (2017) if I'm not mistaken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
having to manually set explicit url's really defeats that purpose.
Do you mean statically configuring the rb=
parameter? Sure, we need to set forwardauth.address
anyway and could include ?rb=
. But unlike nginx, we don't have variables available to inject URLs dynamically per-request.
That might work for the root of a static setup, but even if the domain and path prefixes would remain static, it wouldn't support different paths. If a client requested /api/version
and instead ends up at /
after auth because of the static config, I wouldn't consider that fully supported. Especially because this isn't the expected behavior when we're using X-Auth-Request-Redirect
/ ?rb=
in other setups.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was saying the proposed idea was a bad one, so i'm not really into debating how it could be done anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed @Ornias1993, sorry for preaching to the choir. I wanted make sure I understood you correctly, and to include some more specifics for anyone else to follow along in case they're less familiar with Traefik.
oauthproxy.go
Outdated
logger.Printf("X-Auth-Request-Redirect: %s", redirect) | ||
// traefik does not send X-Auth-Request-Redirect, so give the traefik headers a try in case X-Auth-Request-Redirect is not set | ||
if !p.IsValidRedirect(redirect) { | ||
redirect = req.Header.Get("X-Forwarded-Proto") + "://" + req.Header.Get("X-Forwarded-Host") + req.Header.Get("X-Forwarded-Uri") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at this PR, it added a unified way for X-Forwarded-Host
vs Host
usage. Let's use that helper here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at this PR
What PR are you talking about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a link missing? Not sure what helper you're pointing to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Link is definitely missing 😅
util.GetRequestHost
is the function we pretty much aligned on everywhere instead of req.Host
oauthproxy.go
Outdated
OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix), | ||
AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix), | ||
UserInfoPath: fmt.Sprintf("%s/userinfo", opts.ProxyPrefix), | ||
AuthenticateOrStartPath: fmt.Sprintf("%s/auth_or_start", opts.ProxyPrefix), // originally contributed but not merged by: https://github.com/oauth2-proxy/oauth2-proxy/compare/master...maxlaverse:support_traefik?expand=1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This path structure doesn't feel elegant. I feel like a querystring parameter that triggers a fallback to the OAuthStart
handler instead of a 401 Unauthorized
in the failure case on /auth
is more elegant.
E.e /oauth2/auth?start=true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems pretty clean as well. Though again technically it isn't like start
which immediately redirects, it's more like sign_in
which either shows the various providers available or redirects when --skip-provider-button=true
.
One downside to this approach vs having a new endpoint or new config option I can imagine, is that if someone assumes this functionality is available but is running an older version where it isn't yet, you wouldn't get any warnings/errors on startup. It would silently ignore the parameter and have unexpected behavior during actual requests that need authenticating.
In that regard I think either a config option or new endpoint is more predictable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Config option for the auth endpoint would be "the best of both worlds" in this case, imho.
It would keep the endpoints elegant and keeps everything nice and tidy...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the intent to fallback to sign-in or start page? I'm tempted to lean toward start
to align with recommendations for other subrequest
recommended architectures - SignInPage
kills existing OAuth2-Proxy session cookies.
But I could be swayed, I see the utility if people like the sign-in page (I hate it, get me to my IdP 😄 )
Could be chosen based on the option querystring param proposal? ?unauthorized=(?:start|sign_in)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One downside to this approach vs having a new endpoint or new config option I can imagine, is that if someone assumes this functionality is available but is running an older version where it isn't yet, you wouldn't get any warnings/errors on startup. It would silently ignore the parameter and have unexpected behavior during actual requests that need authenticating.
This is a known issue in many areas we hope to address with restructuring our docs. With the current docs representing master, we frequently get users confused by following the for configuration options that have merges but aren't in a release yet.
So I wouldn't let this be concern for our design decision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Config option for the auth endpoint would be "the best of both worlds" in this case, imho.
It would keep the endpoints elegant and keeps everything nice and tidy...
Didn't read this closely enough -- Agreed! Config option is best to give an alternative to /oauth2/auth
failure behavior
@@ -490,6 +493,12 @@ func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error) | |||
} | |||
|
|||
redirect = req.Header.Get("X-Auth-Request-Redirect") | |||
logger.Printf("X-Auth-Request-Redirect: %s", redirect) | |||
// traefik does not send X-Auth-Request-Redirect, so give the traefik headers a try in case X-Auth-Request-Redirect is not set | |||
if !p.IsValidRedirect(redirect) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be after the rd
querystring parameter codepath since that is the more standard and currently adopted path. This ordering will lead to confusing log messages for K8s nginx ingress users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NickMeves K8s NGINX ingress with traefik is afaik not actually being the adviced way of running traefik anymore.
(as in: It is still formally "supported", but pretty much depricated)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant for other non-traefik users -- If I use K8s nginx ingress with this PR using rd
, this leads to a lot of superfluous and confusing log messages for me that aren't applicable.
I'd be tempted to say we just log what the final redirect URL is at the end once we settle on a mechanism to chose it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can traefik not inject this header? Surely it should be able to configure this? If not, can it not inject a query string parameter?
Add support for traefiks X-Forwarded headers: - X-Forwarded-Proto - X-Forwarded-Host - X-Forwarded-Uri Use them to construct a redirect uri, when X-Auth-Request-Redirect is not present or valid. Use one endpoint for auth and sign_in, which is the way thomseddon/traefik-forward-auth works. This also allows to skip the auth provider button. Fixes oauth2-proxy#46
9651678
to
716a2a8
Compare
Hi, thanks for the review. I think I've integrated all feedback. Please have a look. |
67b3782
to
940b1c1
Compare
940b1c1
to
0674c95
Compare
Are there any updates on this? I've been pushing away my Traefik v2 deployment due to the missing support for quite some time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I'm aware users of Traefik have managed to get this configured before without needing changes to OAuth2 Proxy, I left a couple of questions in place based on this
@@ -24,6 +24,7 @@ An example [oauth2-proxy.cfg]({{ site.gitweb }}/contrib/oauth2-proxy.cfg.example | |||
| ------ | ---- | ----------- | ------- | | |||
| `--acr-values` | string | optional, see [docs](https://openid.net/specs/openid-connect-eap-acr-values-1_0.html#acrValues) | `""` | | |||
| `--approval-prompt` | string | OAuth approval_prompt | `"force"` | | |||
| `--auth-endpoint-sign-in` | bool | Show sign in page, when request to /oauth2/auth is not authorized | `false` | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the difference between having this and a static endpoint configured? I believe that was why the static upstream option was implemented in the first place
@@ -490,6 +493,12 @@ func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error) | |||
} | |||
|
|||
redirect = req.Header.Get("X-Auth-Request-Redirect") | |||
logger.Printf("X-Auth-Request-Redirect: %s", redirect) | |||
// traefik does not send X-Auth-Request-Redirect, so give the traefik headers a try in case X-Auth-Request-Redirect is not set | |||
if !p.IsValidRedirect(redirect) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can traefik not inject this header? Surely it should be able to configure this? If not, can it not inject a query string parameter?
@JoelSpeed can't reply to your second review but to reply: Traefik doesn't have dynamic variables to be used in the configuration, it is proposed last year (traefik/traefik#5036) but no change happened since then. Although you can inject headers, you can't really dynamically change it. |
This pull request has been inactive for 60 days. If the pull request is still relevant please comment to re-activate the pull request. If no action is taken within 7 days, the pull request will be marked closed. |
Are there any updates on this? /unstale |
The PR is stale for more than 3 months and during this time I've used this patched build. If a new release is near, I'll probably have to migrate to Cloudflare Access, which doesn't support GitLab Groups, or stay on the patched build. Traefik upstream hasn't gotten any further on dynamic headers and although there's a plugin for |
We haven't heard responses to PR feedback and it needs a rebase. None of the maintainers are Traefik users, so we don't have subject matter expertise to push this over the edge ourselves. Truthfully, a detailed writeup of the problem and architecture with supporting documentation would be very helpful. For me personally, it's hard to judge the merit of some of these structural changes without understanding why Traefik needs them. The new header behavior guiding redirects makes sense. But I don't understand what is driving implementing a new endpoint/functionality under |
Understandable, I think I'll make a PR with just the header logic and provide a better write-up. |
#957 is created. |
#957 merged which captured most of this logic. |
Is there any chance on reconsidering the missing functionality that hasn't been captured in #957 please? While The
|
@pcneo83 Can you make a feature request issue with what we are missing so we can track any future improvements that might be needed. Otherwise, this writeup you've generated will likely get lost in this closed PR. |
Add support for traefiks X-Forwarded headers:
Use them to construct a redirect uri, when X-Auth-Request-Redirect
is not present or valid.
Use one endpoint for auth and sign_in, which is the way
thomseddon/traefik-forward-auth works.
This also allows to skip the auth provider button.
Fixes #46
Description
Motivation and Context
How Has This Been Tested?
Checklist: