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

Authentication middleware #593

Closed
sebastienfr opened this issue Aug 3, 2016 · 47 comments
Closed

Authentication middleware #593

sebastienfr opened this issue Aug 3, 2016 · 47 comments
Labels

Comments

@sebastienfr
Copy link

A global authentication middleware being able to redirect incoming request to a remote authentication service which could transform initial requests before they are forwarded to internal services would be a great improvement for traefik.

Use case is to be able to validate an OAuth token, add a JWT in request header with login information and forward it the right service. If OAuth token is not valid, the request is rejected with a 403 immediately.

Issue links to #30 and #391.

@emilevauge
Copy link
Member

@sebastienfr: are you planning to work on this one (just to be sure)?

@sebastienfr
Copy link
Author

@emilevauge: for the moment it looks like quite a big work and it's not compatible with our current project deadline. But we are working on a similar feature that could maybe be adapted to be more generic and put back in traefik at a later stage.

@guybrush
Copy link

i am using https://github.com/bitly/oauth2_proxy for this (it also works in combination with traefik)

@ViViDboarder
Copy link
Contributor

@guybrush that was a great recommendation! I also found beevelop/nginx-basic-auth works great to do the same with Basic Auth.

@ViViDboarder ViViDboarder mentioned this issue Aug 16, 2016
3 tasks
@sebastienfr
Copy link
Author

@guybrush thanks i'll have a look.

@diegooliveira
Copy link
Contributor

Hello, I did a pull request #764 to address this issue.

Please review, any change, code style or design problem, please let know.

@xeor
Copy link

xeor commented Feb 2, 2017

Will this ever happen now that #764 and it's v2 #1030 is closed? The alternatives with using something like https://github.com/bitly/oauth2_proxy in addition to træfik, works. But is far from optimal, at least when it comes to multi-domain setups (oauth2_proxy needs 1 instance per domain).

@diegooliveira
Copy link
Contributor

@xeor The PR is closed but there are happening a talk to have a more sound design. Take a look in #1030

@emilevauge
Copy link
Member

You are welcome to give your feedback on the proposal: Authentication middleware proposal

@dstroot
Copy link

dstroot commented Feb 25, 2017

Does anyone have an example of how to use Traefik and https://github.com/bitly/oauth2_proxy together they would be willing to share? I have found a couple examples using NGINX for ingress but no examples using Traefik. Although it's encouraging to hear it can be done.

Here's an NGINX example that is quite clear: https://eng.fromatob.com/post/2017/02/lets-encrypt-oauth-2-and-kubernetes-ingress/ .

@guybrush
Copy link

guybrush commented Feb 27, 2017

@dstroot is there something specific you are struggling with? the blog-article you posted has all the relevant information in it.

maybe you can post your compose.yml? it is pretty straight forward to use the oauth2_proxy, here is an example (this is something i typed just now without testing):

your request will go like that: your_service.docker.localhost:80 → traefik:80 → oauth2_proxy:8090 → your_service:80

version: '2'
services:
  traefik:
    image: traefik
    command: -c /dev/null --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  your_service:
    image: emilevauge/whoami
    
  oauth2_proxy:
    image: busybox
    labels:
      - "traefik.backend=whoami"
      - "traefik.frontend.rule=Host:your_service.docker.localhost"
      - "traefik.port="8090"
    volumes:
      # get the binary from github and mount it into the container:
      - ./oauth2_proxy:/oauth2_proxy
      # also mount certificates:
      - /etc/ssl/certs:/etc/ssl/certs:ro
    command: >
      /bin/sh -c '
      /oauth2_proxy
      -client-id="<id>"
      -client-secret="<secret>"
      -scope="api"
      -provider="gitlab"
      "-cookie-secret=$$(head -c 16 /dev/urandom | base64)"
      -cookie-secure=true
      -cookie-expire=24h0m0s
      -cookie-refresh=1h0m0s
      -email-domain=*
      -login-url="https://your.gitlab.com/oauth/authorize"
      -redeem-url="https://your.gitlab.com/oauth/token"
      -validate-url="https://your.gitlab.com/api/v3/user"
      -redirect-url="http://your_service.docker.localhost"
      -upstream="http://your_service:80"
      -pass-host-header=true
      -http-address="http://:8090"'

