From 7e618fa80449308a7006f1c8404d7458371b1458 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Fri, 15 Jun 2018 09:34:22 -0500 Subject: [PATCH 01/17] added https://github.com/unrolled/secure to proxy --- main.go | 45 ++++++++++++++++++++++++++++++++++++++++++++- options.go | 19 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 287dc4894..5e97519d1 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "github.com/BurntSushi/toml" "github.com/mreiferson/go-options" + "github.com/unrolled/secure" ) func main() { @@ -21,6 +22,8 @@ func main() { upstreams := StringArray{} skipAuthRegex := StringArray{} googleGroups := StringArray{} + httpAllowedHosts := StringArray{} + httpHostsProxyHeaders := StringArray{} config := flagSet.String("config", "", "path to config file") showVersion := flagSet.Bool("version", false, "print version string") @@ -81,6 +84,25 @@ func main() { flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)") + // These are options that allow you to tune various parameters for https://github.com/unrolled/secure + flagSet.Var(&httpAllowedHosts, "httpAllowedHosts", "a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.") + flagSet.Var(&httpHostsProxyHeaders, "httpHostsProxyHeaders", "a set of header keys that may hold a proxied hostname value for the request.") + flagSet.Bool("httpSSLRedirect", false, "If set to true, then only allow HTTPS requests. Default is false.") + flagSet.Bool("httpSSLTemporaryRedirect", false, "If true, then a 302 will be used while redirecting. Default is false (301).") + flagSet.String("httpSSLHost", "", "the host name that is used to redirect HTTP requests to HTTPS. Default is \"\", which indicates to use the same host.") + flagSet.Int64("httpSTSSeconds", 0, "The max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.") + flagSet.Bool("httpSTSIncludeSubdomains", false, "If set to true, the 'includeSubdomains' will be appended to the Strict-Transport-Security header. Default is false.") + flagSet.Bool("httpSTSPreload", false, "If set to true, the 'preload' flag will be appended to the Strict-Transport-Security header. Default is false.") + flagSet.Bool("httpForceSTSHeader", false, "STS header is only included when the connection is HTTPS. If you want to force it to always be added, set to true. Default is false.") + flagSet.Bool("httpFrameDeny", false, "If set to true, adds the X-Frame-Options header with the value of 'DENY'. Default is false.") + flagSet.String("httpCustomFrameOptionsValue", "", "allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option. Default is \"\".") + flagSet.Bool("httpContentTypeNosniff", false, "If true, adds the X-Content-Type-Options header with the value 'nosniff'. Default is false.") + flagSet.Bool("httpBrowserXssFilter", false, "If true, adds the X-XSS-Protection header with the value '1; mode=block'. Default is false.") + flagSet.String("httpCustomBrowserXssValue", "", "Allows the X-XSS-Protection header value to be set with a custom value. This overrides the BrowserXssFilter option. Default is \"\".") + flagSet.String("httpContentSecurityPolicy", "", "Allows the Content-Security-Policy header value to be set with a custom value. Default is \"\". Passing a template string will replace '$NONCE' with a dynamic nonce value of 16 bytes for each request which can be later retrieved using the Nonce function.") + flagSet.String("httpPublicKey", "", "Implements HPKP to prevent MITM attacks with forged certificates. Default is \"\".") + flagSet.String("httpReferrerPolicy", "", "Allows the Referrer-Policy header with the value to be set with a custom value. Default is \"\".") + flagSet.Parse(os.Args[1:]) if *showVersion { @@ -125,8 +147,29 @@ func main() { } } + secureMiddleware := secure.New(secure.Options{ + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, + CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, + }) + h := LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging, opts.RequestLoggingFormat) + s := &Server{ - Handler: LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging, opts.RequestLoggingFormat), + Handler: secureMiddleware.Handler(h), Opts: opts, } s.ListenAndServe() diff --git a/options.go b/options.go index 949fbba80..faeb59761 100644 --- a/options.go +++ b/options.go @@ -79,6 +79,25 @@ type Options struct { SignatureKey string `flag:"signature-key" cfg:"signature_key" env:"OAUTH2_PROXY_SIGNATURE_KEY"` + // These are options that allow you to tune various parameters for https://github.com/unrolled/secure + HttpAllowedHosts []string `flag:"httpAllowedHosts" cfg:"httpAllowedHosts"` + HttpHostsProxyHeaders []string `flag:"httpHostsProxyHeaders" cfg:"httpHostsProxyHeaders"` + HttpSSLRedirect bool `flag:"httpSSLRedirect" cfg:"httpSSLRedirect"` + HttpSSLTemporaryRedirect bool `flag:"httpSSLTemporaryRedirect" cfg:"httpSSLTemporaryRedirect"` + HttpSSLHost string `flag:"httpSSLHost" cfg:"httpSSLHost"` + HttpSTSSeconds int64 `flag:"httpSTSSeconds" cfg:"httpSTSSeconds"` + HttpSTSIncludeSubdomains bool `flag:"httpSTSIncludeSubdomains" cfg:"httpSTSIncludeSubdomains"` + HttpSTSPreload bool `flag:"httpSTSPreload" cfg:"httpSTSPreload"` + HttpForceSTSHeader bool `flag:"httpForceSTSHeader" cfg:"httpForceSTSHeader"` + HttpFrameDeny bool `flag:"httpFrameDeny" cfg:"httpFrameDeny"` + HttpCustomFrameOptionsValue string `flag:"httpCustomFrameOptionsValue" cfg:"httpCustomFrameOptionsValue"` + HttpContentTypeNosniff bool `flag:"httpContentTypeNosniff" cfg:"httpContentTypeNosniff"` + HttpBrowserXssFilter bool `flag:"httpBrowserXssFilter" cfg:"httpBrowserXssFilter"` + HttpCustomBrowserXssValue string `flag:"httpCustomBrowserXssValue" cfg:"httpCustomBrowserXssValue"` + HttpContentSecurityPolicy string `flag:"httpContentSecurityPolicy" cfg:"httpContentSecurityPolicy"` + HttpPublicKey string `flag:"httpPublicKey" cfg:"httpPublicKey"` + HttpReferrerPolicy string `flag:"httpReferrerPolicy" cfg:"httpReferrerPolicy"` + // internal values that are set after config validation redirectURL *url.URL proxyURLs []*url.URL From a51f1da0ea51808b1cbdf046251366840a5fa0d3 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Fri, 15 Jun 2018 11:07:56 -0500 Subject: [PATCH 02/17] make tests, move secure middleware to NewOAuthProxy --- main.go | 24 +----------------------- oauthproxy.go | 24 +++++++++++++++++++++++- oauthproxy_test.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/main.go b/main.go index 5e97519d1..79a422d2b 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,6 @@ import ( "github.com/BurntSushi/toml" "github.com/mreiferson/go-options" - "github.com/unrolled/secure" ) func main() { @@ -147,29 +146,8 @@ func main() { } } - secureMiddleware := secure.New(secure.Options{ - AllowedHosts: opts.HttpAllowedHosts, - HostsProxyHeaders: opts.HttpHostsProxyHeaders, - SSLRedirect: opts.HttpSSLRedirect, - SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, - SSLHost: opts.HttpSSLHost, - STSSeconds: opts.HttpSTSSeconds, - STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, - STSPreload: opts.HttpSTSPreload, - ForceSTSHeader: opts.HttpForceSTSHeader, - FrameDeny: opts.HttpFrameDeny, - CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, - ContentTypeNosniff: opts.HttpContentTypeNosniff, - BrowserXssFilter: opts.HttpBrowserXssFilter, - CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, - ContentSecurityPolicy: opts.HttpContentSecurityPolicy, - PublicKey: opts.HttpPublicKey, - ReferrerPolicy: opts.HttpReferrerPolicy, - }) - h := LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging, opts.RequestLoggingFormat) - s := &Server{ - Handler: secureMiddleware.Handler(h), + Handler: LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging, opts.RequestLoggingFormat), Opts: opts, } s.ListenAndServe() diff --git a/oauthproxy.go b/oauthproxy.go index 21e5dfc74..be2e98e69 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -17,6 +17,7 @@ import ( "github.com/bitly/oauth2_proxy/cookie" "github.com/bitly/oauth2_proxy/providers" "github.com/mbland/hmacauth" + "github.com/unrolled/secure" ) const SignatureHeader = "GAP-Signature" @@ -117,6 +118,7 @@ func NewFileServer(path string, filesystemPath string) (proxy http.Handler) { func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { serveMux := http.NewServeMux() + var auth hmacauth.HmacAuth if sigData := opts.signatureData; sigData != nil { auth = hmacauth.NewHmacAuth(sigData.hash, []byte(sigData.key), @@ -171,6 +173,26 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { } } + secureMiddleware := secure.New(secure.Options{ + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, + CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, + }) + return &OAuthProxy{ CookieName: opts.CookieName, CSRFCookieName: fmt.Sprintf("%v_%v", opts.CookieName, "csrf"), @@ -192,7 +214,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { ProxyPrefix: opts.ProxyPrefix, provider: opts.provider, - serveMux: serveMux, + serveMux: secureMiddleware.Handler(serveMux), redirectURL: redirectURL, skipAuthRegex: opts.SkipAuthRegex, skipAuthPreflight: opts.SkipAuthPreflight, diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 1e6b3140d..90baa5e09 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -836,3 +836,32 @@ func TestRequestSignaturePostRequest(t *testing.T) { assert.Equal(t, 200, st.rw.Code) assert.Equal(t, st.rw.Body.String(), "signatures match") } + +func TestHttpBrowserXssFilterTrue(t *testing.T) { + opts := NewOptions() + opts.ClientID = "bazquux" + opts.ClientSecret = "foobar" + opts.CookieSecret = "xyzzyplugh" + opts.HttpBrowserXssFilter = true + opts.Validate() + + proxy := NewOAuthProxy(opts, func(string) bool { return true }) + rw := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/", nil) + proxy.ServeHTTP(rw, req) + assert.Equal(t, "1; mode=block", rw.HeaderMap.Get("X-XSS-Protection")) +} + +func TestHttpBrowserXssFilterFalse(t *testing.T) { + opts := NewOptions() + opts.ClientID = "bazquux" + opts.ClientSecret = "foobar" + opts.CookieSecret = "xyzzyplugh" + opts.Validate() + + proxy := NewOAuthProxy(opts, func(string) bool { return true }) + rw := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/", nil) + proxy.ServeHTTP(rw, req) + assert.Equal(t, "", rw.HeaderMap.Get("X-XSS-Protection")) +} From 60b211e866a0b6779a87524dbf0e5ccd8a1ac594 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Fri, 15 Jun 2018 11:19:22 -0500 Subject: [PATCH 03/17] make test name more descriptive --- oauthproxy_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 90baa5e09..727758a86 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -852,7 +852,7 @@ func TestHttpBrowserXssFilterTrue(t *testing.T) { assert.Equal(t, "1; mode=block", rw.HeaderMap.Get("X-XSS-Protection")) } -func TestHttpBrowserXssFilterFalse(t *testing.T) { +func TestHttpBrowserXssFilterFalseBydefault(t *testing.T) { opts := NewOptions() opts.ClientID = "bazquux" opts.ClientSecret = "foobar" From 4513f35b16292842e49dafe028cc3ae869bf1662 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Fri, 15 Jun 2018 11:27:56 -0500 Subject: [PATCH 04/17] cleanup some extra whitespace --- oauthproxy.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/oauthproxy.go b/oauthproxy.go index be2e98e69..42a6d22a4 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -118,7 +118,6 @@ func NewFileServer(path string, filesystemPath string) (proxy http.Handler) { func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { serveMux := http.NewServeMux() - var auth hmacauth.HmacAuth if sigData := opts.signatureData; sigData != nil { auth = hmacauth.NewHmacAuth(sigData.hash, []byte(sigData.key), @@ -192,7 +191,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { PublicKey: opts.HttpPublicKey, ReferrerPolicy: opts.HttpReferrerPolicy, }) - + return &OAuthProxy{ CookieName: opts.CookieName, CSRFCookieName: fmt.Sprintf("%v_%v", opts.CookieName, "csrf"), From f150841a9c9febc4426f6a684de34d40efe20289 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Fri, 15 Jun 2018 12:02:13 -0500 Subject: [PATCH 05/17] try a different way of registering handler --- oauthproxy.go | 46 +++++++++++++++++++++++----------------------- oauthproxy_test.go | 1 + 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/oauthproxy.go b/oauthproxy.go index 42a6d22a4..eb9af5c4b 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -118,6 +118,25 @@ func NewFileServer(path string, filesystemPath string) (proxy http.Handler) { func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { serveMux := http.NewServeMux() + secureMiddleware := secure.New(secure.Options{ + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, + CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, + }) var auth hmacauth.HmacAuth if sigData := opts.signatureData; sigData != nil { auth = hmacauth.NewHmacAuth(sigData.hash, []byte(sigData.key), @@ -136,14 +155,15 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { setProxyDirector(proxy) } serveMux.Handle(path, - &UpstreamProxy{u.Host, proxy, auth}) + secureMiddleware.Handler(&UpstreamProxy{u.Host, proxy, auth})) case "file": if u.Fragment != "" { path = u.Fragment } log.Printf("mapping path %q => file system %q", path, u.Path) proxy := NewFileServer(path, u.Path) - serveMux.Handle(path, &UpstreamProxy{path, proxy, nil}) + serveMux.Handle(path, + secureMiddleware.Handler(&UpstreamProxy{path, proxy, nil})) default: panic(fmt.Sprintf("unknown upstream protocol %s", u.Scheme)) } @@ -172,26 +192,6 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { } } - secureMiddleware := secure.New(secure.Options{ - AllowedHosts: opts.HttpAllowedHosts, - HostsProxyHeaders: opts.HttpHostsProxyHeaders, - SSLRedirect: opts.HttpSSLRedirect, - SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, - SSLHost: opts.HttpSSLHost, - STSSeconds: opts.HttpSTSSeconds, - STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, - STSPreload: opts.HttpSTSPreload, - ForceSTSHeader: opts.HttpForceSTSHeader, - FrameDeny: opts.HttpFrameDeny, - CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, - ContentTypeNosniff: opts.HttpContentTypeNosniff, - BrowserXssFilter: opts.HttpBrowserXssFilter, - CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, - ContentSecurityPolicy: opts.HttpContentSecurityPolicy, - PublicKey: opts.HttpPublicKey, - ReferrerPolicy: opts.HttpReferrerPolicy, - }) - return &OAuthProxy{ CookieName: opts.CookieName, CSRFCookieName: fmt.Sprintf("%v_%v", opts.CookieName, "csrf"), @@ -213,7 +213,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { ProxyPrefix: opts.ProxyPrefix, provider: opts.provider, - serveMux: secureMiddleware.Handler(serveMux), + serveMux: serveMux, redirectURL: redirectURL, skipAuthRegex: opts.SkipAuthRegex, skipAuthPreflight: opts.SkipAuthPreflight, diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 727758a86..07b13af96 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -863,5 +863,6 @@ func TestHttpBrowserXssFilterFalseBydefault(t *testing.T) { rw := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) proxy.ServeHTTP(rw, req) + assert.Equal(t, false, opts.HttpBrowserXssFilter) assert.Equal(t, "", rw.HeaderMap.Get("X-XSS-Protection")) } From 27a4c9acbde28322912069937f8979237a72e6f3 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Mon, 18 Jun 2018 10:21:01 -0500 Subject: [PATCH 06/17] wrap the middleware outside of the constructor --- main.go | 23 ++++++++++++++++++++++- oauthproxy.go | 25 ++----------------------- oauthproxy_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 26 deletions(-) diff --git a/main.go b/main.go index 79a422d2b..f1fa29ff3 100644 --- a/main.go +++ b/main.go @@ -127,7 +127,28 @@ func main() { os.Exit(1) } validator := NewValidator(opts.EmailDomains, opts.AuthenticatedEmailsFile) - oauthproxy := NewOAuthProxy(opts, validator) + bareproxy := NewOAuthProxy(opts, validator) + secureMiddleware := secure.New(secure.Options{ + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, + CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, + }) + oauthproxy := secureMiddleware.Handler(bareproxy) + if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" { if len(opts.EmailDomains) > 1 { diff --git a/oauthproxy.go b/oauthproxy.go index eb9af5c4b..21e5dfc74 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -17,7 +17,6 @@ import ( "github.com/bitly/oauth2_proxy/cookie" "github.com/bitly/oauth2_proxy/providers" "github.com/mbland/hmacauth" - "github.com/unrolled/secure" ) const SignatureHeader = "GAP-Signature" @@ -118,25 +117,6 @@ func NewFileServer(path string, filesystemPath string) (proxy http.Handler) { func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { serveMux := http.NewServeMux() - secureMiddleware := secure.New(secure.Options{ - AllowedHosts: opts.HttpAllowedHosts, - HostsProxyHeaders: opts.HttpHostsProxyHeaders, - SSLRedirect: opts.HttpSSLRedirect, - SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, - SSLHost: opts.HttpSSLHost, - STSSeconds: opts.HttpSTSSeconds, - STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, - STSPreload: opts.HttpSTSPreload, - ForceSTSHeader: opts.HttpForceSTSHeader, - FrameDeny: opts.HttpFrameDeny, - CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, - ContentTypeNosniff: opts.HttpContentTypeNosniff, - BrowserXssFilter: opts.HttpBrowserXssFilter, - CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, - ContentSecurityPolicy: opts.HttpContentSecurityPolicy, - PublicKey: opts.HttpPublicKey, - ReferrerPolicy: opts.HttpReferrerPolicy, - }) var auth hmacauth.HmacAuth if sigData := opts.signatureData; sigData != nil { auth = hmacauth.NewHmacAuth(sigData.hash, []byte(sigData.key), @@ -155,15 +135,14 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { setProxyDirector(proxy) } serveMux.Handle(path, - secureMiddleware.Handler(&UpstreamProxy{u.Host, proxy, auth})) + &UpstreamProxy{u.Host, proxy, auth}) case "file": if u.Fragment != "" { path = u.Fragment } log.Printf("mapping path %q => file system %q", path, u.Path) proxy := NewFileServer(path, u.Path) - serveMux.Handle(path, - secureMiddleware.Handler(&UpstreamProxy{path, proxy, nil})) + serveMux.Handle(path, &UpstreamProxy{path, proxy, nil}) default: panic(fmt.Sprintf("unknown upstream protocol %s", u.Scheme)) } diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 07b13af96..0a9dfa517 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -18,6 +18,7 @@ import ( "github.com/bitly/oauth2_proxy/providers" "github.com/mbland/hmacauth" "github.com/stretchr/testify/assert" + "github.com/unrolled/secure" ) func init() { @@ -845,7 +846,27 @@ func TestHttpBrowserXssFilterTrue(t *testing.T) { opts.HttpBrowserXssFilter = true opts.Validate() - proxy := NewOAuthProxy(opts, func(string) bool { return true }) + bareproxy := NewOAuthProxy(opts, func(string) bool { return true }) + secureMiddleware := secure.New(secure.Options{ + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, + CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, + }) + proxy := secureMiddleware.Handler(bareproxy) rw := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) proxy.ServeHTTP(rw, req) @@ -859,7 +880,27 @@ func TestHttpBrowserXssFilterFalseBydefault(t *testing.T) { opts.CookieSecret = "xyzzyplugh" opts.Validate() - proxy := NewOAuthProxy(opts, func(string) bool { return true }) + bareproxy := NewOAuthProxy(opts, func(string) bool { return true }) + secureMiddleware := secure.New(secure.Options{ + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, + CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, + }) + proxy := secureMiddleware.Handler(bareproxy) rw := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) proxy.ServeHTTP(rw, req) From d3df255f35e5dd63967b862545329e12dc1bb3b8 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Mon, 18 Jun 2018 10:24:03 -0500 Subject: [PATCH 07/17] forgot to add the lib to main --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index f1fa29ff3..e715136ef 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "github.com/BurntSushi/toml" "github.com/mreiferson/go-options" + "github.com/unrolled/secure" ) func main() { From 5b171a574ddafde005caf95d87477833c4f9120d Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Mon, 18 Jun 2018 10:31:05 -0500 Subject: [PATCH 08/17] made sure calls to the original proxy work --- main.go | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/main.go b/main.go index e715136ef..7bfb627b0 100644 --- a/main.go +++ b/main.go @@ -129,6 +129,23 @@ func main() { } validator := NewValidator(opts.EmailDomains, opts.AuthenticatedEmailsFile) bareproxy := NewOAuthProxy(opts, validator) + + if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" { + if len(opts.EmailDomains) > 1 { + bareproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(opts.EmailDomains, ", ")) + } else if opts.EmailDomains[0] != "*" { + bareproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", opts.EmailDomains[0]) + } + } + + if opts.HtpasswdFile != "" { + log.Printf("using htpasswd file %s", opts.HtpasswdFile) + bareproxy.HtpasswdFile, err = NewHtpasswdFromFile(opts.HtpasswdFile) + bareproxy.DisplayHtpasswdForm = opts.DisplayHtpasswdForm + if err != nil { + log.Fatalf("FATAL: unable to open %s %s", opts.HtpasswdFile, err) + } + } secureMiddleware := secure.New(secure.Options{ AllowedHosts: opts.HttpAllowedHosts, HostsProxyHeaders: opts.HttpHostsProxyHeaders, @@ -150,24 +167,6 @@ func main() { }) oauthproxy := secureMiddleware.Handler(bareproxy) - - if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" { - if len(opts.EmailDomains) > 1 { - oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(opts.EmailDomains, ", ")) - } else if opts.EmailDomains[0] != "*" { - oauthproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", opts.EmailDomains[0]) - } - } - - if opts.HtpasswdFile != "" { - log.Printf("using htpasswd file %s", opts.HtpasswdFile) - oauthproxy.HtpasswdFile, err = NewHtpasswdFromFile(opts.HtpasswdFile) - oauthproxy.DisplayHtpasswdForm = opts.DisplayHtpasswdForm - if err != nil { - log.Fatalf("FATAL: unable to open %s %s", opts.HtpasswdFile, err) - } - } - s := &Server{ Handler: LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging, opts.RequestLoggingFormat), Opts: opts, From 9e0b5851ee6a6ca370b0f0dc015099589e7e0a82 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Tue, 19 Jun 2018 10:22:51 -0500 Subject: [PATCH 09/17] make sure that dep ensure works --- Gopkg.lock | 30 +++++++++++++++++++++++------- Gopkg.toml | 6 +++--- watcher.go | 2 +- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 5a3758a17..d15441c37 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -19,6 +19,16 @@ revision = "aabad6e819789e569bd6aabf444c935aa9ba1e44" version = "v0.5.0" +[[projects]] + name = "github.com/bitly/oauth2_proxy" + packages = [ + "api", + "cookie", + "providers" + ] + revision = "b90a23473f10c7bb2d84acd033f7d7ed81b95dd3" + version = "v2.2" + [[projects]] branch = "v2" name = "github.com/coreos/go-oidc" @@ -31,6 +41,12 @@ revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" +[[projects]] + name = "github.com/fsnotify/fsnotify" + packages = ["."] + revision = "836bfd95fecc0f1511dd66bdbf2b5b61ab8b00b6" + version = "v1.2.11" + [[projects]] branch = "master" name = "github.com/golang/protobuf" @@ -70,6 +86,12 @@ revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0" version = "v1.1.4" +[[projects]] + branch = "master" + name = "github.com/unrolled/secure" + packages = ["."] + revision = "8287f3899c8e3d490748e18fe7d438629132914e" + [[projects]] branch = "master" name = "golang.org/x/crypto" @@ -130,12 +152,6 @@ revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" version = "v1.0.0" -[[projects]] - name = "gopkg.in/fsnotify.v1" - packages = ["."] - revision = "836bfd95fecc0f1511dd66bdbf2b5b61ab8b00b6" - version = "v1.2.11" - [[projects]] name = "gopkg.in/square/go-jose.v2" packages = [ @@ -149,6 +165,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "b502c41a61115d14d6379be26b0300f65d173bdad852f0170d387ebf2d7ec173" + inputs-digest = "f9ed0c5bfe9c08fe7aa500f3b8878e13494217d0b5cdbde213325fad2d544a4c" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index c4005e114..377d8c335 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -4,8 +4,8 @@ # [[constraint]] - name = "github.com/18F/hmacauth" - version = "~1.0.1" + name = "github.com/mbland/hmacauth" + version = "~1.0.2" [[constraint]] name = "github.com/BurntSushi/toml" @@ -36,7 +36,7 @@ name = "google.golang.org/api" [[constraint]] - name = "gopkg.in/fsnotify.v1" + name = "github.com/fsnotify/fsnotify" version = "~1.2.0" [[constraint]] diff --git a/watcher.go b/watcher.go index bedb9f893..b739d1421 100644 --- a/watcher.go +++ b/watcher.go @@ -8,7 +8,7 @@ import ( "path/filepath" "time" - "gopkg.in/fsnotify.v1" + "github.com/fsnotify/fsnotify" ) func WaitForReplacement(filename string, op fsnotify.Op, From 174872313225deb7eed2e4fc0d7ab40705b16342 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Tue, 19 Jun 2018 11:06:09 -0500 Subject: [PATCH 10/17] formatted to make gofmt happy --- main.go | 32 +++++++++++------------ oauthproxy_test.go | 64 +++++++++++++++++++++++----------------------- options.go | 34 ++++++++++++------------ 3 files changed, 65 insertions(+), 65 deletions(-) diff --git a/main.go b/main.go index 7bfb627b0..692c9c6ea 100644 --- a/main.go +++ b/main.go @@ -147,23 +147,23 @@ func main() { } } secureMiddleware := secure.New(secure.Options{ - AllowedHosts: opts.HttpAllowedHosts, - HostsProxyHeaders: opts.HttpHostsProxyHeaders, - SSLRedirect: opts.HttpSSLRedirect, - SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, - SSLHost: opts.HttpSSLHost, - STSSeconds: opts.HttpSTSSeconds, - STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, - STSPreload: opts.HttpSTSPreload, - ForceSTSHeader: opts.HttpForceSTSHeader, - FrameDeny: opts.HttpFrameDeny, + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, - ContentTypeNosniff: opts.HttpContentTypeNosniff, - BrowserXssFilter: opts.HttpBrowserXssFilter, - CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, - ContentSecurityPolicy: opts.HttpContentSecurityPolicy, - PublicKey: opts.HttpPublicKey, - ReferrerPolicy: opts.HttpReferrerPolicy, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, }) oauthproxy := secureMiddleware.Handler(bareproxy) diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 0a9dfa517..7b8b48dff 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -848,23 +848,23 @@ func TestHttpBrowserXssFilterTrue(t *testing.T) { bareproxy := NewOAuthProxy(opts, func(string) bool { return true }) secureMiddleware := secure.New(secure.Options{ - AllowedHosts: opts.HttpAllowedHosts, - HostsProxyHeaders: opts.HttpHostsProxyHeaders, - SSLRedirect: opts.HttpSSLRedirect, - SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, - SSLHost: opts.HttpSSLHost, - STSSeconds: opts.HttpSTSSeconds, - STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, - STSPreload: opts.HttpSTSPreload, - ForceSTSHeader: opts.HttpForceSTSHeader, - FrameDeny: opts.HttpFrameDeny, + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, - ContentTypeNosniff: opts.HttpContentTypeNosniff, - BrowserXssFilter: opts.HttpBrowserXssFilter, - CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, - ContentSecurityPolicy: opts.HttpContentSecurityPolicy, - PublicKey: opts.HttpPublicKey, - ReferrerPolicy: opts.HttpReferrerPolicy, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, }) proxy := secureMiddleware.Handler(bareproxy) rw := httptest.NewRecorder() @@ -882,23 +882,23 @@ func TestHttpBrowserXssFilterFalseBydefault(t *testing.T) { bareproxy := NewOAuthProxy(opts, func(string) bool { return true }) secureMiddleware := secure.New(secure.Options{ - AllowedHosts: opts.HttpAllowedHosts, - HostsProxyHeaders: opts.HttpHostsProxyHeaders, - SSLRedirect: opts.HttpSSLRedirect, - SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, - SSLHost: opts.HttpSSLHost, - STSSeconds: opts.HttpSTSSeconds, - STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, - STSPreload: opts.HttpSTSPreload, - ForceSTSHeader: opts.HttpForceSTSHeader, - FrameDeny: opts.HttpFrameDeny, + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, - ContentTypeNosniff: opts.HttpContentTypeNosniff, - BrowserXssFilter: opts.HttpBrowserXssFilter, - CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, - ContentSecurityPolicy: opts.HttpContentSecurityPolicy, - PublicKey: opts.HttpPublicKey, - ReferrerPolicy: opts.HttpReferrerPolicy, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, }) proxy := secureMiddleware.Handler(bareproxy) rw := httptest.NewRecorder() diff --git a/options.go b/options.go index faeb59761..21042c1f0 100644 --- a/options.go +++ b/options.go @@ -80,23 +80,23 @@ type Options struct { SignatureKey string `flag:"signature-key" cfg:"signature_key" env:"OAUTH2_PROXY_SIGNATURE_KEY"` // These are options that allow you to tune various parameters for https://github.com/unrolled/secure - HttpAllowedHosts []string `flag:"httpAllowedHosts" cfg:"httpAllowedHosts"` - HttpHostsProxyHeaders []string `flag:"httpHostsProxyHeaders" cfg:"httpHostsProxyHeaders"` - HttpSSLRedirect bool `flag:"httpSSLRedirect" cfg:"httpSSLRedirect"` - HttpSSLTemporaryRedirect bool `flag:"httpSSLTemporaryRedirect" cfg:"httpSSLTemporaryRedirect"` - HttpSSLHost string `flag:"httpSSLHost" cfg:"httpSSLHost"` - HttpSTSSeconds int64 `flag:"httpSTSSeconds" cfg:"httpSTSSeconds"` - HttpSTSIncludeSubdomains bool `flag:"httpSTSIncludeSubdomains" cfg:"httpSTSIncludeSubdomains"` - HttpSTSPreload bool `flag:"httpSTSPreload" cfg:"httpSTSPreload"` - HttpForceSTSHeader bool `flag:"httpForceSTSHeader" cfg:"httpForceSTSHeader"` - HttpFrameDeny bool `flag:"httpFrameDeny" cfg:"httpFrameDeny"` - HttpCustomFrameOptionsValue string `flag:"httpCustomFrameOptionsValue" cfg:"httpCustomFrameOptionsValue"` - HttpContentTypeNosniff bool `flag:"httpContentTypeNosniff" cfg:"httpContentTypeNosniff"` - HttpBrowserXssFilter bool `flag:"httpBrowserXssFilter" cfg:"httpBrowserXssFilter"` - HttpCustomBrowserXssValue string `flag:"httpCustomBrowserXssValue" cfg:"httpCustomBrowserXssValue"` - HttpContentSecurityPolicy string `flag:"httpContentSecurityPolicy" cfg:"httpContentSecurityPolicy"` - HttpPublicKey string `flag:"httpPublicKey" cfg:"httpPublicKey"` - HttpReferrerPolicy string `flag:"httpReferrerPolicy" cfg:"httpReferrerPolicy"` + HttpAllowedHosts []string `flag:"httpAllowedHosts" cfg:"httpAllowedHosts"` + HttpHostsProxyHeaders []string `flag:"httpHostsProxyHeaders" cfg:"httpHostsProxyHeaders"` + HttpSSLRedirect bool `flag:"httpSSLRedirect" cfg:"httpSSLRedirect"` + HttpSSLTemporaryRedirect bool `flag:"httpSSLTemporaryRedirect" cfg:"httpSSLTemporaryRedirect"` + HttpSSLHost string `flag:"httpSSLHost" cfg:"httpSSLHost"` + HttpSTSSeconds int64 `flag:"httpSTSSeconds" cfg:"httpSTSSeconds"` + HttpSTSIncludeSubdomains bool `flag:"httpSTSIncludeSubdomains" cfg:"httpSTSIncludeSubdomains"` + HttpSTSPreload bool `flag:"httpSTSPreload" cfg:"httpSTSPreload"` + HttpForceSTSHeader bool `flag:"httpForceSTSHeader" cfg:"httpForceSTSHeader"` + HttpFrameDeny bool `flag:"httpFrameDeny" cfg:"httpFrameDeny"` + HttpCustomFrameOptionsValue string `flag:"httpCustomFrameOptionsValue" cfg:"httpCustomFrameOptionsValue"` + HttpContentTypeNosniff bool `flag:"httpContentTypeNosniff" cfg:"httpContentTypeNosniff"` + HttpBrowserXssFilter bool `flag:"httpBrowserXssFilter" cfg:"httpBrowserXssFilter"` + HttpCustomBrowserXssValue string `flag:"httpCustomBrowserXssValue" cfg:"httpCustomBrowserXssValue"` + HttpContentSecurityPolicy string `flag:"httpContentSecurityPolicy" cfg:"httpContentSecurityPolicy"` + HttpPublicKey string `flag:"httpPublicKey" cfg:"httpPublicKey"` + HttpReferrerPolicy string `flag:"httpReferrerPolicy" cfg:"httpReferrerPolicy"` // internal values that are set after config validation redirectURL *url.URL From d71bc76f6e4f838cce39b0986cde1ce9aa3862c0 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Tue, 19 Jun 2018 11:51:25 -0500 Subject: [PATCH 11/17] turn on -x to see why test.sh is failing in travis --- test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.sh b/test.sh index acc17a231..72da90634 100755 --- a/test.sh +++ b/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -x EXIT_CODE=0 echo "gofmt" diff -u <(echo -n) <(gofmt -d $(find . -type f -name '*.go' -not -path "./vendor/*")) || EXIT_CODE=1 @@ -11,4 +11,4 @@ for pkg in $(go list ./... | grep -v '/vendor/' ); do echo "go test -v -race $pkg" GOMAXPROCS=4 go test -v -timeout 90s0s -race "$pkg" || EXIT_CODE=1 done -exit $EXIT_CODE \ No newline at end of file +exit $EXIT_CODE From 6aa4c50aef81b7704035c4f855dea8b8a56730f8 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Tue, 19 Jun 2018 13:09:04 -0500 Subject: [PATCH 12/17] don't need -x --- test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 72da90634..1a97e41f5 100755 --- a/test.sh +++ b/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash -x +#!/bin/bash EXIT_CODE=0 echo "gofmt" diff -u <(echo -n) <(gofmt -d $(find . -type f -name '*.go' -not -path "./vendor/*")) || EXIT_CODE=1 From 8926159f32c2292d569208781ffe53546a86e04f Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Tue, 26 Jun 2018 11:00:34 -0700 Subject: [PATCH 13/17] updated tests with roger's help --- main.go | 46 +------------------------------------------ oauthproxy.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++ oauthproxy_test.go | 49 +++++----------------------------------------- 3 files changed, 55 insertions(+), 89 deletions(-) diff --git a/main.go b/main.go index 692c9c6ea..4f5191457 100644 --- a/main.go +++ b/main.go @@ -6,12 +6,10 @@ import ( "log" "os" "runtime" - "strings" "time" "github.com/BurntSushi/toml" "github.com/mreiferson/go-options" - "github.com/unrolled/secure" ) func main() { @@ -122,50 +120,8 @@ func main() { cfg.LoadEnvForStruct(opts) options.Resolve(opts, flagSet, cfg) - err := opts.Validate() - if err != nil { - log.Printf("%s", err) - os.Exit(1) - } validator := NewValidator(opts.EmailDomains, opts.AuthenticatedEmailsFile) - bareproxy := NewOAuthProxy(opts, validator) - - if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" { - if len(opts.EmailDomains) > 1 { - bareproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(opts.EmailDomains, ", ")) - } else if opts.EmailDomains[0] != "*" { - bareproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", opts.EmailDomains[0]) - } - } - - if opts.HtpasswdFile != "" { - log.Printf("using htpasswd file %s", opts.HtpasswdFile) - bareproxy.HtpasswdFile, err = NewHtpasswdFromFile(opts.HtpasswdFile) - bareproxy.DisplayHtpasswdForm = opts.DisplayHtpasswdForm - if err != nil { - log.Fatalf("FATAL: unable to open %s %s", opts.HtpasswdFile, err) - } - } - secureMiddleware := secure.New(secure.Options{ - AllowedHosts: opts.HttpAllowedHosts, - HostsProxyHeaders: opts.HttpHostsProxyHeaders, - SSLRedirect: opts.HttpSSLRedirect, - SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, - SSLHost: opts.HttpSSLHost, - STSSeconds: opts.HttpSTSSeconds, - STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, - STSPreload: opts.HttpSTSPreload, - ForceSTSHeader: opts.HttpForceSTSHeader, - FrameDeny: opts.HttpFrameDeny, - CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, - ContentTypeNosniff: opts.HttpContentTypeNosniff, - BrowserXssFilter: opts.HttpBrowserXssFilter, - CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, - ContentSecurityPolicy: opts.HttpContentSecurityPolicy, - PublicKey: opts.HttpPublicKey, - ReferrerPolicy: opts.HttpReferrerPolicy, - }) - oauthproxy := secureMiddleware.Handler(bareproxy) + oauthproxy := CreateSecureProxy(opts, validator) s := &Server{ Handler: LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging, opts.RequestLoggingFormat), diff --git a/oauthproxy.go b/oauthproxy.go index 21e5dfc74..d34a402a1 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -7,6 +7,7 @@ import ( "html/template" "log" "net" + "os" "net/http" "net/http/httputil" "net/url" @@ -17,6 +18,7 @@ import ( "github.com/bitly/oauth2_proxy/cookie" "github.com/bitly/oauth2_proxy/providers" "github.com/mbland/hmacauth" + "github.com/unrolled/secure" ) const SignatureHeader = "GAP-Signature" @@ -115,6 +117,53 @@ func NewFileServer(path string, filesystemPath string) (proxy http.Handler) { return http.StripPrefix(path, http.FileServer(http.Dir(filesystemPath))) } +func CreateSecureProxy(opts *Options, validator func(string) bool) http.Handler { + bareproxy := NewOAuthProxy(opts, validator) + err := opts.Validate() + if err != nil { + log.Printf("%s", err) + os.Exit(1) + } + + if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" { + if len(opts.EmailDomains) > 1 { + bareproxy.SignInMessage = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(opts.EmailDomains, ", ")) + } else if opts.EmailDomains[0] != "*" { + bareproxy.SignInMessage = fmt.Sprintf("Authenticate using %v", opts.EmailDomains[0]) + } + } + + if opts.HtpasswdFile != "" { + log.Printf("using htpasswd file %s", opts.HtpasswdFile) + bareproxy.HtpasswdFile, err = NewHtpasswdFromFile(opts.HtpasswdFile) + bareproxy.DisplayHtpasswdForm = opts.DisplayHtpasswdForm + if err != nil { + log.Fatalf("FATAL: unable to open %s %s", opts.HtpasswdFile, err) + } + } + + secureMiddleware := secure.New(secure.Options{ + AllowedHosts: opts.HttpAllowedHosts, + HostsProxyHeaders: opts.HttpHostsProxyHeaders, + SSLRedirect: opts.HttpSSLRedirect, + SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, + SSLHost: opts.HttpSSLHost, + STSSeconds: opts.HttpSTSSeconds, + STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, + STSPreload: opts.HttpSTSPreload, + ForceSTSHeader: opts.HttpForceSTSHeader, + FrameDeny: opts.HttpFrameDeny, + CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, + ContentTypeNosniff: opts.HttpContentTypeNosniff, + BrowserXssFilter: opts.HttpBrowserXssFilter, + CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, + ContentSecurityPolicy: opts.HttpContentSecurityPolicy, + PublicKey: opts.HttpPublicKey, + ReferrerPolicy: opts.HttpReferrerPolicy, + }) + return secureMiddleware.Handler(bareproxy) +} + func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { serveMux := http.NewServeMux() var auth hmacauth.HmacAuth diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 7b8b48dff..8c71c212f 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -18,7 +18,6 @@ import ( "github.com/bitly/oauth2_proxy/providers" "github.com/mbland/hmacauth" "github.com/stretchr/testify/assert" - "github.com/unrolled/secure" ) func init() { @@ -843,64 +842,26 @@ func TestHttpBrowserXssFilterTrue(t *testing.T) { opts.ClientID = "bazquux" opts.ClientSecret = "foobar" opts.CookieSecret = "xyzzyplugh" + opts.EmailDomains = []string{"*"} opts.HttpBrowserXssFilter = true opts.Validate() - bareproxy := NewOAuthProxy(opts, func(string) bool { return true }) - secureMiddleware := secure.New(secure.Options{ - AllowedHosts: opts.HttpAllowedHosts, - HostsProxyHeaders: opts.HttpHostsProxyHeaders, - SSLRedirect: opts.HttpSSLRedirect, - SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, - SSLHost: opts.HttpSSLHost, - STSSeconds: opts.HttpSTSSeconds, - STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, - STSPreload: opts.HttpSTSPreload, - ForceSTSHeader: opts.HttpForceSTSHeader, - FrameDeny: opts.HttpFrameDeny, - CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, - ContentTypeNosniff: opts.HttpContentTypeNosniff, - BrowserXssFilter: opts.HttpBrowserXssFilter, - CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, - ContentSecurityPolicy: opts.HttpContentSecurityPolicy, - PublicKey: opts.HttpPublicKey, - ReferrerPolicy: opts.HttpReferrerPolicy, - }) - proxy := secureMiddleware.Handler(bareproxy) + proxy := CreateSecureProxy(opts, func(string) bool { return true }) rw := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) proxy.ServeHTTP(rw, req) assert.Equal(t, "1; mode=block", rw.HeaderMap.Get("X-XSS-Protection")) } -func TestHttpBrowserXssFilterFalseBydefault(t *testing.T) { +func TestHttpBrowserXssFilterFalseByDefault(t *testing.T) { opts := NewOptions() opts.ClientID = "bazquux" opts.ClientSecret = "foobar" opts.CookieSecret = "xyzzyplugh" + opts.EmailDomains = []string{"*"} opts.Validate() - bareproxy := NewOAuthProxy(opts, func(string) bool { return true }) - secureMiddleware := secure.New(secure.Options{ - AllowedHosts: opts.HttpAllowedHosts, - HostsProxyHeaders: opts.HttpHostsProxyHeaders, - SSLRedirect: opts.HttpSSLRedirect, - SSLTemporaryRedirect: opts.HttpSSLTemporaryRedirect, - SSLHost: opts.HttpSSLHost, - STSSeconds: opts.HttpSTSSeconds, - STSIncludeSubdomains: opts.HttpSTSIncludeSubdomains, - STSPreload: opts.HttpSTSPreload, - ForceSTSHeader: opts.HttpForceSTSHeader, - FrameDeny: opts.HttpFrameDeny, - CustomFrameOptionsValue: opts.HttpCustomFrameOptionsValue, - ContentTypeNosniff: opts.HttpContentTypeNosniff, - BrowserXssFilter: opts.HttpBrowserXssFilter, - CustomBrowserXssValue: opts.HttpCustomBrowserXssValue, - ContentSecurityPolicy: opts.HttpContentSecurityPolicy, - PublicKey: opts.HttpPublicKey, - ReferrerPolicy: opts.HttpReferrerPolicy, - }) - proxy := secureMiddleware.Handler(bareproxy) + proxy := CreateSecureProxy(opts, func(string) bool { return true }) rw := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) proxy.ServeHTTP(rw, req) From 0dcf2222cf6a5c29a1aca63019c6d8c09853a6f5 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Tue, 26 Jun 2018 11:14:09 -0700 Subject: [PATCH 14/17] fixed gofmt stuff --- oauthproxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauthproxy.go b/oauthproxy.go index d34a402a1..6eff080f2 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -7,10 +7,10 @@ import ( "html/template" "log" "net" - "os" "net/http" "net/http/httputil" "net/url" + "os" "regexp" "strings" "time" From 041466f070bf8d04f24808ec9e6f3d600d6bc873 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Tue, 26 Jun 2018 11:31:41 -0700 Subject: [PATCH 15/17] added an end to end test for the secure proxy --- oauthproxy_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 8c71c212f..b1dfa0a17 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -868,3 +868,21 @@ func TestHttpBrowserXssFilterFalseByDefault(t *testing.T) { assert.Equal(t, false, opts.HttpBrowserXssFilter) assert.Equal(t, "", rw.HeaderMap.Get("X-XSS-Protection")) } + +func TestSecureRobotsTxt(t *testing.T) { + opts := NewOptions() + opts.ClientID = "bazquux" + opts.ClientSecret = "foobar" + opts.CookieSecret = "xyzzyplugh" + opts.EmailDomains = []string{"*"} + opts.HttpBrowserXssFilter = true + opts.Validate() + + proxy := CreateSecureProxy(opts, func(string) bool { return true }) + rw := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/robots.txt", nil) + proxy.ServeHTTP(rw, req) + assert.Equal(t, 200, rw.Code) + assert.Equal(t, "User-agent: *\nDisallow: /", rw.Body.String()) + assert.Equal(t, "1; mode=block", rw.HeaderMap.Get("X-XSS-Protection")) +} From a86773d3946f0d78b7b5d7950d4d99e11dd2b312 Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Tue, 26 Jun 2018 14:27:47 -0700 Subject: [PATCH 16/17] move options validation back --- main.go | 5 +++++ oauthproxy.go | 7 +------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 4f5191457..983008909 100644 --- a/main.go +++ b/main.go @@ -119,6 +119,11 @@ func main() { } cfg.LoadEnvForStruct(opts) options.Resolve(opts, flagSet, cfg) + err := opts.Validate() + if err != nil { + log.Printf("%s", err) + os.Exit(1) + } validator := NewValidator(opts.EmailDomains, opts.AuthenticatedEmailsFile) oauthproxy := CreateSecureProxy(opts, validator) diff --git a/oauthproxy.go b/oauthproxy.go index 6eff080f2..e6b5cc793 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -10,7 +10,6 @@ import ( "net/http" "net/http/httputil" "net/url" - "os" "regexp" "strings" "time" @@ -119,11 +118,7 @@ func NewFileServer(path string, filesystemPath string) (proxy http.Handler) { func CreateSecureProxy(opts *Options, validator func(string) bool) http.Handler { bareproxy := NewOAuthProxy(opts, validator) - err := opts.Validate() - if err != nil { - log.Printf("%s", err) - os.Exit(1) - } + var err error if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" { if len(opts.EmailDomains) > 1 { From 5f186f5cc22fde2c4097c5560b3710436a2a2c8d Mon Sep 17 00:00:00 2001 From: timothy-spencer Date: Tue, 26 Jun 2018 14:53:28 -0700 Subject: [PATCH 17/17] rename Create --- main.go | 2 +- oauthproxy.go | 2 +- oauthproxy_test.go | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 983008909..cecad2afb 100644 --- a/main.go +++ b/main.go @@ -126,7 +126,7 @@ func main() { } validator := NewValidator(opts.EmailDomains, opts.AuthenticatedEmailsFile) - oauthproxy := CreateSecureProxy(opts, validator) + oauthproxy := NewSecureProxy(opts, validator) s := &Server{ Handler: LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging, opts.RequestLoggingFormat), diff --git a/oauthproxy.go b/oauthproxy.go index e6b5cc793..468f824a5 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -116,7 +116,7 @@ func NewFileServer(path string, filesystemPath string) (proxy http.Handler) { return http.StripPrefix(path, http.FileServer(http.Dir(filesystemPath))) } -func CreateSecureProxy(opts *Options, validator func(string) bool) http.Handler { +func NewSecureProxy(opts *Options, validator func(string) bool) http.Handler { bareproxy := NewOAuthProxy(opts, validator) var err error diff --git a/oauthproxy_test.go b/oauthproxy_test.go index b1dfa0a17..5416d01a8 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -846,7 +846,7 @@ func TestHttpBrowserXssFilterTrue(t *testing.T) { opts.HttpBrowserXssFilter = true opts.Validate() - proxy := CreateSecureProxy(opts, func(string) bool { return true }) + proxy := NewSecureProxy(opts, func(string) bool { return true }) rw := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) proxy.ServeHTTP(rw, req) @@ -861,7 +861,7 @@ func TestHttpBrowserXssFilterFalseByDefault(t *testing.T) { opts.EmailDomains = []string{"*"} opts.Validate() - proxy := CreateSecureProxy(opts, func(string) bool { return true }) + proxy := NewSecureProxy(opts, func(string) bool { return true }) rw := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) proxy.ServeHTTP(rw, req) @@ -878,7 +878,7 @@ func TestSecureRobotsTxt(t *testing.T) { opts.HttpBrowserXssFilter = true opts.Validate() - proxy := CreateSecureProxy(opts, func(string) bool { return true }) + proxy := NewSecureProxy(opts, func(string) bool { return true }) rw := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/robots.txt", nil) proxy.ServeHTTP(rw, req)