Add Feature-Policy header support
This commit is contained in:
parent
c0ef5ce512
commit
cd164de776
15 changed files with 194 additions and 41 deletions
5
Gopkg.lock
generated
5
Gopkg.lock
generated
|
@ -1554,11 +1554,11 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "v1"
|
||||
digest = "1:60888cead16f066c948c078258b27f2885dce91cb6aadacf545b62a1ae1d08cb"
|
||||
digest = "1:ca2fa30e83e743bf24f13fbfa883e228955748ab59785b6ae9bd8a55a05bd157"
|
||||
name = "github.com/unrolled/secure"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "a1cf62cc2159fff407728f118c41aece76c397fa"
|
||||
revision = "716474489ad3d350e961ec6de19a6ab24e40f006"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a68c3f55d44d225da4f22ffbed2d8572d267cb19aaa1d60537769034ac66bc01"
|
||||
|
@ -2239,6 +2239,7 @@
|
|||
"github.com/docker/go-connections/sockets",
|
||||
"github.com/eapache/channels",
|
||||
"github.com/elazarl/go-bindata-assetfs",
|
||||
"github.com/fatih/structs",
|
||||
"github.com/gambol99/go-marathon",
|
||||
"github.com/go-acme/lego/certcrypto",
|
||||
"github.com/go-acme/lego/certificate",
|
||||
|
|
|
@ -370,6 +370,10 @@ The `publicKey` implements HPKP to prevent MITM attacks with forged certificates
|
|||
|
||||
The `referrerPolicy` allows sites to control when browsers will pass the Referer header to other sites.
|
||||
|
||||
### `featurePolicy`
|
||||
|
||||
The `featurePolicy` allows sites to control browser features.
|
||||
|
||||
### `isDevelopment`
|
||||
|
||||
Set `isDevelopment` to true when developing.
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
- "traefik.http.middlewares.middleware09.headers.isdevelopment=true"
|
||||
- "traefik.http.middlewares.middleware09.headers.publickey=foobar"
|
||||
- "traefik.http.middlewares.middleware09.headers.referrerpolicy=foobar"
|
||||
- "traefik.http.middlewares.middleware09.headers.featurepolicy=foobar"
|
||||
- "traefik.http.middlewares.middleware09.headers.sslforcehost=true"
|
||||
- "traefik.http.middlewares.middleware09.headers.sslhost=foobar"
|
||||
- "traefik.http.middlewares.middleware09.headers.sslproxyheaders.name0=foobar"
|
||||
|
|
|
@ -161,6 +161,7 @@
|
|||
contentSecurityPolicy = "foobar"
|
||||
publicKey = "foobar"
|
||||
referrerPolicy = "foobar"
|
||||
featurePolicy = "foobar"
|
||||
isDevelopment = true
|
||||
[http.middlewares.Middleware09.headers.customRequestHeaders]
|
||||
name0 = "foobar"
|
||||
|
|
|
@ -194,6 +194,7 @@ http:
|
|||
contentSecurityPolicy: foobar
|
||||
publicKey: foobar
|
||||
referrerPolicy: foobar
|
||||
featurePolicy: foobar
|
||||
isDevelopment: true
|
||||
Middleware10:
|
||||
ipWhiteList:
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
"traefik.http.middlewares.middleware09.headers.isdevelopment": "true",
|
||||
"traefik.http.middlewares.middleware09.headers.publickey": "foobar",
|
||||
"traefik.http.middlewares.middleware09.headers.referrerpolicy": "foobar",
|
||||
"traefik.http.middlewares.middleware09.headers.featurepolicy": "foobar",
|
||||
"traefik.http.middlewares.middleware09.headers.sslforcehost": "true",
|
||||
"traefik.http.middlewares.middleware09.headers.sslhost": "foobar",
|
||||
"traefik.http.middlewares.middleware09.headers.sslproxyheaders.name0": "foobar",
|
||||
|
|
34
integration/fixtures/headers/secure.toml
Normal file
34
integration/fixtures/headers/secure.toml
Normal file
|
@ -0,0 +1,34 @@
|
|||
[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 = ["secure"]
|
||||
service = "service1"
|
||||
|
||||
[http.routers.router2]
|
||||
rule = "Host(`test2.localhost`)"
|
||||
service = "service1"
|
||||
|
||||
[http.middlewares]
|
||||
[http.middlewares.secure.headers]
|
||||
featurePolicy = "vibrate 'none';"
|
||||
|
||||
[http.services]
|
||||
[http.services.service1.loadBalancer]
|
||||
[[http.services.service1.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:9000"
|
|
@ -113,3 +113,43 @@ func (s *HeadersSuite) TestCorsResponses(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/headers/secure.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{
|
||||
"Feature-Policy": {"vibrate 'none';"},
|
||||
},
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -384,6 +384,7 @@
|
|||
contentSecurityPolicy = "foobar"
|
||||
publicKey = "foobar"
|
||||
referrerPolicy = "foobar"
|
||||
featurePolicy = "foobar"
|
||||
isDevelopment = true
|
||||
[http.middlewares.Middleware8.headers.customRequestHeaders]
|
||||
name0 = "foobar"
|
||||
|
|
|
@ -167,6 +167,7 @@ type Headers struct {
|
|||
ContentSecurityPolicy string `json:"contentSecurityPolicy,omitempty" toml:"contentSecurityPolicy,omitempty" yaml:"contentSecurityPolicy,omitempty"`
|
||||
PublicKey string `json:"publicKey,omitempty" toml:"publicKey,omitempty" yaml:"publicKey,omitempty"`
|
||||
ReferrerPolicy string `json:"referrerPolicy,omitempty" toml:"referrerPolicy,omitempty" yaml:"referrerPolicy,omitempty"`
|
||||
FeaturePolicy string `json:"featurePolicy,omitempty" toml:"featurePolicy,omitempty" yaml:"featurePolicy,omitempty"`
|
||||
IsDevelopment bool `json:"isDevelopment,omitempty" toml:"isDevelopment,omitempty" yaml:"isDevelopment,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -208,6 +209,7 @@ func (h *Headers) HasSecureHeadersDefined() bool {
|
|||
h.ContentSecurityPolicy != "" ||
|
||||
h.PublicKey != "" ||
|
||||
h.ReferrerPolicy != "" ||
|
||||
h.FeaturePolicy != "" ||
|
||||
h.IsDevelopment)
|
||||
}
|
||||
|
||||
|
|
|
@ -376,6 +376,7 @@
|
|||
contentSecurityPolicy = "foobar"
|
||||
publicKey = "foobar"
|
||||
referrerPolicy = "foobar"
|
||||
featurePolicy = "foobar"
|
||||
isDevelopment = true
|
||||
[http.middlewares.Middleware8.headers.customRequestHeaders]
|
||||
name0 = "foobar"
|
||||
|
|
|
@ -63,6 +63,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
|||
"traefik.http.middlewares.Middleware8.headers.isdevelopment": "true",
|
||||
"traefik.http.middlewares.Middleware8.headers.publickey": "foobar",
|
||||
"traefik.http.middlewares.Middleware8.headers.referrerpolicy": "foobar",
|
||||
"traefik.http.middlewares.Middleware8.headers.featurepolicy": "foobar",
|
||||
"traefik.http.middlewares.Middleware8.headers.sslforcehost": "true",
|
||||
"traefik.http.middlewares.Middleware8.headers.sslhost": "foobar",
|
||||
"traefik.http.middlewares.Middleware8.headers.sslproxyheaders.name0": "foobar",
|
||||
|
@ -487,6 +488,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
|||
ContentSecurityPolicy: "foobar",
|
||||
PublicKey: "foobar",
|
||||
ReferrerPolicy: "foobar",
|
||||
FeaturePolicy: "foobar",
|
||||
IsDevelopment: true,
|
||||
},
|
||||
},
|
||||
|
@ -884,6 +886,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
|||
ContentSecurityPolicy: "foobar",
|
||||
PublicKey: "foobar",
|
||||
ReferrerPolicy: "foobar",
|
||||
FeaturePolicy: "foobar",
|
||||
IsDevelopment: true,
|
||||
},
|
||||
},
|
||||
|
@ -1020,6 +1023,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
|||
"traefik.HTTP.Middlewares.Middleware8.Headers.IsDevelopment": "true",
|
||||
"traefik.HTTP.Middlewares.Middleware8.Headers.PublicKey": "foobar",
|
||||
"traefik.HTTP.Middlewares.Middleware8.Headers.ReferrerPolicy": "foobar",
|
||||
"traefik.HTTP.Middlewares.Middleware8.Headers.FeaturePolicy": "foobar",
|
||||
"traefik.HTTP.Middlewares.Middleware8.Headers.SSLForceHost": "true",
|
||||
"traefik.HTTP.Middlewares.Middleware8.Headers.SSLHost": "foobar",
|
||||
"traefik.HTTP.Middlewares.Middleware8.Headers.SSLProxyHeaders.name0": "foobar",
|
||||
|
|
|
@ -29,7 +29,6 @@ func New(ctx context.Context, next http.Handler, config dynamic.Headers, name st
|
|||
// HeaderMiddleware -> SecureMiddleWare -> next
|
||||
logger := middlewares.GetLogger(ctx, name, typeName)
|
||||
logger.Debug("Creating middleware")
|
||||
|
||||
hasSecureHeaders := config.HasSecureHeadersDefined()
|
||||
hasCustomHeaders := config.HasCustomHeadersDefined()
|
||||
hasCorsHeaders := config.HasCorsHeadersDefined()
|
||||
|
@ -94,6 +93,7 @@ func newSecure(next http.Handler, headers dynamic.Headers) *secureHeader {
|
|||
HostsProxyHeaders: headers.HostsProxyHeaders,
|
||||
SSLProxyHeaders: headers.SSLProxyHeaders,
|
||||
STSSeconds: headers.STSSeconds,
|
||||
FeaturePolicy: headers.FeaturePolicy,
|
||||
}
|
||||
|
||||
return &secureHeader{
|
||||
|
|
|
@ -30,6 +30,7 @@ func buildHeaders(hdrs *dynamic.Headers) func(*http.Response) error {
|
|||
HostsProxyHeaders: hdrs.HostsProxyHeaders,
|
||||
SSLProxyHeaders: hdrs.SSLProxyHeaders,
|
||||
STSSeconds: hdrs.STSSeconds,
|
||||
FeaturePolicy: hdrs.FeaturePolicy,
|
||||
}
|
||||
|
||||
return func(resp *http.Response) error {
|
||||
|
|
125
vendor/github.com/unrolled/secure/secure.go
generated
vendored
125
vendor/github.com/unrolled/secure/secure.go
generated
vendored
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -20,8 +21,11 @@ const (
|
|||
xssProtectionHeader = "X-XSS-Protection"
|
||||
xssProtectionValue = "1; mode=block"
|
||||
cspHeader = "Content-Security-Policy"
|
||||
cspReportOnlyHeader = "Content-Security-Policy-Report-Only"
|
||||
hpkpHeader = "Public-Key-Pins"
|
||||
referrerPolicyHeader = "Referrer-Policy"
|
||||
featurePolicyHeader = "Feature-Policy"
|
||||
expectCTHeader = "Expect-CT"
|
||||
|
||||
ctxSecureHeaderKey = secureCtxKey("SecureResponseHeader")
|
||||
cspNonceSize = 16
|
||||
|
@ -61,6 +65,8 @@ type Options struct {
|
|||
STSPreload bool
|
||||
// ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
|
||||
ContentSecurityPolicy string
|
||||
// ContentSecurityPolicyReportOnly allows the Content-Security-Policy-Report-Only header value to be set with a custom value. Default is "".
|
||||
ContentSecurityPolicyReportOnly string
|
||||
// CustomBrowserXssValue allows the X-XSS-Protection header value to be set with a custom value. This overrides the BrowserXssFilter option. Default is "".
|
||||
CustomBrowserXssValue string // nolint: golint
|
||||
// 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.
|
||||
|
@ -71,10 +77,15 @@ type Options struct {
|
|||
PublicKey string
|
||||
// ReferrerPolicy allows sites to control when browsers will pass the Referer header to other sites. Default is "".
|
||||
ReferrerPolicy string
|
||||
// FeaturePolicy allows to selectively enable and disable use of various browser features and APIs. Default is "".
|
||||
FeaturePolicy string
|
||||
// SSLHost is the host name that is used to redirect http requests to https. Default is "", which indicates to use the same host.
|
||||
SSLHost string
|
||||
// AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
|
||||
AllowedHosts []string
|
||||
// AllowedHostsAreRegex determines, if the provided slice contains valid regular expressions. If this flag is set to true, every request's
|
||||
// host will be checked against these expressions. Default is false for backwards compatibility.
|
||||
AllowedHostsAreRegex bool
|
||||
// HostsProxyHeaders is a set of header keys that may hold a proxied hostname value for the request.
|
||||
HostsProxyHeaders []string
|
||||
// SSLHostFunc is a function pointer, the return value of the function is the host name that has same functionality as `SSHost`. Default is nil.
|
||||
|
@ -84,6 +95,8 @@ type Options struct {
|
|||
SSLProxyHeaders map[string]string
|
||||
// STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
|
||||
STSSeconds int64
|
||||
// ExpectCTHeader allows the Expect-CT header value to be set with a custom value. Default is "".
|
||||
ExpectCTHeader string
|
||||
}
|
||||
|
||||
// Secure is a middleware that helps setup a few basic security features. A single secure.Options struct can be
|
||||
|
@ -94,6 +107,10 @@ type Secure struct {
|
|||
|
||||
// badHostHandler is the handler used when an incorrect host is passed in.
|
||||
badHostHandler http.Handler
|
||||
|
||||
// cRegexAllowedHosts saves the compiled regular expressions of the AllowedHosts
|
||||
// option for subsequent use in processRequest
|
||||
cRegexAllowedHosts []*regexp.Regexp
|
||||
}
|
||||
|
||||
// New constructs a new Secure instance with the supplied options.
|
||||
|
@ -106,13 +123,27 @@ func New(options ...Options) *Secure {
|
|||
}
|
||||
|
||||
o.ContentSecurityPolicy = strings.Replace(o.ContentSecurityPolicy, "$NONCE", "'nonce-%[1]s'", -1)
|
||||
o.ContentSecurityPolicyReportOnly = strings.Replace(o.ContentSecurityPolicyReportOnly, "$NONCE", "'nonce-%[1]s'", -1)
|
||||
|
||||
o.nonceEnabled = strings.Contains(o.ContentSecurityPolicy, "%[1]s")
|
||||
o.nonceEnabled = strings.Contains(o.ContentSecurityPolicy, "%[1]s") || strings.Contains(o.ContentSecurityPolicyReportOnly, "%[1]s")
|
||||
|
||||
return &Secure{
|
||||
s := &Secure{
|
||||
opt: o,
|
||||
badHostHandler: http.HandlerFunc(defaultBadHostHandler),
|
||||
}
|
||||
|
||||
if s.opt.AllowedHostsAreRegex {
|
||||
// Test for invalid regular expressions in AllowedHosts
|
||||
for _, allowedHost := range o.AllowedHosts {
|
||||
regex, err := regexp.Compile(fmt.Sprintf("^%s$", allowedHost))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error parsing AllowedHost: %s", err))
|
||||
}
|
||||
s.cRegexAllowedHosts = append(s.cRegexAllowedHosts, regex)
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// SetBadHostHandler sets the handler to call when secure rejects the host name.
|
||||
|
@ -123,13 +154,10 @@ func (s *Secure) SetBadHostHandler(handler http.Handler) {
|
|||
// Handler implements the http.HandlerFunc for integration with the standard net/http lib.
|
||||
func (s *Secure) Handler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if s.opt.nonceEnabled {
|
||||
r = withCSPNonce(r, cspRandNonce())
|
||||
}
|
||||
|
||||
// Let secure process the request. If it returns an error,
|
||||
// that indicates the request should not continue.
|
||||
err := s.Process(w, r)
|
||||
responseHeader, r, err := s.processRequest(w, r)
|
||||
addResponseHeaders(responseHeader, w)
|
||||
|
||||
// If there was an error, do not continue.
|
||||
if err != nil {
|
||||
|
@ -144,13 +172,9 @@ func (s *Secure) Handler(h http.Handler) http.Handler {
|
|||
// Note that this is for requests only and will not write any headers.
|
||||
func (s *Secure) HandlerForRequestOnly(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if s.opt.nonceEnabled {
|
||||
r = withCSPNonce(r, cspRandNonce())
|
||||
}
|
||||
|
||||
// Let secure process the request. If it returns an error,
|
||||
// that indicates the request should not continue.
|
||||
responseHeader, err := s.processRequest(w, r)
|
||||
responseHeader, r, err := s.processRequest(w, r)
|
||||
|
||||
// If there was an error, do not continue.
|
||||
if err != nil {
|
||||
|
@ -167,13 +191,10 @@ func (s *Secure) HandlerForRequestOnly(h http.Handler) http.Handler {
|
|||
|
||||
// HandlerFuncWithNext is a special implementation for Negroni, but could be used elsewhere.
|
||||
func (s *Secure) HandlerFuncWithNext(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
if s.opt.nonceEnabled {
|
||||
r = withCSPNonce(r, cspRandNonce())
|
||||
}
|
||||
|
||||
// Let secure process the request. If it returns an error,
|
||||
// that indicates the request should not continue.
|
||||
err := s.Process(w, r)
|
||||
responseHeader, r, err := s.processRequest(w, r)
|
||||
addResponseHeaders(responseHeader, w)
|
||||
|
||||
// If there was an error, do not call next.
|
||||
if err == nil && next != nil {
|
||||
|
@ -184,13 +205,9 @@ func (s *Secure) HandlerFuncWithNext(w http.ResponseWriter, r *http.Request, nex
|
|||
// HandlerFuncWithNextForRequestOnly is a special implementation for Negroni, but could be used elsewhere.
|
||||
// Note that this is for requests only and will not write any headers.
|
||||
func (s *Secure) HandlerFuncWithNextForRequestOnly(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
if s.opt.nonceEnabled {
|
||||
r = withCSPNonce(r, cspRandNonce())
|
||||
}
|
||||
|
||||
// Let secure process the request. If it returns an error,
|
||||
// that indicates the request should not continue.
|
||||
responseHeader, err := s.processRequest(w, r)
|
||||
responseHeader, r, err := s.processRequest(w, r)
|
||||
|
||||
// If there was an error, do not call next.
|
||||
if err == nil && next != nil {
|
||||
|
@ -202,21 +219,37 @@ func (s *Secure) HandlerFuncWithNextForRequestOnly(w http.ResponseWriter, r *htt
|
|||
}
|
||||
}
|
||||
|
||||
// Process runs the actual checks and writes the headers in the ResponseWriter.
|
||||
func (s *Secure) Process(w http.ResponseWriter, r *http.Request) error {
|
||||
responseHeader, err := s.processRequest(w, r)
|
||||
// addResponseHeaders Adds the headers from 'responseHeader' to the response.
|
||||
func addResponseHeaders(responseHeader http.Header, w http.ResponseWriter) {
|
||||
if responseHeader != nil {
|
||||
for key, values := range responseHeader {
|
||||
for _, value := range values {
|
||||
w.Header().Add(key, value)
|
||||
w.Header().Set(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process runs the actual checks and writes the headers in the ResponseWriter.
|
||||
func (s *Secure) Process(w http.ResponseWriter, r *http.Request) error {
|
||||
responseHeader, _, err := s.processRequest(w, r)
|
||||
addResponseHeaders(responseHeader, w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ProcessNoModifyRequest runs the actual checks but does not write the headers in the ResponseWriter.
|
||||
func (s *Secure) ProcessNoModifyRequest(w http.ResponseWriter, r *http.Request) (http.Header, *http.Request, error) {
|
||||
return s.processRequest(w, r)
|
||||
}
|
||||
|
||||
// processRequest runs the actual checks on the request and returns an error if the middleware chain should stop.
|
||||
func (s *Secure) processRequest(w http.ResponseWriter, r *http.Request) (http.Header, error) {
|
||||
func (s *Secure) processRequest(w http.ResponseWriter, r *http.Request) (http.Header, *http.Request, error) {
|
||||
// Setup nonce if required.
|
||||
if s.opt.nonceEnabled {
|
||||
r = withCSPNonce(r, cspRandNonce())
|
||||
}
|
||||
|
||||
// Resolve the host for the request, using proxy headers if present.
|
||||
host := r.Host
|
||||
for _, header := range s.opt.HostsProxyHeaders {
|
||||
|
@ -229,16 +262,25 @@ func (s *Secure) processRequest(w http.ResponseWriter, r *http.Request) (http.He
|
|||
// Allowed hosts check.
|
||||
if len(s.opt.AllowedHosts) > 0 && !s.opt.IsDevelopment {
|
||||
isGoodHost := false
|
||||
if s.opt.AllowedHostsAreRegex {
|
||||
for _, allowedHost := range s.cRegexAllowedHosts {
|
||||
if match := allowedHost.MatchString(host); match {
|
||||
isGoodHost = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, allowedHost := range s.opt.AllowedHosts {
|
||||
if strings.EqualFold(allowedHost, host) {
|
||||
isGoodHost = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !isGoodHost {
|
||||
s.badHostHandler.ServeHTTP(w, r)
|
||||
return nil, fmt.Errorf("bad host name: %s", host)
|
||||
return nil, nil, fmt.Errorf("bad host name: %s", host)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,11 +307,11 @@ func (s *Secure) processRequest(w http.ResponseWriter, r *http.Request) (http.He
|
|||
}
|
||||
|
||||
http.Redirect(w, r, url.String(), status)
|
||||
return nil, fmt.Errorf("redirecting to HTTPS")
|
||||
return nil, nil, fmt.Errorf("redirecting to HTTPS")
|
||||
}
|
||||
|
||||
if s.opt.SSLForceHost {
|
||||
var SSLHost = host;
|
||||
var SSLHost = host
|
||||
if s.opt.SSLHostFunc != nil {
|
||||
if h := (*s.opt.SSLHostFunc)(host); len(h) > 0 {
|
||||
SSLHost = h
|
||||
|
@ -288,7 +330,7 @@ func (s *Secure) processRequest(w http.ResponseWriter, r *http.Request) (http.He
|
|||
}
|
||||
|
||||
http.Redirect(w, r, url.String(), status)
|
||||
return nil, fmt.Errorf("redirecting to HTTPS")
|
||||
return nil, nil, fmt.Errorf("redirecting to HTTPS")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,12 +385,31 @@ func (s *Secure) processRequest(w http.ResponseWriter, r *http.Request) (http.He
|
|||
}
|
||||
}
|
||||
|
||||
// Content Security Policy Report Only header.
|
||||
if len(s.opt.ContentSecurityPolicyReportOnly) > 0 {
|
||||
if s.opt.nonceEnabled {
|
||||
responseHeader.Set(cspReportOnlyHeader, fmt.Sprintf(s.opt.ContentSecurityPolicyReportOnly, CSPNonce(r.Context())))
|
||||
} else {
|
||||
responseHeader.Set(cspReportOnlyHeader, s.opt.ContentSecurityPolicyReportOnly)
|
||||
}
|
||||
}
|
||||
|
||||
// Referrer Policy header.
|
||||
if len(s.opt.ReferrerPolicy) > 0 {
|
||||
responseHeader.Set(referrerPolicyHeader, s.opt.ReferrerPolicy)
|
||||
}
|
||||
|
||||
return responseHeader, nil
|
||||
// Feature Policy header.
|
||||
if len(s.opt.FeaturePolicy) > 0 {
|
||||
responseHeader.Set(featurePolicyHeader, s.opt.FeaturePolicy)
|
||||
}
|
||||
|
||||
// Expect-CT header.
|
||||
if len(s.opt.ExpectCTHeader) > 0 {
|
||||
responseHeader.Set(expectCTHeader, s.opt.ExpectCTHeader)
|
||||
}
|
||||
|
||||
return responseHeader, r, nil
|
||||
}
|
||||
|
||||
// isSSL determine if we are on HTTPS.
|
||||
|
|
Loading…
Reference in a new issue