From 8f30b4751ed9ca800c3494a50b74366281ebe03f Mon Sep 17 00:00:00 2001 From: Vishal Nayak Date: Thu, 15 Dec 2016 17:53:07 -0500 Subject: [PATCH] Add 'no-store' response header from all the API outlets (#2183) --- http/handler.go | 21 +++++++++++++++++++-- http/handler_test.go | 33 +++++++++++++++++++++++++++++++++ http/help.go | 2 +- http/logical.go | 1 + vault/cluster.go | 6 ++++++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/http/handler.go b/http/handler.go index dc47ab32de..af77ddda93 100644 --- a/http/handler.go +++ b/http/handler.go @@ -61,9 +61,26 @@ func Handler(core *vault.Core) http.Handler { mux.Handle("/v1/", handleRequestForwarding(core, handleLogical(core, false, nil))) // Wrap the handler in another handler to trigger all help paths. - handler := handleHelpHandler(mux, core) + helpWrappedHandler := wrapHelpHandler(mux, core) - return handler + // Wrap the help wrapped handler with another layer with a generic + // handler + genericWrappedHandler := wrapGenericHandler(helpWrappedHandler) + + return genericWrappedHandler +} + +// wrapGenericHandler wraps the handler with an extra layer of handler where +// tasks that should be commonly handled for all the requests and/or responses +// are performed. +func wrapGenericHandler(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Set the Cache-Control header for all the responses returned + // by Vault + w.Header().Set("Cache-Control", "no-store") + h.ServeHTTP(w, r) + return + }) } // A lookup on a token that is about to expire returns nil, which means by the diff --git a/http/handler_test.go b/http/handler_test.go index 835d58fba7..a7b1ad3d03 100644 --- a/http/handler_test.go +++ b/http/handler_test.go @@ -13,6 +13,39 @@ import ( "github.com/hashicorp/vault/vault" ) +func TestHandler_CacheControlNoStore(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := TestServer(t, core) + defer ln.Close() + + req, err := http.NewRequest("GET", addr+"/v1/sys/mounts", nil) + if err != nil { + t.Fatalf("err: %s", err) + } + req.Header.Set(AuthHeaderName, token) + req.Header.Set(WrapTTLHeaderName, "60s") + + client := cleanhttp.DefaultClient() + resp, err := client.Do(req) + if err != nil { + t.Fatalf("err: %s", err) + } + + if resp == nil { + t.Fatalf("nil response") + } + + actual := resp.Header.Get("Cache-Control") + + if actual == "" { + t.Fatalf("missing 'Cache-Control' header entry in response writer") + } + + if actual != "no-store" { + t.Fatalf("bad: Cache-Control. Expected: 'no-store', Actual: %q", actual) + } +} + // We use this test to verify header auth func TestSysMounts_headerAuth(t *testing.T) { core, _, token := vault.TestCoreUnsealed(t) diff --git a/http/help.go b/http/help.go index 6c0b59c719..b7191a929a 100644 --- a/http/help.go +++ b/http/help.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/vault/vault" ) -func handleHelpHandler(h http.Handler, core *vault.Core) http.Handler { +func wrapHelpHandler(h http.Handler, core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { // If the help parameter is not blank, then show the help if v := req.URL.Query().Get("help"); v != "" || req.Method == "HELP" { diff --git a/http/logical.go b/http/logical.go index d21d192b96..9d83614ca9 100644 --- a/http/logical.go +++ b/http/logical.go @@ -260,6 +260,7 @@ func respondRaw(w http.ResponseWriter, r *http.Request, resp *logical.Response) if contentType != "" { w.Header().Set("Content-Type", contentType) } + w.WriteHeader(status) w.Write(body) } diff --git a/vault/cluster.go b/vault/cluster.go index 0f14f4e37a..798787277a 100644 --- a/vault/cluster.go +++ b/vault/cluster.go @@ -398,6 +398,12 @@ func WrapHandlerForClustering(handler http.Handler, logger log.Logger) func() (h } w.Header().Add("Content-Type", "application/json") + + // The response writer here is different from + // the one set in Vault's HTTP handler. + // Hence, set the Cache-Control explicitly. + w.Header().Set("Cache-Control", "no-store") + w.WriteHeader(http.StatusInternalServerError) type errorResponse struct {