@ldez ldez added the kind/enhancement a new or improved feature. label Apr 22, 2017
@kminehart
Copy link

kminehart commented Apr 23, 2017

Those of you mentioning oauth2_proxy, I'm working on a helm chart for it. I have it working with a Gitlab OAuth provider.

Be warned though. oauth2_proxy really doesn't lend itself to being used on Kubernetes. So a traefik-integrated solution would be much better.

If we could specify something in the Ingress similar to the nginx ingress controller then I think that would be ideal.

Nginx example:
https://github.com/kubernetes/ingress/blob/master/examples/auth/external-auth/nginx/README.md

@emilevauge
Copy link
Member

We may also have a look at https://github.com/hsluoyz/casbin

@pbarker
Copy link

pbarker commented May 17, 2017

I think this should be solved here #1370, similar to how Kong does it

@ldez ldez added kind/proposal a proposal that needs to be discussed. and removed kind/enhancement a new or improved feature. labels Jun 1, 2017
@ekozan
Copy link

ekozan commented Jun 21, 2017

somebody work on this one ?

@tcolgate
Copy link
Contributor

I think the issue with the above compose example is that is means you can't load balance accross multiple back ends, which limits how useful traefik is in that setup. It probably makes more sense to have oauth2_proxy in front of traefik there, and have it upstream to traefik (though if you are using acme, that isn't going to work well at all).
It would be nice if traefik supported the nginx approach where you just call out to oauth2_proxy for auth.

@Vlaaaaaaad
Copy link

Any updates on this?

@ekozan
Copy link

ekozan commented Sep 21, 2017

done ;) check the 1.4 changelog

@elliotwms
Copy link

Fixed in #2132

@Vlaaaaaaad
Copy link

If this is done should the issue not be closed? Is there still work to be done on this?

@tcolgate
Copy link
Contributor

tcolgate commented Nov 1, 2017

I'm not sure how practical the current approach is.

