Skip to content
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

OpenID Connect authentication middleware #3216

Closed
wants to merge 6 commits into from

Conversation

EdSchouten
Copy link

@EdSchouten EdSchouten commented Apr 20, 2018

What does this PR do?

This PR adds OpenID Connect authentication middleware to Traefik. This middleware can be configured either through the configuration file or by adding annotations to ingress objects in Kubernetes.

Motivation

We are currently in the process of designing a Kubernetes cluster that is going to be used by a large number of engineers within a single organisation. These engineers will be able to spawn their own (or their team's) web applications, under the condition that they are adequately secured. For this reason, we want to enforce the use of our company's OpenID Connect identity provider where easily possible.

Up until so far we've made use of keycloak-proxy, but this has the obvious downside that it requires every engineer to maintain their own containers for this. Especially at such a large scale, it also causes an unnecessary amount of memory/CPU overhead. It also makes it hard to enforce this policy; we'd need to check whether everyone actually has such a proxy running and ingresses actually route traffic to them, which is complex.

This is why we'd like to work towards a system where the ingress controller can already enforce the use of OpenID Connect. With this change applied, setting up OpenID Connect is simply a matter of adding the following annotations to an ingress:

ingress.kubernetes.io/auth-type: oidc
ingress.kubernetes.io/auth-oidc-discovery-url: https://login-service.example.com/
ingress.kubernetes.io/auth-oidc-scopes: openid profile email
ingress.kubernetes.io/auth-secret: some-kubernetes-secret-containing-client-id-and-secret

After enforcing authentication, requests are forwarded with some X-Auth-* headers set, just like keycloak-proxy does.

More

  • Added/updated tests
  • Added/updated documentation

Additional Notes

This patch is still horribly incomplete. The reason I'm sending this PR is because I'm interested in knowing whether the Traefik project is interested in a change like this. If so, I'm very willing to bash this change into a decent shape for getting it upstream.

Ed Schouten added 2 commits April 18, 2018 21:53
This library will be used by the OIDC authentication middleware.
@emilevauge
Copy link
Member

@EdSchouten Thanks for your contribution.
Few months ago, we have added a way to forward authentication to a delegate server.
We would love to help you use that feature instead, as we tend to avoid complex authentication implementations into Traefik.
Could you join us on Slack ?

@emilevauge
Copy link
Member

emilevauge commented May 31, 2018

@EdSchouten Sorry but we still need to validate the need of integrating OpenID into Traefik. Indeed, we would prefer to use our forward authentication feature, even if we need to make it evolve. I suggest to continue the discussion on Slack (channel #dev), and stop pushing new code for now ;)

@emilevauge
Copy link
Member

Hi @EdSchouten
After several internal discussions, we prefer to not implement OIDC right into Traefik.
There are too many different (and used) authentication protocols, we cannot maintain all implementations in Traefik, and we would rather not have only few available.
For now, we then prefer to delegate authentication to external servers, using authentication forward, or in the case of OpenID, using for example Keycloak. We will work an adding documentation soon on how OpenID can be managed with Traefik and an external authentication server.
Finally, we cannot disclose on this for now, but we are working on another way to let Traefik manage more authentication protocols natively. We hope to talk about this soon ;)
We are sorry to close this one for now, thanks again for your great contribution!

@styk-tv
Copy link

styk-tv commented Aug 8, 2018

@emilevauge quite possible you misunderstood the original question. @EdSchouten is not asking for the server functionality but for termination on forward-proxy level in usecases of public, confidential and bearer-only. you are mentioning keycloack but that is exactly what @EdSchouten is asking for here, and yes we would love to use keycloak.

ingress.kubernetes.io/auth-type: oidc
ingress.kubernetes.io/auth-oidc-discovery-url: https://login-service.example.com/
ingress.kubernetes.io/auth-oidc-scopes: openid profile email
ingress.kubernetes.io/auth-secret: some-kubernetes-secret-containing-client-id-and-secret

however what is missing is above functionality similar to Swagger's OpenApi 3 or Kong you need to be able to inspect jwt token and admit control to back-ends based on group membership for example. that would be totally kick-ass and would buy you tons of additional attention from the serious side of the universe. (love your product btw)

https://swagger.io/docs/specification/authentication/openid-connect-discovery/

@EdSchouten
Copy link
Author

EdSchouten commented Aug 11, 2018

Hi Peter,

Yes, passing the JWT to the backend process is something that I'd love to add. The only problem is, I'm not enough of an expert to know what's the proper way for doing that. I know there is Authentication: Bearer ...., but that is supposed to hold an OAuth2 access key, not a JWT, right?

Edit: oh, wait. What you suggesting is something different. Namely: accepting or rejecting requests based on fields in the ID token. That is something that I haven't added, because our internal OIDC server is already responsible for granting/rejecting access on a client ID basis.

@styk-tv
Copy link

styk-tv commented Aug 12, 2018

Hi @EdSchouten

Well, on the container level it would be nice to have unsecured api's or ui's and having a gateway in front that can do admission control based on token signature/expiry validity and then actual group membership. But I understand why @emilevauge doesn't want to pollute main code. For now we can oauth2_proxy, Apache modules, Kong (as ingress) or Swagger.

BTW: in OpenID Connect which is what Keycloak is using, Oauth2 token is JWT. You can easily prove that by looking for TWO dots in the token that separates the message into 3 parts (header,payload,signature). Just paste the token into a window at jwt.io and you can inspect it like any other JWT.

@bsloeserwij
Copy link

Great PR! This is exactly what we are looking for. We want to ensure all Traefik backends can only be reached after having been authenticated with Keycloak (or similar) without increasing the complexity of our stack by having to add intermediate proxies etc. We currently connect each application server/ HTTP server directly with Keycloak using adapters or OIDC modules, which is manageable if you have 5 of them, but becomes a big challenge if you have +100 of them. So, +1 on this functionality.

@styk-tv
Copy link

styk-tv commented Sep 12, 2018

@bsloeserwij well, i talked to Traefik, Nginx, Haproxy, OpenApi Swagger and Auth0 about building an K8S Ingress controller to support this. There are side containers you can use for that like Nokia's Lua_oidc. 'm sure one of them will come up with a great way to handle this. Kong is an overkill for me but already does that.

Personally, I went in the direction of GraphQL and protection of Neo4j & Dgraph queries based on JWT token principal's group membership. Having fun with that so far and left kube ingress behind for the time being. For the record +1 on this functionality. If I can't do it in ingress i will do this with OIDC on the stateless API level (GraphQL) and queries can take advantage of confirmed principal from token. As long as I can do stateless horizontal scaling and utilize K8S service mesh

@Starefossen
Copy link
Contributor

Starefossen commented Sep 12, 2018 via email

@travisghansen
Copy link

@EdSchouten I've been fighting some of the same short-comings you described. I decided to create a project that let's you run a single instance to handle multiple configurations. I'm at the state code-wise where I'm looking for some early testing (I've only been using keycload). Any interest from anyone on this thread? It's also proxy agnostic (ie: it works with any proxy that support forward/external auth including nginx and ambassador).

@travisghansen
Copy link

The documentation is a little rough around the edges but I've now tested with 2 different providers (keycloak, and self-hosted gitlab) successfully and have working configurations for both nginx and traefik (cluster annotation examples coming in the next day or 2). It's probably ready for some alpha level feedback. Opened travisghansen/external-auth-server#1 on the project directly to continue the conversation there.

https://github.com/travisghansen/oauth-external-auth-server

@travisghansen
Copy link

As a follow up to the comments above, I adjusted the scope of the project to be a generic external auth server. I went ahead and implemented 6 plugins (so far) including oauth2, OpenID Connect, ldap, htpasswd, request param, and request header. In addition, the plugins can be put into a pipeline within a single configuration. For example you can enable both basic auth (via htpasswd or ldap) and allow for oauth2/openid to protect the same service.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants