Skip to content
Snippets Groups Projects
Unverified Commit 81c7892b authored by Rohith Jayawardene's avatar Rohith Jayawardene Committed by GitHub
Browse files

Upstream Authorization Cookie (#287)

- adding an option to stop the proxy from including the authorization cookies in the upstream request
parent 7179eac5
No related branches found
No related tags found
No related merge requests found
...@@ -31,6 +31,7 @@ func newDefaultConfig() *Config { ...@@ -31,6 +31,7 @@ func newDefaultConfig() *Config {
CookieAccessName: "kc-access", CookieAccessName: "kc-access",
CookieRefreshName: "kc-state", CookieRefreshName: "kc-state",
EnableAuthorizationHeader: true, EnableAuthorizationHeader: true,
EnableAuthorizationCookies: true,
EnableTokenHeader: true, EnableTokenHeader: true,
Headers: make(map[string]string), Headers: make(map[string]string),
LetsEncryptCacheDir: "./cache/", LetsEncryptCacheDir: "./cache/",
......
...@@ -134,8 +134,6 @@ type Config struct { ...@@ -134,8 +134,6 @@ type Config struct {
// Headers permits adding customs headers across the board // Headers permits adding customs headers across the board
Headers map[string]string `json:"headers" yaml:"headers" usage:"custom headers to the upstream request, key=value"` Headers map[string]string `json:"headers" yaml:"headers" usage:"custom headers to the upstream request, key=value"`
// EnableTokenHeader adds the JWT token to the upstream authentication headers
EnableTokenHeader bool `json:"enable-token-header" yaml:"enable-token-header" usage:"enables the token authentication header X-Auth-Token to upstream"`
// EnableEncryptedToken indicates the access token should be encoded // EnableEncryptedToken indicates the access token should be encoded
EnableEncryptedToken bool `json:"enable-encrypted-token" yaml:"enable-encrypted-token" usage:"enable encryption for the access tokens"` EnableEncryptedToken bool `json:"enable-encrypted-token" yaml:"enable-encrypted-token" usage:"enable encryption for the access tokens"`
// EnableLogging indicates if we should log all the requests // EnableLogging indicates if we should log all the requests
...@@ -150,8 +148,12 @@ type Config struct { ...@@ -150,8 +148,12 @@ type Config struct {
EnableRefreshTokens bool `json:"enable-refresh-tokens" yaml:"enable-refresh-tokens" usage:"enables the handling of the refresh tokens" env:"ENABLE_REFRESH_TOKEN"` EnableRefreshTokens bool `json:"enable-refresh-tokens" yaml:"enable-refresh-tokens" usage:"enables the handling of the refresh tokens" env:"ENABLE_REFRESH_TOKEN"`
// EnableLoginHandler indicates we want the login handler enabled // EnableLoginHandler indicates we want the login handler enabled
EnableLoginHandler bool `json:"enable-login-handler" yaml:"enable-login-handler" usage:"enables the handling of the refresh tokens" env:"ENABLE_LOGIN_HANDLER"` EnableLoginHandler bool `json:"enable-login-handler" yaml:"enable-login-handler" usage:"enables the handling of the refresh tokens" env:"ENABLE_LOGIN_HANDLER"`
// EnableTokenHeader adds the JWT token to the upstream authentication headers
EnableTokenHeader bool `json:"enable-token-header" yaml:"enable-token-header" usage:"enables the token authentication header X-Auth-Token to upstream"`
// EnableAuthorizationHeader indicates we should pass the authorization header // EnableAuthorizationHeader indicates we should pass the authorization header
EnableAuthorizationHeader bool `json:"enable-authorization-header" yaml:"enable-authorization-header" usage:"adds the authorization header to the proxy request"` EnableAuthorizationHeader bool `json:"enable-authorization-header" yaml:"enable-authorization-header" usage:"adds the authorization header to the proxy request" env:"ENABLE_AUTHORIZATION_HEADER"`
// EnableAuthorizationCookies indicates we should pass the authorization cookies to the upstream endpoint
EnableAuthorizationCookies bool `json:"enable-authorization-cookies" yaml:"enable-authorization-cookies" usage:"adds the authorization cookies to the uptream proxy request" env:"ENABLE_AUTHORIZATION_COOKIES"`
// EnableHTTPSRedirect indicate we should redirection http -> https // EnableHTTPSRedirect indicate we should redirection http -> https
EnableHTTPSRedirect bool `json:"enable-https-redirection" yaml:"enable-https-redirection" usage:"enable the http to https redirection on the http service"` EnableHTTPSRedirect bool `json:"enable-https-redirection" yaml:"enable-https-redirection" usage:"enable the http to https redirection on the http service"`
// EnableProfiling indicates if profiles is switched on // EnableProfiling indicates if profiles is switched on
......
...@@ -288,19 +288,19 @@ func TestCallbackURL(t *testing.T) { ...@@ -288,19 +288,19 @@ func TestCallbackURL(t *testing.T) {
}, },
{ {
URI: oauthURL + callbackURL + "?code=fake", URI: oauthURL + callbackURL + "?code=fake",
ExpectedCookies: []string{cfg.CookieAccessName}, ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
ExpectedLocation: "/", ExpectedLocation: "/",
ExpectedCode: http.StatusTemporaryRedirect, ExpectedCode: http.StatusTemporaryRedirect,
}, },
{ {
URI: oauthURL + callbackURL + "?code=fake&state=/admin", URI: oauthURL + callbackURL + "?code=fake&state=/admin",
ExpectedCookies: []string{cfg.CookieAccessName}, ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
ExpectedLocation: "/", ExpectedLocation: "/",
ExpectedCode: http.StatusTemporaryRedirect, ExpectedCode: http.StatusTemporaryRedirect,
}, },
{ {
URI: oauthURL + callbackURL + "?code=fake&state=L2FkbWlu", URI: oauthURL + callbackURL + "?code=fake&state=L2FkbWlu",
ExpectedCookies: []string{cfg.CookieAccessName}, ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
ExpectedLocation: "/admin", ExpectedLocation: "/admin",
ExpectedCode: http.StatusTemporaryRedirect, ExpectedCode: http.StatusTemporaryRedirect,
}, },
......
...@@ -317,6 +317,8 @@ func (r *oauthProxy) headersMiddleware(custom []string) func(http.Handler) http. ...@@ -317,6 +317,8 @@ func (r *oauthProxy) headersMiddleware(custom []string) func(http.Handler) http.
customClaims[x] = fmt.Sprintf("X-Auth-%s", toHeader(x)) customClaims[x] = fmt.Sprintf("X-Auth-%s", toHeader(x))
} }
cookieFilter := []string{r.config.CookieAccessName, r.config.CookieRefreshName}
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
scope := req.Context().Value(contextScopeName).(*RequestScope) scope := req.Context().Value(contextScopeName).(*RequestScope)
...@@ -328,6 +330,7 @@ func (r *oauthProxy) headersMiddleware(custom []string) func(http.Handler) http. ...@@ -328,6 +330,7 @@ func (r *oauthProxy) headersMiddleware(custom []string) func(http.Handler) http.
req.Header.Set("X-Auth-Subject", user.id) req.Header.Set("X-Auth-Subject", user.id)
req.Header.Set("X-Auth-Userid", user.name) req.Header.Set("X-Auth-Userid", user.name)
req.Header.Set("X-Auth-Username", user.name) req.Header.Set("X-Auth-Username", user.name)
// should we add the token header? // should we add the token header?
if r.config.EnableTokenHeader { if r.config.EnableTokenHeader {
req.Header.Set("X-Auth-Token", user.token.Encode()) req.Header.Set("X-Auth-Token", user.token.Encode())
...@@ -336,7 +339,10 @@ func (r *oauthProxy) headersMiddleware(custom []string) func(http.Handler) http. ...@@ -336,7 +339,10 @@ func (r *oauthProxy) headersMiddleware(custom []string) func(http.Handler) http.
if r.config.EnableAuthorizationHeader { if r.config.EnableAuthorizationHeader {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", user.token.Encode())) req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", user.token.Encode()))
} }
// are we filtering out the cookies
if !r.config.EnableAuthorizationCookies {
filterCookies(req, cookieFilter)
}
// inject any custom claims // inject any custom claims
for claim, header := range customClaims { for claim, header := range customClaims {
if claim, found := user.claims[claim]; found { if claim, found := user.claims[claim]; found {
......
...@@ -57,7 +57,7 @@ type fakeRequest struct { ...@@ -57,7 +57,7 @@ type fakeRequest struct {
ExpectedCode int ExpectedCode int
ExpectedContent string ExpectedContent string
ExpectedContentContains string ExpectedContentContains string
ExpectedCookies []string ExpectedCookies map[string]string
ExpectedHeaders map[string]string ExpectedHeaders map[string]string
ExpectedProxyHeaders map[string]string ExpectedProxyHeaders map[string]string
ExpectedLocation string ExpectedLocation string
...@@ -238,11 +238,14 @@ func (f *fakeProxy) RunTests(t *testing.T, requests []fakeRequest) { ...@@ -238,11 +238,14 @@ func (f *fakeProxy) RunTests(t *testing.T, requests []fakeRequest) {
assert.Contains(t, e, c.ExpectedContentContains, "case %d, expected content: %s, got: %s", i, c.ExpectedContentContains, e) assert.Contains(t, e, c.ExpectedContentContains, "case %d, expected content: %s, got: %s", i, c.ExpectedContentContains, e)
} }
if len(c.ExpectedCookies) > 0 { if len(c.ExpectedCookies) > 0 {
l := len(c.ExpectedCookies) for k, v := range c.ExpectedCookies {
g := len(resp.Cookies()) cookie := findCookie(k, resp.Cookies())
assert.Equal(t, l, g, "case %d, expected %d cookies, got: %d", i, l, g) if !assert.NotNil(t, cookie, "case %d, expected cookie %s not found", i, k) {
for _, x := range c.ExpectedCookies { continue
assert.NotNil(t, findCookie(x, resp.Cookies()), "case %d, expected cookie %s not found", i, x) }
if v != "" {
assert.Equal(t, cookie.Value, v, "case %d, expected cookie value: %s, got: %s", i, v, cookie.Value)
}
} }
} }
if c.OnResponse != nil { if c.OnResponse != nil {
...@@ -883,7 +886,7 @@ func TestCheckRefreshTokens(t *testing.T) { ...@@ -883,7 +886,7 @@ func TestCheckRefreshTokens(t *testing.T) {
Redirects: false, Redirects: false,
ExpectedProxy: true, ExpectedProxy: true,
ExpectedCode: http.StatusOK, ExpectedCode: http.StatusOK,
ExpectedCookies: []string{cfg.CookieAccessName}, ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
}, },
} }
p.RunTests(t, requests) p.RunTests(t, requests)
......
...@@ -27,6 +27,32 @@ import ( ...@@ -27,6 +27,32 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
// filterCookies is responsible for censoring any cookies we don't want sent
func filterCookies(req *http.Request, filter []string) error {
// @NOTE: there doesn't appear to be a way of removing a cookie from the http.Request as
// AddCookie() just append
cookies := req.Cookies()
// @step: empty the current cookies
req.Header.Set("Cookie", "")
// @step: iterate the cookies and filter out anything we
for _, x := range cookies {
var found bool
// @step: does this cookie match our filter?
for _, n := range filter {
if x.Name == n {
req.AddCookie(&http.Cookie{Name: x.Name, Value: "censored"})
found = true
break
}
}
if !found {
req.AddCookie(x)
}
}
return nil
}
// revokeProxy is responsible to stopping the middleware from proxying the request // revokeProxy is responsible to stopping the middleware from proxying the request
func (r *oauthProxy) revokeProxy(w http.ResponseWriter, req *http.Request) context.Context { func (r *oauthProxy) revokeProxy(w http.ResponseWriter, req *http.Request) context.Context {
var scope *RequestScope var scope *RequestScope
......
...@@ -327,6 +327,29 @@ func TestAuthTokenHeaderDisabled(t *testing.T) { ...@@ -327,6 +327,29 @@ func TestAuthTokenHeaderDisabled(t *testing.T) {
p.RunTests(t, requests) p.RunTests(t, requests)
} }
func TestDisableAuthorizationCookie(t *testing.T) {
c := newFakeKeycloakConfig()
c.EnableAuthorizationCookies = false
p := newFakeProxy(c)
token := newTestToken(p.idp.getLocation())
signed, _ := p.idp.signToken(token.claims)
requests := []fakeRequest{
{
URI: "/auth_all/test",
Cookies: []*http.Cookie{
{Name: c.CookieAccessName, Value: signed.Encode()},
{Name: "mycookie", Value: "myvalue"},
},
HasToken: true,
ExpectedContentContains: "kc-access=censored; mycookie=myvalue",
ExpectedCode: http.StatusOK,
ExpectedProxy: true,
},
}
p.RunTests(t, requests)
}
func newTestService() string { func newTestService() string {
_, _, u := newTestProxyService(nil) _, _, u := newTestProxyService(nil)
return u return u
...@@ -382,6 +405,7 @@ func newFakeKeycloakConfig() *Config { ...@@ -382,6 +405,7 @@ func newFakeKeycloakConfig() *Config {
DisableAllLogging: true, DisableAllLogging: true,
DiscoveryURL: "127.0.0.1:0", DiscoveryURL: "127.0.0.1:0",
EnableAuthorizationHeader: true, EnableAuthorizationHeader: true,
EnableAuthorizationCookies: true,
EnableLogging: false, EnableLogging: false,
EnableLoginHandler: true, EnableLoginHandler: true,
EnableTokenHeader: true, EnableTokenHeader: true,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment