Allow multiple secure middlewares to operate independently
This commit is contained in:
parent
cb1d0441e9
commit
73513f8371
7 changed files with 99 additions and 11 deletions
33
integration/fixtures/headers/secure_multiple.toml
Normal file
33
integration/fixtures/headers/secure_multiple.toml
Normal 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"
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)")
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
Loading…
Reference in a new issue