diff --git a/command/server.go b/command/server.go index 32be0f197f..0aab187f04 100644 --- a/command/server.go +++ b/command/server.go @@ -944,8 +944,9 @@ CLUSTER_SYNTHESIS_COMPLETE: // Initialize the HTTP servers for _, ln := range lns { handler := vaulthttp.Handler(&vault.HandlerProperties{ - Core: core, - MaxRequestSize: ln.maxRequestSize, + Core: core, + MaxRequestSize: ln.maxRequestSize, + DisablePrintableCheck: config.DisablePrintableCheck, }) // We perform validation on the config earlier, we can just cast here diff --git a/command/server/config.go b/command/server/config.go index 7a5212aa99..beb77a1e3f 100644 --- a/command/server/config.go +++ b/command/server/config.go @@ -28,11 +28,13 @@ type Config struct { Seal *Seal `hcl:"-"` - CacheSize int `hcl:"cache_size"` - DisableCache bool `hcl:"-"` - DisableCacheRaw interface{} `hcl:"disable_cache"` - DisableMlock bool `hcl:"-"` - DisableMlockRaw interface{} `hcl:"disable_mlock"` + CacheSize int `hcl:"cache_size"` + DisableCache bool `hcl:"-"` + DisableCacheRaw interface{} `hcl:"disable_cache"` + DisableMlock bool `hcl:"-"` + DisableMlockRaw interface{} `hcl:"disable_mlock"` + DisablePrintableCheck bool `hcl:"-"` + DisablePrintableCheckRaw interface{} `hcl:"disable_printable_check"` EnableUI bool `hcl:"-"` EnableUIRaw interface{} `hcl:"ui"` @@ -391,6 +393,12 @@ func ParseConfig(d string, logger log.Logger) (*Config, error) { } } + if result.DisablePrintableCheckRaw != nil { + if result.DisablePrintableCheck, err = parseutil.ParseBool(result.DisablePrintableCheckRaw); err != nil { + return nil, err + } + } + if result.EnableRawEndpointRaw != nil { if result.EnableRawEndpoint, err = parseutil.ParseBool(result.EnableRawEndpointRaw); err != nil { return nil, err diff --git a/http/handler.go b/http/handler.go index 0939d1dd96..f5586ab8fb 100644 --- a/http/handler.go +++ b/http/handler.go @@ -117,7 +117,10 @@ func Handler(props *vault.HandlerProperties) http.Handler { // Wrap the handler with PrintablePathCheckHandler to check for non-printable // characters in the request path. - printablePathCheckHandler := cleanhttp.PrintablePathCheckHandler(genericWrappedHandler, nil) + printablePathCheckHandler := genericWrappedHandler + if !props.DisablePrintableCheck { + printablePathCheckHandler = cleanhttp.PrintablePathCheckHandler(genericWrappedHandler, nil) + } return printablePathCheckHandler } diff --git a/http/handler_test.go b/http/handler_test.go index ca9a945788..af19940695 100644 --- a/http/handler_test.go +++ b/http/handler_test.go @@ -410,11 +410,22 @@ func TestHandler_error(t *testing.T) { } func TestHandler_nonPrintableChars(t *testing.T) { + testNonPrintable(t, false) + testNonPrintable(t, true) +} + +func testNonPrintable(t *testing.T, disable bool) { core, _, token := vault.TestCoreUnsealed(t) - ln, addr := TestServer(t, core) + ln, addr := TestListener(t) + props := &vault.HandlerProperties{ + Core: core, + MaxRequestSize: DefaultMaxRequestSize, + DisablePrintableCheck: disable, + } + TestServerWithListenerAndProperties(t, ln, addr, core, props) defer ln.Close() - req, err := http.NewRequest("GET", addr+"/v1/sys/mounts\n", nil) + req, err := http.NewRequest("PUT", addr+"/v1/cubbyhole/foo\u2028bar", strings.NewReader(`{"zip": "zap"}`)) if err != nil { t.Fatalf("err: %s", err) } @@ -426,5 +437,9 @@ func TestHandler_nonPrintableChars(t *testing.T) { t.Fatalf("err: %s", err) } - testResponseStatus(t, resp, 400) + if disable { + testResponseStatus(t, resp, 204) + } else { + testResponseStatus(t, resp, 400) + } } diff --git a/http/testing.go b/http/testing.go index 13501f5daf..f390b5f42b 100644 --- a/http/testing.go +++ b/http/testing.go @@ -25,15 +25,12 @@ func TestListener(tb testing.TB) (net.Listener, string) { return ln, addr } -func TestServerWithListener(tb testing.TB, ln net.Listener, addr string, core *vault.Core) { +func TestServerWithListenerAndProperties(tb testing.TB, ln net.Listener, addr string, core *vault.Core, props *vault.HandlerProperties) { // Create a muxer to handle our requests so that we can authenticate // for tests. mux := http.NewServeMux() mux.Handle("/_test/auth", http.HandlerFunc(testHandleAuth)) - mux.Handle("/", Handler(&vault.HandlerProperties{ - Core: core, - MaxRequestSize: DefaultMaxRequestSize, - })) + mux.Handle("/", Handler(props)) server := &http.Server{ Addr: ln.Addr().String(), @@ -42,6 +39,16 @@ func TestServerWithListener(tb testing.TB, ln net.Listener, addr string, core *v go server.Serve(ln) } +func TestServerWithListener(tb testing.TB, ln net.Listener, addr string, core *vault.Core) { + // Create a muxer to handle our requests so that we can authenticate + // for tests. + props := &vault.HandlerProperties{ + Core: core, + MaxRequestSize: DefaultMaxRequestSize, + } + TestServerWithListenerAndProperties(tb, ln, addr, core, props) +} + func TestServer(tb testing.TB, core *vault.Core) (net.Listener, string) { ln, addr := TestListener(tb) TestServerWithListener(tb, ln, addr, core) diff --git a/vault/request_handling.go b/vault/request_handling.go index a6424b3626..f474e062ba 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -29,8 +29,9 @@ const ( // HanlderProperties is used to seed configuration into a vaulthttp.Handler. // It's in this package to avoid a circular dependency type HandlerProperties struct { - Core *Core - MaxRequestSize int64 + Core *Core + MaxRequestSize int64 + DisablePrintableCheck bool } // fetchEntityAndDerivedPolicies returns the entity object for the given entity