From 74f8b68af8d8b1f40406b3cf3fd238ebcc225c79 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Wed, 30 Sep 2020 11:49:06 +0200 Subject: [PATCH] proxy: ask for pb_proxy scope, set authorization header if enabled --- proxy/go.mod | 10 ++- proxy/go.sum | 14 ++-- proxy/pkg/proxy/claims.go | 30 +++++++++ proxy/pkg/proxy/oauthproxy.go | 114 +++++++++++++++------------------ proxy/pkg/server/api.go | 1 + proxy/pkg/server/api_bundle.go | 6 ++ 6 files changed, 101 insertions(+), 74 deletions(-) create mode 100644 proxy/pkg/proxy/claims.go diff --git a/proxy/go.mod b/proxy/go.mod index f4bbefa841..a60f4612c7 100644 --- a/proxy/go.mod +++ b/proxy/go.mod @@ -29,12 +29,16 @@ require ( github.com/recws-org/recws v1.2.1 github.com/sirupsen/logrus v1.7.0 github.com/spf13/afero v1.4.0 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.7.1 // indirect github.com/stretchr/testify v1.6.1 go.mongodb.org/mongo-driver v1.4.1 // indirect golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect - golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect - golang.org/x/sys v0.0.0-20200922070232-aee5d888a860 // indirect - golang.org/x/tools v0.0.0-20200923053713-ba800b16d873 // indirect + golang.org/x/net v0.0.0-20200927032502-5d4f70055728 // indirect + golang.org/x/sys v0.0.0-20200929083018-4d22bbb62b3c // indirect + golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f // indirect gopkg.in/ini.v1 v1.61.0 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect ) diff --git a/proxy/go.sum b/proxy/go.sum index 477512cbeb..b1e467a2eb 100644 --- a/proxy/go.sum +++ b/proxy/go.sum @@ -421,8 +421,6 @@ github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -441,8 +439,6 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.3 h1:kJSsc6EXkBLgr3SphHk9w5mtjn0bjlR4JYEXKrJ45rQ= -github.com/magiconair/properties v1.8.3/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -562,8 +558,6 @@ github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= @@ -767,6 +761,8 @@ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200927032502-5d4f70055728 h1:5wtQIAulKU5AbLQOkjxl32UufnIOqgBX72pS0AV14H0= +golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -837,6 +833,8 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860 h1:YEu4SMq7D0cmT7CBbXfcH0NZeuChAXwsHe/9XueUO6o= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200929083018-4d22bbb62b3c h1:/h0vtH0PyU0xAoZJVcRw1k0Ng+U0JAy3QDiFmppIlIE= +golang.org/x/sys v0.0.0-20200929083018-4d22bbb62b3c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -903,8 +901,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f h1:33yHANSyO/TeglgY9rBhUpX43wtonTXoFOsMRtNB6qE= golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200923053713-ba800b16d873 h1:Q5Sq7Lt0bkn6Ax1NAraQhKRN7xxxy1LV4guxsyFHZx4= -golang.org/x/tools v0.0.0-20200923053713-ba800b16d873/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f h1:7+Nz9MyPqt2qMCTvNiRy1G0zYfkB7UCa+ayT6uVvbyI= +golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/proxy/pkg/proxy/claims.go b/proxy/pkg/proxy/claims.go new file mode 100644 index 0000000000..66bc18b9be --- /dev/null +++ b/proxy/pkg/proxy/claims.go @@ -0,0 +1,30 @@ +package proxy + +import ( + "encoding/base64" + "encoding/json" + "strings" +) + +type Claims struct { + Proxy struct { + UserAttributes map[string]string `json:"user_attributes"` + } `json:"pb_proxy"` +} + +func (c *Claims) FromIDToken(idToken string) error { + // id_token is a base64 encode ID token payload + // https://developers.google.com/accounts/docs/OAuth2Login#obtainuserinfo + jwt := strings.Split(idToken, ".") + jwtData := strings.TrimSuffix(jwt[1], "=") + b, err := base64.RawURLEncoding.DecodeString(jwtData) + if err != nil { + return err + } + + err = json.Unmarshal(b, c) + if err != nil { + return err + } + return nil +} diff --git a/proxy/pkg/proxy/oauthproxy.go b/proxy/pkg/proxy/oauthproxy.go index f56791f6b5..1590bfb5a6 100644 --- a/proxy/pkg/proxy/oauthproxy.go +++ b/proxy/pkg/proxy/oauthproxy.go @@ -70,38 +70,39 @@ type OAuthProxy struct { AuthOnlyPath string UserInfoPath string - redirectURL *url.URL // the url to receive requests at - whitelistDomains []string - provider providers.Provider - providerNameOverride string - sessionStore sessionsapi.SessionStore - ProxyPrefix string - SignInMessage string - basicAuthValidator basic.Validator - displayHtpasswdForm bool - serveMux http.Handler - SetXAuthRequest bool - PassBasicAuth bool - SetBasicAuth bool - SkipProviderButton bool - PassUserHeaders bool - BasicAuthPassword string - PassAccessToken bool - SetAuthorization bool - PassAuthorization bool - PreferEmailToUser bool - skipAuthRegex []string - skipAuthPreflight bool - skipAuthStripHeaders bool - skipJwtBearerTokens bool - mainJwtBearerVerifier *oidc.IDTokenVerifier - extraJwtBearerVerifiers []*oidc.IDTokenVerifier - compiledRegex []*regexp.Regexp - templates *template.Template - realClientIPParser ipapi.RealClientIPParser - trustedIPs *ip.NetSet - Banner string - Footer string + redirectURL *url.URL // the url to receive requests at + whitelistDomains []string + provider providers.Provider + providerNameOverride string + sessionStore sessionsapi.SessionStore + ProxyPrefix string + SignInMessage string + basicAuthValidator basic.Validator + displayHtpasswdForm bool + serveMux http.Handler + SetXAuthRequest bool + PassBasicAuth bool + SetBasicAuth bool + SkipProviderButton bool + PassUserHeaders bool + BasicAuthUserAttribute string + BasicAuthPasswordAttribute string + PassAccessToken bool + SetAuthorization bool + PassAuthorization bool + PreferEmailToUser bool + skipAuthRegex []string + skipAuthPreflight bool + skipAuthStripHeaders bool + skipJwtBearerTokens bool + mainJwtBearerVerifier *oidc.IDTokenVerifier + extraJwtBearerVerifiers []*oidc.IDTokenVerifier + compiledRegex []*regexp.Regexp + templates *template.Template + realClientIPParser ipapi.RealClientIPParser + trustedIPs *ip.NetSet + Banner string + Footer string sessionChain alice.Chain @@ -200,7 +201,6 @@ func NewOAuthProxy(opts *options.Options) (*OAuthProxy, error) { PassBasicAuth: opts.PassBasicAuth, SetBasicAuth: opts.SetBasicAuth, PassUserHeaders: opts.PassUserHeaders, - BasicAuthPassword: opts.BasicAuthPassword, PassAccessToken: opts.PassAccessToken, SetAuthorization: opts.SetAuthorization, PassAuthorization: opts.PassAuthorization, @@ -891,27 +891,6 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R // addHeadersForProxying adds the appropriate headers the request / response for proxying func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Request, session *sessionsapi.SessionState) { - if p.PassBasicAuth { - if p.PreferEmailToUser && session.Email != "" { - req.SetBasicAuth(session.Email, p.BasicAuthPassword) - req.Header["X-Forwarded-User"] = []string{session.Email} - req.Header.Del("X-Forwarded-Email") - } else { - req.SetBasicAuth(session.User, p.BasicAuthPassword) - req.Header["X-Forwarded-User"] = []string{session.User} - if session.Email != "" { - req.Header["X-Forwarded-Email"] = []string{session.Email} - } else { - req.Header.Del("X-Forwarded-Email") - } - } - if session.PreferredUsername != "" { - req.Header["X-Forwarded-Preferred-Username"] = []string{session.PreferredUsername} - } else { - req.Header.Del("X-Forwarded-Preferred-Username") - } - } - if p.PassUserHeaders { if p.PreferEmailToUser && session.Email != "" { req.Header["X-Forwarded-User"] = []string{session.Email} @@ -970,16 +949,25 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req } } if p.SetBasicAuth { - switch { - case p.PreferEmailToUser && session.Email != "": - authVal := b64.StdEncoding.EncodeToString([]byte(session.Email + ":" + p.BasicAuthPassword)) - rw.Header().Set("Authorization", "Basic "+authVal) - case session.User != "": - authVal := b64.StdEncoding.EncodeToString([]byte(session.User + ":" + p.BasicAuthPassword)) - rw.Header().Set("Authorization", "Basic "+authVal) - default: - rw.Header().Del("Authorization") + claims := Claims{} + err := claims.FromIDToken(session.IDToken) + if err != nil { + log.WithError(err).Warning("Failed to parse IDToken") } + + userAttributes := claims.Proxy.UserAttributes + var ok bool + var password string + if password, ok = userAttributes[p.BasicAuthPasswordAttribute]; !ok { + password = "" + } + // Check if we should use email or a custom attribute as username + var username string + if username, ok = userAttributes[p.BasicAuthUserAttribute]; !ok { + username = session.Email + } + authVal := b64.StdEncoding.EncodeToString([]byte(username + ":" + password)) + req.Header["Authorization"] = []string{fmt.Sprintf("Basic %s", authVal)} } if p.SetAuthorization { if session.IDToken != "" { diff --git a/proxy/pkg/server/api.go b/proxy/pkg/server/api.go index 88d5af0ba6..d3fa4ab12a 100644 --- a/proxy/pkg/server/api.go +++ b/proxy/pkg/server/api.go @@ -51,6 +51,7 @@ func getCommonOptions() *options.Options { commonOpts.Logging.SilencePing = true commonOpts.SetXAuthRequest = true commonOpts.SetAuthorization = false + commonOpts.Scope = "openid email profile pb_proxy" return commonOpts } diff --git a/proxy/pkg/server/api_bundle.go b/proxy/pkg/server/api_bundle.go index 81b2ca0aee..d4421a98a9 100644 --- a/proxy/pkg/server/api_bundle.go +++ b/proxy/pkg/server/api_bundle.go @@ -125,6 +125,12 @@ func (pb *providerBundle) Build(provider *models.ProxyOutpostConfig) { os.Exit(1) } + if *&provider.BasicAuthEnabled { + oauthproxy.SetBasicAuth = true + oauthproxy.BasicAuthUserAttribute = provider.BasicAuthUserAttribute + oauthproxy.BasicAuthPasswordAttribute = provider.BasicAuthPasswordAttribute + } + pb.proxy = oauthproxy pb.Handler = chain.Then(oauthproxy) }