Allow multiple secure middlewares to operate independently

This commit is contained in:
Daniel Tomcej 2020-07-01 01:42:04 -07:00 committed by GitHub
parent cb1d0441e9
commit 73513f8371
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 11 deletions

View file

@ -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"

View file

@ -153,3 +153,44 @@ func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) {
c.Assert(err, checker.IsNil) 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)
}
}

View file

@ -55,7 +55,7 @@ func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name strin
if hasSecureHeaders { if hasSecureHeaders {
logger.Debug("Setting up secureHeaders from %v", cfg) logger.Debug("Setting up secureHeaders from %v", cfg)
handler = newSecure(next, cfg) handler = newSecure(next, cfg, name)
nextHandler = handler nextHandler = handler
} }
@ -84,7 +84,7 @@ type secureHeader struct {
} }
// newSecure constructs a new secure instance with supplied options. // 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{ opt := secure.Options{
BrowserXssFilter: cfg.BrowserXSSFilter, BrowserXssFilter: cfg.BrowserXSSFilter,
ContentTypeNosniff: cfg.ContentTypeNosniff, ContentTypeNosniff: cfg.ContentTypeNosniff,
@ -107,6 +107,7 @@ func newSecure(next http.Handler, cfg dynamic.Headers) *secureHeader {
SSLProxyHeaders: cfg.SSLProxyHeaders, SSLProxyHeaders: cfg.SSLProxyHeaders,
STSSeconds: cfg.STSSeconds, STSSeconds: cfg.STSSeconds,
FeaturePolicy: cfg.FeaturePolicy, FeaturePolicy: cfg.FeaturePolicy,
SecureContextKey: contextKey,
} }
return &secureHeader{ return &secureHeader{

View file

@ -167,7 +167,9 @@ func TestSSLForceHost(t *testing.T) {
SSLRedirect: true, SSLRedirect: true,
SSLForceHost: true, SSLForceHost: true,
SSLHost: "powpow.example.com", SSLHost: "powpow.example.com",
}), },
"mymiddleware",
),
expected: http.StatusMovedPermanently, expected: http.StatusMovedPermanently,
}, },
{ {
@ -177,7 +179,9 @@ func TestSSLForceHost(t *testing.T) {
SSLRedirect: true, SSLRedirect: true,
SSLForceHost: true, SSLForceHost: true,
SSLHost: "powpow.example.com", SSLHost: "powpow.example.com",
}), },
"mymiddleware",
),
expected: http.StatusMovedPermanently, expected: http.StatusMovedPermanently,
}, },
{ {
@ -187,7 +191,9 @@ func TestSSLForceHost(t *testing.T) {
SSLRedirect: true, SSLRedirect: true,
SSLForceHost: true, SSLForceHost: true,
SSLHost: "powpow.example.com", SSLHost: "powpow.example.com",
}), },
"mymiddleware",
),
expected: http.StatusOK, expected: http.StatusOK,
}, },
{ {
@ -197,7 +203,9 @@ func TestSSLForceHost(t *testing.T) {
SSLRedirect: true, SSLRedirect: true,
SSLForceHost: true, SSLForceHost: true,
SSLHost: "powpow.example.com", SSLHost: "powpow.example.com",
}), },
"mymiddleware",
),
expected: http.StatusMovedPermanently, expected: http.StatusMovedPermanently,
}, },
{ {
@ -207,7 +215,9 @@ func TestSSLForceHost(t *testing.T) {
SSLRedirect: true, SSLRedirect: true,
SSLForceHost: false, SSLForceHost: false,
SSLHost: "powpow.example.com", SSLHost: "powpow.example.com",
}), },
"mymiddleware",
),
expected: http.StatusMovedPermanently, expected: http.StatusMovedPermanently,
}, },
{ {
@ -217,7 +227,9 @@ func TestSSLForceHost(t *testing.T) {
SSLRedirect: true, SSLRedirect: true,
SSLForceHost: false, SSLForceHost: false,
SSLHost: "powpow.example.com", SSLHost: "powpow.example.com",
}), },
"mymiddleware",
),
expected: http.StatusOK, expected: http.StatusOK,
}, },
} }

View file

@ -8,7 +8,7 @@ import (
"github.com/unrolled/secure" "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{ opt := secure.Options{
BrowserXssFilter: hdrs.BrowserXSSFilter, BrowserXssFilter: hdrs.BrowserXSSFilter,
ContentTypeNosniff: hdrs.ContentTypeNosniff, ContentTypeNosniff: hdrs.ContentTypeNosniff,
@ -31,6 +31,7 @@ func buildHeaders(hdrs *dynamic.Headers) func(*http.Response) error {
SSLProxyHeaders: hdrs.SSLProxyHeaders, SSLProxyHeaders: hdrs.SSLProxyHeaders,
STSSeconds: hdrs.STSSeconds, STSSeconds: hdrs.STSSeconds,
FeaturePolicy: hdrs.FeaturePolicy, FeaturePolicy: hdrs.FeaturePolicy,
SecureContextKey: contextKey,
} }
return func(resp *http.Response) error { return func(resp *http.Response) error {

View file

@ -36,7 +36,7 @@ func (f *Builder) Build(ctx context.Context, names []string) func(*http.Response
if conf.Headers != nil { if conf.Headers != nil {
getLogger(ctx, middleName, "Headers").Debug("Creating Middleware (ResponseModifier)") 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 { } else if conf.Chain != nil {
chainCtx := provider.AddInContext(ctx, middleName) chainCtx := provider.AddInContext(ctx, middleName)
getLogger(chainCtx, middleName, "Chain").Debug("Creating Middleware (ResponseModifier)") getLogger(chainCtx, middleName, "Chain").Debug("Creating Middleware (ResponseModifier)")

View file

@ -62,7 +62,7 @@ func TestBuilderBuild(t *testing.T) {
}) })
headerM := *middlewares["foo"].Headers headerM := *middlewares["foo"].Headers
handler, err := headers.New(ctx, next, headerM, "secure") handler, err := headers.New(ctx, next, headerM, "foo")
require.NoError(t, err) require.NoError(t, err)
handler.ServeHTTP(httptest.NewRecorder(), handler.ServeHTTP(httptest.NewRecorder(),