From 73513f8371d6d60f8c5ad67d91ecfb3da07ddb99 Mon Sep 17 00:00:00 2001 From: Daniel Tomcej Date: Wed, 1 Jul 2020 01:42:04 -0700 Subject: [PATCH] Allow multiple secure middlewares to operate independently --- .../fixtures/headers/secure_multiple.toml | 33 +++++++++++++++ integration/headers_test.go | 41 +++++++++++++++++++ pkg/middlewares/headers/headers.go | 5 ++- pkg/middlewares/headers/headers_test.go | 24 ++++++++--- pkg/responsemodifiers/headers.go | 3 +- pkg/responsemodifiers/response_modifier.go | 2 +- .../response_modifier_test.go | 2 +- 7 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 integration/fixtures/headers/secure_multiple.toml diff --git a/integration/fixtures/headers/secure_multiple.toml b/integration/fixtures/headers/secure_multiple.toml new file mode 100644 index 000000000..9603046e5 --- /dev/null +++ b/integration/fixtures/headers/secure_multiple.toml @@ -0,0 +1,33 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + +[entryPoints] + [entryPoints.web] + address = ":8000" + +[providers.file] + filename = "{{ .SelfFilename }}" + +## dynamic configuration ## + +[http.routers] + [http.routers.router1] + rule = "Host(`test.localhost`)" + middlewares = ["foo", "bar"] + service = "service1" + + +[http.middlewares] + [http.middlewares.foo.headers] + frameDeny = true + [http.middlewares.bar.headers] + contentTypeNosniff = true + +[http.services] + [http.services.service1.loadBalancer] + [[http.services.service1.loadBalancer.servers]] + url = "http://127.0.0.1:9000" diff --git a/integration/headers_test.go b/integration/headers_test.go index 34df16a78..06c208ef7 100644 --- a/integration/headers_test.go +++ b/integration/headers_test.go @@ -153,3 +153,44 @@ func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) { c.Assert(err, checker.IsNil) } } + +func (s *HeadersSuite) TestMultipleSecureHeadersResponses(c *check.C) { + file := s.adaptFile(c, "fixtures/headers/secure_multiple.toml", struct{}{}) + defer os.Remove(file) + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + backend := startTestServer("9000", http.StatusOK) + defer backend.Close() + + err = try.GetRequest(backend.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) + c.Assert(err, checker.IsNil) + + testCase := []struct { + desc string + expected http.Header + reqHost string + }{ + { + desc: "Feature-Policy Set", + expected: http.Header{ + "X-Frame-Options": {"DENY"}, + "X-Content-Type-Options": {"nosniff"}, + }, + reqHost: "test.localhost", + }, + } + + for _, test := range testCase { + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + c.Assert(err, checker.IsNil) + req.Host = test.reqHost + + err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected)) + c.Assert(err, checker.IsNil) + } +} diff --git a/pkg/middlewares/headers/headers.go b/pkg/middlewares/headers/headers.go index 43faa6dff..83529da43 100644 --- a/pkg/middlewares/headers/headers.go +++ b/pkg/middlewares/headers/headers.go @@ -55,7 +55,7 @@ func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name strin if hasSecureHeaders { logger.Debug("Setting up secureHeaders from %v", cfg) - handler = newSecure(next, cfg) + handler = newSecure(next, cfg, name) nextHandler = handler } @@ -84,7 +84,7 @@ type secureHeader struct { } // newSecure constructs a new secure instance with supplied options. -func newSecure(next http.Handler, cfg dynamic.Headers) *secureHeader { +func newSecure(next http.Handler, cfg dynamic.Headers, contextKey string) *secureHeader { opt := secure.Options{ BrowserXssFilter: cfg.BrowserXSSFilter, ContentTypeNosniff: cfg.ContentTypeNosniff, @@ -107,6 +107,7 @@ func newSecure(next http.Handler, cfg dynamic.Headers) *secureHeader { SSLProxyHeaders: cfg.SSLProxyHeaders, STSSeconds: cfg.STSSeconds, FeaturePolicy: cfg.FeaturePolicy, + SecureContextKey: contextKey, } return &secureHeader{ diff --git a/pkg/middlewares/headers/headers_test.go b/pkg/middlewares/headers/headers_test.go index b5294aa59..18a7d2389 100644 --- a/pkg/middlewares/headers/headers_test.go +++ b/pkg/middlewares/headers/headers_test.go @@ -167,7 +167,9 @@ func TestSSLForceHost(t *testing.T) { SSLRedirect: true, SSLForceHost: true, SSLHost: "powpow.example.com", - }), + }, + "mymiddleware", + ), expected: http.StatusMovedPermanently, }, { @@ -177,7 +179,9 @@ func TestSSLForceHost(t *testing.T) { SSLRedirect: true, SSLForceHost: true, SSLHost: "powpow.example.com", - }), + }, + "mymiddleware", + ), expected: http.StatusMovedPermanently, }, { @@ -187,7 +191,9 @@ func TestSSLForceHost(t *testing.T) { SSLRedirect: true, SSLForceHost: true, SSLHost: "powpow.example.com", - }), + }, + "mymiddleware", + ), expected: http.StatusOK, }, { @@ -197,7 +203,9 @@ func TestSSLForceHost(t *testing.T) { SSLRedirect: true, SSLForceHost: true, SSLHost: "powpow.example.com", - }), + }, + "mymiddleware", + ), expected: http.StatusMovedPermanently, }, { @@ -207,7 +215,9 @@ func TestSSLForceHost(t *testing.T) { SSLRedirect: true, SSLForceHost: false, SSLHost: "powpow.example.com", - }), + }, + "mymiddleware", + ), expected: http.StatusMovedPermanently, }, { @@ -217,7 +227,9 @@ func TestSSLForceHost(t *testing.T) { SSLRedirect: true, SSLForceHost: false, SSLHost: "powpow.example.com", - }), + }, + "mymiddleware", + ), expected: http.StatusOK, }, } diff --git a/pkg/responsemodifiers/headers.go b/pkg/responsemodifiers/headers.go index 403785eea..3a6a47e25 100644 --- a/pkg/responsemodifiers/headers.go +++ b/pkg/responsemodifiers/headers.go @@ -8,7 +8,7 @@ import ( "github.com/unrolled/secure" ) -func buildHeaders(hdrs *dynamic.Headers) func(*http.Response) error { +func buildHeaders(hdrs *dynamic.Headers, contextKey string) func(*http.Response) error { opt := secure.Options{ BrowserXssFilter: hdrs.BrowserXSSFilter, ContentTypeNosniff: hdrs.ContentTypeNosniff, @@ -31,6 +31,7 @@ func buildHeaders(hdrs *dynamic.Headers) func(*http.Response) error { SSLProxyHeaders: hdrs.SSLProxyHeaders, STSSeconds: hdrs.STSSeconds, FeaturePolicy: hdrs.FeaturePolicy, + SecureContextKey: contextKey, } return func(resp *http.Response) error { diff --git a/pkg/responsemodifiers/response_modifier.go b/pkg/responsemodifiers/response_modifier.go index bc7b272cf..92b99d566 100644 --- a/pkg/responsemodifiers/response_modifier.go +++ b/pkg/responsemodifiers/response_modifier.go @@ -36,7 +36,7 @@ func (f *Builder) Build(ctx context.Context, names []string) func(*http.Response if conf.Headers != nil { getLogger(ctx, middleName, "Headers").Debug("Creating Middleware (ResponseModifier)") - modifiers = append(modifiers, buildHeaders(conf.Headers)) + modifiers = append(modifiers, buildHeaders(conf.Headers, middleName)) } else if conf.Chain != nil { chainCtx := provider.AddInContext(ctx, middleName) getLogger(chainCtx, middleName, "Chain").Debug("Creating Middleware (ResponseModifier)") diff --git a/pkg/responsemodifiers/response_modifier_test.go b/pkg/responsemodifiers/response_modifier_test.go index 9b3d7806b..8a53c92f7 100644 --- a/pkg/responsemodifiers/response_modifier_test.go +++ b/pkg/responsemodifiers/response_modifier_test.go @@ -62,7 +62,7 @@ func TestBuilderBuild(t *testing.T) { }) headerM := *middlewares["foo"].Headers - handler, err := headers.New(ctx, next, headerM, "secure") + handler, err := headers.New(ctx, next, headerM, "foo") require.NoError(t, err) handler.ServeHTTP(httptest.NewRecorder(),