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

Add forward authentication option #1972

Merged
merged 4 commits into from
Aug 25, 2017
Merged

Conversation

drampelt
Copy link
Contributor

@drampelt drampelt commented Aug 19, 2017

Description

This implements forward authentication, much like in ngx_http_auth_request_module.

Based on the discussion in #1030 and Proposal Doc, it's clear we needed to start with something simple and then build on it later. This implementation is a simplified version of @vladshub's implementation in #1030, removing the request and response body header/query parameter manipulation.

When forward auth is enabled, the request is first forwarded to the specified auth server. If the auth server responds with a 2XX status, the original request is performed. Otherwise, the response from the auth server is returned.

Next steps

If everyone agrees this is a good starting point, I think the next step would be either copying or renaming response headers from the auth server as request headers sent to the backend.

From what I can tell that seems to be the main use-case, the auth server provides authenticated user information in response headers and then the backend application uses that information to automatically authenticate the user. See oauth2_proxy with nginx as an example of this. I believe this would bring it to feature parity with ngx_http_auth_request_module.

See this commit for a potential implementation of header mapping.

Of course I'm open to discussion about how to proceed and interested in hearing other ideas.

@emilevauge
Copy link
Member

Wow, great job @drampelt !
Design LGTM :)

Copy link

@StevenACoffman StevenACoffman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvement!

Copy link
Member

@emilevauge emilevauge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @drampelt, great job!
I made few comments though.

auth/forward.go Outdated
return
}

defer forwardResponse.Body.Close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move this line after the block:

if readError != nil {
...
}
defer forwardResponse.Body.Close()

Indeed, if readError is not nil, maybe the body will be empty, then the defered Close() would lead to a panic.

types/types.go Outdated
@@ -314,6 +315,11 @@ type Digest struct {
UsersFile string
}

// Forward authentication
type Forward struct {
Address string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be great to add the possibility to connect in TLS (with an self-signed cert).

Copy link
Contributor

@nmengin nmengin Aug 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm agree with @emilevauge for the TLS connection.
Indeed, for example, I tried to test the PR by connecting Traefik to a docker_auth server but this server allows only HTTPS connections...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I agree that would be useful. Unfortunately I'm not too familiar with Go yet and not really sure how to go about doing that - would anyone else be interested in implementing that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem @drampelt.
We can do the modifications for your 😉

Copy link
Contributor

@ldez ldez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick review.

auth/forward.go Outdated
func Forward(forward *types.Forward, w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
client := http.Client{}

forwardReq, err := http.NewRequest("GET", forward.Address, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"GET" -> http.methodGet

auth/forward.go Outdated
return
}

if forwardResponse.StatusCode < 200 || forwardResponse.StatusCode >= 300 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you replace:

  • 200 by http.StatusOK
  • 300 by http.StatusMultipleChoices

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe the limit can be 400 instead of 300?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose 300 to allow the auth server to redirect to a login page if necessary. For example if doing Google sign in, the auth server would need to redirect the user to Google by returning a 302.

"github.com/abbot/go-http-auth"
goauth "github.com/abbot/go-http-auth"

"github.com/containous/traefik/auth"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you sort imports?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, could you clarify what order they should be in? I'm not too familiar with Go yet and gofmt seems to think that is fine.

@emilevauge
Copy link
Member

@drampelt The code freeze for 1.4 will happen in 2 days (August 25th). Do you think you will be able to fix your PR in the meantime ?

@drampelt
Copy link
Contributor Author

drampelt commented Aug 24, 2017

Thanks for the reviews, I have a fairly busy week but I'll see if I can get it fixed up by tomorrow.

@drampelt
Copy link
Contributor Author

Fixed most of the issues I knew how to.

Copy link
Contributor

@timoreimann timoreimann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marathon part LGTM.

Copy link
Contributor

@nmengin nmengin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks for this really useful PR.
LGTM 👏 👏

Copy link
Member

@emilevauge emilevauge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done @drampelt and @nmengin 👏
LGTM

Copy link
Contributor

@ldez ldez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@traefiker traefiker merged commit 52b69fb into traefik:master Aug 25, 2017
@ldez ldez added the kind/enhancement a new or improved feature. label Aug 28, 2017
@RC1140
Copy link

RC1140 commented Sep 18, 2017

Hi
Any idea how to use this, I grabbed the latest RC and made the config changes as per the doc update but I never get redirected.

I used github as an example to test

defaultEntryPoints = ["http"]

[entryPoints]
    [entryPoints.http]
    address = ":80"
      [entryPoints.http.auth.forward]
      address = "http://github.com/login/oauth/authorize"

[web]
address = ":8081"

[file]

[backends]
  [backends.backend1]
    [backends.backend1.servers.server1]
       url = "http://localhost:8000"

[frontends]
  [frontends.frontend1]
      backend = "backend1"
      passHostHeader = true
      [frontends.frontend1.routes.example]
          rule = "Host:localhost"

If I try to hit localhost i simply hit my local server on port 8000 with no auth request.

@ldez
Copy link
Contributor

ldez commented Sep 18, 2017

@RC1140 Thanks for your interest in Traefik 😃

Please discuss this in :

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

Successfully merging this pull request may close these issues.

8 participants