Consider https://github.com/bitly/oauth2_proxy . The nginx confiugration for auth_request there shows that
/oauth/ is excluded from the auth (so that it can be used for the signin flow.
failure of auth has a 401 page configured that the passes you on to /oauth/sign_in to begin the login flow.

It's not obvious from the traefik docs as to how this can be achieved via endpoint configuration. I'm not sure that it can, but if it can, then setup should be documented.

@Vlaaaaaaad
Copy link

Oh, got it. Support for simple header check auth is in, but not support for oauth which requires a more involved workflow. Did I understand this correctly?

@tcolgate
Copy link
Contributor

tcolgate commented Nov 1, 2017 via email

@oresot
Copy link

oresot commented Feb 5, 2018

@tcolgate I see that solving the part where requests to https://oauth2/auth need to be bypassed by traefik, but there are still things missing, like passing the redirect URL (the one that the client actually requested) to the auth backend.

@lukasmrtvy
Copy link

lukasmrtvy commented Sep 6, 2018

@XA21X
Copy link

XA21X commented Sep 11, 2018

I've been trying to get this to work:

Traefik -------> Internal Websites
  |       |
  |       -----> Keycloak
  |
  |                 ^
  |                 | (Goes back through Traefik!)
  V                 |
OpenResty --> (OpenID Connect)

and I've finally succeeded :D

@oresot (and @tcolgate) This is closer to the topology in your lower diagram but it still uses middleware. This should be more easily usable in a cluster environment i.e. Kubernetes. FYI, I'm only using docker-compose.

Since #2162, I've been able to run Keycloak behind Traefik by selectively enabling forward auth for the internal entrypoints. Although I would prefer a blacklist instead i.e. protect everything by default like #3562.

I'm using the zmartzone/lua-resty-openidc OpenResty module which provides OpenID Connect Relying Party functionality to use as the forward auth server. But instead of serving or proxying to secured content, I return 202 Accepted once authentication (and authorisation) succeeds.

OAuth2 2.0 requires the use of redirect URLs (so the cookie can be specifically set for the relying party's domain) but the request reaches lua-resty-openidc with the contents in the X-Forwarded-URI header (due to Traefik auth) which wasn't able to be handled by the module. I implemented this (and other related bits) in my fork: XA21X/lua-resty-openidc.

This probably isn't the cleanest solution but I already wanted to use OpenResty to implement custom authorisation logic (restricting domains according to LDAP groups etc). Before this, I've also gotten Traefik's forward auth to work with Luzifer/nginx-sso using similar modifications.

@bootc
Copy link

bootc commented Oct 21, 2018

@XA21X care to share your OpenResty configuration? It sounds like a great replacement for the abandoned oauth2_proxy but my Lua just isn't up to scratch!

@XA21X
Copy link

XA21X commented Oct 30, 2018

@bootc @C-Duv @tcheronneau

Apologies for the delay, it took surprisingly long to extract the relevant bits and reproduce the results. I've set up a live demo with an isolated realm that uses the exact code I've published if anyone's interested.

@bootc
Copy link

bootc commented Oct 30, 2018

@XA21X That's awesome, thanks for posting that!

@stedaniels
Copy link

@XA21X have you raised an issue/submitted a PR to the upstream https://github.com/zmartzone/lua-resty-openidc?

@jgoux
Copy link

jgoux commented Dec 14, 2018

@XA21X Thanks for the example.

I was currently trying keycloak with nginx and lua-resty-openidc but wanted a great GUI to manage my traffic, this is on point!

As @stedaniels suggested, it would be great to propose the changes to lua-resty-openidc so we can have it officially supported. 🌟

@XA21X
Copy link

XA21X commented Dec 17, 2018

@stedaniels @jgoux I considered that when I first got it working but then I realised that Traefik's HTTP header seem to be non-standard, so even if support is added to lua-resty-openidc, it would probably need to be an opt-in feature. I'll need to write some unit tests before submitting the PR.

In the long term, it would be nice if Traefik includes such a feature, although lua-resty-openidc with the right configuration surprisingly seems to be able to meet most of the needs mentioned in this issue.

@Svarto
Copy link

Svarto commented Dec 22, 2018

@XA21X thanks a lot for sharing your setup, it's exactly what I've been looking for to better protect some of my self hosted apps.

Is there any way to get slightly "beginner friendly" instructions...I struggle to see what I need to adapt to place it behind my own Traefik reverse proxy SSL terminator?

I see that you have a docker compose spinning in a nginx and modified openresty containers with the Traefik labels for nginx declaring it as an Authentication point - however I assume that I will need to update the openresty-docker/lua/traefik-idc/auth.lua file - do I need to change anything except the discovery, client_id and client_secret variables? And how do I know what the discovery URL should be?

Sorry for the many questions, just really happy to find a working example of this! Thanks a lot for your work!

@LorenzBischof
Copy link

LorenzBischof commented Apr 1, 2019

@XA21X Can you specify what changes were needed to get Luzifer/nginx-sso working? I tried setting it up, but could not get it to redirect from /auth to /login when the authentication fails.

@andig
Copy link

andig commented Apr 1, 2019

For benefit of other readers, I'm using thomseddon/traefik-forward-auth which has an open PR to support selective configuration and more authentication providers.

@travisghansen
Copy link

travisghansen commented Apr 16, 2019

I've been working on a much more dynamic forward auth server and am ready for some early testers if anyone is interested. It features:

  • works with any proxy server (traefik, nginx, ambassador, etc) that supports forward/external auth
  • works with any openid provider (I've been testing predominantly with keycloak but it should be agnostic)
  • only requires 1 installation to service any number of providers/configurations
  • passes tokens to the backing service via headers
  • automatically refreshes tokens

@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.

@travisghansen
Copy link

OK, just landed support for custom assertions and infrastructure to support provider-specific userinfo with oauth2 plugins.

This does 2 broad things:

  1. With oidc you can do custom assertions on any data in the id_token or the userinfo data.
  2. With support for gathering provider specific userinfo on oauth2 it affords the same control (for the providers that have been implemented).

So far I've only implemented userinfo for github in oauth2 which means you can for example do assertions based on teams or organizations or any other user data (user ID, email, etc, etc).

Proper logging is now in place and cleaned up. Secrets should not be getting logged now unless you've turned on debug level logging.

Server-side sessions expiration has been implemented for oauth2/oidc. This allows for expiring sessions to identity providers which provide non-expiring tokens (github for example from what I can tell).

Userinfo expiration has been implemented as well (meaning after X period of time it is considered stale and must be re-retrieved). When used with userinfo assertions this becomes a powerful combination. You can ensure continued validation of users over a long period of time. With github as an example if an individual's teams or orgs changes and no longer meets the criteria then access will be shut down. Same would apply for identity providers which for example include groups in the userinfo data. If someone no longer belongs to a required group their access would be removed.

I'm sure there are some edge-cases being badly handled right now, but generally I now consider the project to be in a beta state. I'm successfully using it with some simple apps currently including the pipeline idea for both ldap and oidc on a single service and actively using both authentication schemes simultaneously. In addition, oidc/oauth2 with the 'right' configuration are even usable with single-page applications (several of my test services are SPAs).

As always, I still need to document several things much better than they are but generally all options have been documented crudely.

Feedback encouraged and welcomed!

@travisghansen
Copy link

I've landed some better documentation and explanation of custom assertions now (still not great, but it's a start). A relatively detailed example using the oauth2 plugin with github with traefik in kubernetes is in the HOWTO.md.

I've implemented a jwt plugin as well which facilitates authenticating jwt Bearer tokens.

At this point I've knocked everything off the list I really care to for the 0.1.0 release.

@travisghansen
Copy link

Added a few more items:

  • custom assertions for the jwt plugin
  • forward auth plugin
  • pipeline circuit breakers (pcb)

The forward auth plugin allows for making the project act as a proxy to a separate/existing forward auth service. This could be helpful if you want to use the pipeline concept but already have an existing forward auth service working as you need.

pcb allows for either stoping the pipeline after a particular plugin based on the nature of the request or response details (ie: Authorization header present for example) or skiping specific plugins in the pipeline based on the similar logic. For example if you supported both jwt and ldap you could skip the jwt plugin when the request does not have an Authorization: Bearer header but force it to stop (specifically in the case of failure) when it does rather than continue on to ldap, etc.

A common use-case would be you have an existing set of users in ldap for example. You could support Basic (via ldap directly) Bearer via jwt and Cookie sessions via oidc issue'ing jwt tokens and the oidc workflow from something like keycloak (backed by the same ldap store). Each returning appropriate response codes to the browser/client based on whatever is provided in the request.

@brijchavda
Copy link

i am using https://github.com/bitly/oauth2_proxy for this (it also works in combination with traefik)

Are you using it with github oauth?

@travisghansen
Copy link

@brijchavda if it’s of any use my auth server works with github and has custom userinfo logic allowing you to run assertions on user details such as team membership, username, etc and any of the data can be passed along to the backend as headers if/as desired. In addition you can allow requests based off of pretty much anything such as path, verb, host, headers, etc.

@ddtmachado
Copy link
Contributor

Hey everyone,

Since we now have a plugin system available, lately we're encouraging contributors to write authentication middleware as plugins.

It reduces the complexity of our code base, give more control to contributors and helps us to keep focused on other important subjects.

For those reasons I'm closing this, but as usual if you feel this is not right or want to discuss a specific proposal that does not fit as a plugin or an enhancement for the ForwardAuth don't hesitate to reach us in the comments or submit a new issue.

@traefik traefik locked and limited conversation to collaborators Jul 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests