Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cors headers #2021

Merged
merged 135 commits into from
Jun 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
135 commits
Select commit Hold shift + click to select a range
389434c
Added function to add CORS headers and handle preflight requests.
Oct 10, 2016
cdd2ca8
Added missing required headers to the Access-Control-Allow-Headers he…
Oct 19, 2016
09282a4
Merge branch 'master' of github.com:naunga/vault into cors-headers
Oct 19, 2016
237a9af
Added config options to enable CORS and set a regexp for origins that…
Oct 19, 2016
f368d09
Reverted to master revision after moving things into more appropriate…
Oct 19, 2016
889ae29
Added wrapper function to apply CORS headers when configured to do so.
Oct 19, 2016
8c268e1
Added fields to Core and CoreConfig structs to enable CORS if desired…
Oct 19, 2016
5852bb3
Standardized on allowed origins instead of domains.
Oct 20, 2016
336ba06
Added CORS configuration options to the tests and associated files.
Oct 20, 2016
1fe3605
Merge branch 'master' of github.com:naunga/vault into cors-headers
Oct 20, 2016
88da2da
Exported the HandleCORS function and moved the invokation into the se…
Oct 20, 2016
d18ed18
This was not the right place to apply the CORS handler. Removed.
Oct 20, 2016
0f5a7fa
Added tests for requests that require CORS.
Oct 20, 2016
fd5494b
Finalized how and where to apply the CORS headers.
Oct 20, 2016
6c54ded
Setting the Origin header in order to test CORS handling.
Oct 20, 2016
9b2f66c
Removed AllowCORS() func and replaced it with CORSInfo() func. This m…
Oct 20, 2016
6747af0
Added configuration for CORS where required.
Oct 20, 2016
23490f1
Merge branch 'master' of github.com:naunga/vault into cors-headers
Oct 20, 2016
092b94d
Merge branch 'master' of github.com:naunga/vault into cors-headers
Nov 8, 2016
a8cca06
Reverting changes. CORS config to be done via a /sys endpoint.
Nov 9, 2016
6d80c03
Reverting changes. CORS config to be done via a /sys endpoint.
Nov 9, 2016
7ca440a
Refactored to align with the way things are going to be handled.
Nov 18, 2016
71048cf
Refactored to place access to the CORS settings behind the barrier in…
Nov 18, 2016
9a4c9f1
Move the logic to add the headers for CORS onto to the cors configura…
Nov 18, 2016
b5e45ba
Merge branch 'cors-headers' of github.com:naunga/vault into cors-headers
Nov 18, 2016
1171697
Merge branch 'master' of github.com:naunga/vault into cors-headers
Nov 18, 2016
71ec898
Added cors command.
Nov 23, 2016
4584a87
Moved to logic determine when to the apply the CORS headers to the Ap…
Nov 23, 2016
367a04f
Made the error returned by CORSConfig() a const.
Nov 23, 2016
ca2487f
Lots of refactoring and clean up.
Nov 23, 2016
4f6b25d
Refactoring and DRYing up.
Nov 23, 2016
3643a10
Added tests for the CORS functionality.
Nov 23, 2016
a98bba2
Initial commit.
Nov 23, 2016
e57ac5e
Initial commit.
Nov 23, 2016
5cbc9fe
Merge branch 'master' into cors-headers
Nov 23, 2016
ceb493e
Added help text.
Nov 24, 2016
9957df9
Merge branch 'master' into cors-headers
Nov 24, 2016
7aa839c
Fixed a bunch of logic bugs that were causing the server to panic aft…
Nov 28, 2016
a974a2b
Removed debug code.
Nov 29, 2016
ef2a522
Fixed logic bug.
Nov 29, 2016
b22172e
Added code to ensure the server responds correctly when the CORS requ…
Nov 29, 2016
dd06686
Skip handling the logical request for preflights.
Nov 29, 2016
0ad8ac3
Flesh out tests for CORS handler
Nov 29, 2016
796f0ef
Refactored so that the core is created with an empty CORSConfig vs. h…
Nov 29, 2016
f1a9225
Refactored ApplyHeaders() to return a 403 if the origin is not valid …
Nov 29, 2016
338fdb1
Merge branch 'master' into cors-headers
Nov 29, 2016
ec20fbc
Fixed typo in comments.
Nov 29, 2016
582189d
Removed (presently) unnecessary test.
Nov 30, 2016
121e0ed
Added documentation for CORS functionality.
Nov 30, 2016
a247952
Merge branch 'master' into cors-headers
Dec 2, 2016
50a0c11
Added a new custom header that tells the Vault server to bypass the C…
Dec 2, 2016
e5ff73f
CORS handler now applies to all paths. Added logic to bypass the CORS…
Dec 2, 2016
799cc8c
Disable() func now sets the core's CORS config to disabled and clears…
Dec 2, 2016
673365a
handleCORSDisable was setting the core's corsConfig property to nil i…
Dec 2, 2016
02edf8e
Merge branch 'master' into cors-headers
Dec 2, 2016
7122b8c
Fixed failing tests.
Dec 2, 2016
7eeba1e
Fixed a typo in a comment.
Dec 4, 2016
d3a5e45
Merge branch 'master' into cors-headers
Dec 4, 2016
5c05c80
Vary header will now be returned.
Dec 4, 2016
d809e4e
Merge branch 'master' into cors-headers
Dec 7, 2016
3bba275
Refactored code and updated documents to reflect that the allowed ori…
Dec 7, 2016
fd735ba
Merge branch 'master' into cors-headers
Dec 14, 2016
7b22c2a
Merged master and resolved conflicts.
Dec 16, 2016
3dba363
Merge branch 'master' into cors-headers
Dec 18, 2016
ff022b6
Merge branch 'master' into cors-headers
Dec 19, 2016
edab784
Merge branch 'master' into cors-headers
Dec 20, 2016
919e916
Merge branch 'master' into cors-headers
Jan 10, 2017
1e40dc8
Merge branch 'master' of github.com:naunga/vault into cors-headers
Jan 16, 2017
0031687
Merge branch 'master' into cors-headers
Jan 18, 2017
18bb96b
Merge branch 'master' into cors-headers
Jan 20, 2017
13bf4ae
Removed unnecessary header.
Jan 24, 2017
543cebd
Remove cors CLI command.
Jan 24, 2017
027e546
Merged the handleCORS() func into wrapCORSHandler(). Was not necessar…
Jan 24, 2017
d838b8e
Removed unnecessary header.
Jan 24, 2017
9d5fb2a
Move check for preflight requests up to the CORS handler where it bel…
Jan 24, 2017
75955d5
Removed unneeded struct fields. Changed pointers to slices to just sl…
Jan 24, 2017
e27b992
Refactored help text and code to reflect the fact that CORSConfig.all…
Jan 24, 2017
1dbb7e1
Merge branch 'master' of github.com:naunga/vault into cors-headers
Jan 24, 2017
8c0a7c9
New config store for api-managable configurations.
Jan 25, 2017
8ed7fe8
Added Settings field to Config struct to allow for many types of conf…
Jan 26, 2017
580ca52
Refactored to use new Config structure.
Jan 26, 2017
0ab733d
Added configStore to Core. Wired up unseal and seal tasks.
Jan 26, 2017
7645af4
Simplified things a bit.
Jan 26, 2017
1b48377
Added code to update the ConfigStore.
Jan 26, 2017
cb2ccd7
Merge branch 'master' into cors-headers
Jan 26, 2017
5f16cf7
Fixed a typo that was making the test fail.
Jan 26, 2017
70b9a9b
Added missing response headers. Renamed allowedHeaders to responseHea…
Jan 26, 2017
f70c6dc
Refactored newCORSConfig() out.
Jan 26, 2017
47f52cb
Merge branch 'master' into cors-headers
Jan 27, 2017
b7be8c5
Merge branch 'master' into cors-headers
Jan 30, 2017
0859a85
Moved logic of when to apply the CORS headers here. This makes the lo…
Jan 31, 2017
0968686
Refactored CORS handler test to reflect the current state of things.
Jan 31, 2017
26af710
It is not necessary to set the isEnabled flag on the CORSConfig. This…
Jan 31, 2017
4ac6a0b
Creating an instance of a sync.RWMutex for the CORSConfig when the co…
Jan 31, 2017
e1afa97
Renamed responseHeaders to preflightHeaders. Added 'LIST' method to l…
Jan 31, 2017
4b5a32a
Renamed handleCORSEnable, Disable, and Status to handleCORSUpdate, De…
Jan 31, 2017
3127933
Changed docs to reflect the fact that the CORS Update and Delete func…
Jan 31, 2017
2bfa2e7
Merge branch 'master' of github.com:naunga/vault into cors-headers
Jan 31, 2017
c3447f4
Refactor test to reflect the fact that the CORS Update and Delete met…
Jan 31, 2017
014ab2e
Merge branch 'master' into cors-headers
Feb 1, 2017
10a5976
Merge branch 'master' into cors-headers
Feb 5, 2017
54ce27b
Merged handleCORSRead() and corsStatusResponse() into a single func.
Feb 5, 2017
6bc49a5
Moved logic to determine when to apply the CORS headers to the http h…
Feb 7, 2017
bb5d81d
Removed the config store.
Feb 7, 2017
9afc279
Gave the test a URL that is actually valid.
Feb 7, 2017
3b0a32d
Refactored after removing the ConfigStore.
Feb 7, 2017
d8a689a
Exported the Enabled and AllowedOrigins fields of the CORSConfig stru…
Feb 7, 2017
077dfad
Updated field names.
Feb 7, 2017
7f67976
Merge branch 'master' into cors-headers
Feb 8, 2017
3d94364
Merge branch 'master' of github.com:naunga/vault into cors-headers
Feb 20, 2017
46d451b
Merge branch 'master' into cors-headers
Feb 28, 2017
fca8f36
Moved the code to put the headers on the response here. Instead of ma…
Mar 1, 2017
468fda3
CORSConfig.Get() was removed. Updated to reflect that. Fixed a typo i…
Mar 1, 2017
2a5a5ef
Removed ApplyHeaders and Get(). Applied correct locks where needed. F…
Mar 1, 2017
f6f18b8
Was missing a return after writing the headers for a preflight reques…
Mar 3, 2017
1adc2d7
Merge branch 'master' into cors-headers
Mar 3, 2017
e7f4662
Merge branch 'master' into cors-headers
Mar 8, 2017
48dd63f
Merge branch 'master' into cors-headers
Mar 8, 2017
d26b641
Merge branch 'master' into cors-headers
Mar 9, 2017
e55caae
Merge branch 'master' into cors-headers
Mar 14, 2017
7da6630
Merge branch 'master' into cors-headers
Mar 20, 2017
54314e0
Merge branch 'master' into cors-headers
Apr 10, 2017
ec019db
Merge branch 'master' of github.com:naunga/vault into cors-headers
May 6, 2017
117326a
Merge branch 'master' of github.com:naunga/vault into cors-headers
May 10, 2017
607d2d5
Updated imports.
May 10, 2017
ed209aa
Made parameter to Enable a string slice.
May 10, 2017
bd48453
Fixed a typo.
May 10, 2017
1bcdf0f
Merge branch 'master' of github.com:naunga/vault into cors-headers
May 10, 2017
b5e9b84
Corrected parameter to func Enable.
May 10, 2017
4fbeadb
Added Access-Control-Max-Age header to list of expected headers.
May 10, 2017
8c9b4e2
Enable func returns an error if the list of URLs is empty. Reworded e…
May 10, 2017
79a87ea
Merge branch 'master' of github.com:naunga/vault into cors-headers
May 10, 2017
370585b
Merge branch 'master' of github.com:naunga/vault into cors-headers
Jun 15, 2017
3403c2d
Merge branch 'master' of github.com:naunga/vault into cors-headers
Jun 15, 2017
b7be115
Merge branch 'master' of github.com:naunga/vault into cors-headers
Jun 16, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions api/sys_config_cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package api

func (c *Sys) CORSStatus() (*CORSResponse, error) {
r := c.c.NewRequest("GET", "/v1/sys/config/cors")
resp, err := c.c.RawRequest(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()

var result CORSResponse
err = resp.DecodeJSON(&result)
return &result, err
}

func (c *Sys) ConfigureCORS(req *CORSRequest) (*CORSResponse, error) {
r := c.c.NewRequest("PUT", "/v1/sys/config/cors")
if err := r.SetJSONBody(req); err != nil {
return nil, err
}

resp, err := c.c.RawRequest(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()

var result CORSResponse
err = resp.DecodeJSON(&result)
return &result, err
}

func (c *Sys) DisableCORS() (*CORSResponse, error) {
r := c.c.NewRequest("DELETE", "/v1/sys/config/cors")

resp, err := c.c.RawRequest(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()

var result CORSResponse
err = resp.DecodeJSON(&result)
return &result, err

}

type CORSRequest struct {
AllowedOrigins string `json:"allowed_origins"`
Enabled bool `json:"enabled"`
}

type CORSResponse struct {
AllowedOrigins string `json:"allowed_origins"`
Enabled bool `json:"enabled"`
}
1 change: 0 additions & 1 deletion cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ func Commands(metaPtr *meta.Meta) map[string]cli.CommandFactory {
Meta: *metaPtr,
}, nil
},

"server": func() (cli.Command, error) {
return &command.ServerCommand{
Meta: *metaPtr,
Expand Down
68 changes: 68 additions & 0 deletions http/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package http

import (
"net/http"
"strings"

"github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/vault"
)

var preflightHeaders = map[string]string{
"Access-Control-Allow-Headers": "*",
"Access-Control-Max-Age": "300",
}

var allowedMethods = []string{
http.MethodDelete,
http.MethodGet,
http.MethodOptions,
http.MethodPost,
http.MethodPut,
"LIST", // LIST is not an official HTTP method, but Vault supports it.
}

func wrapCORSHandler(h http.Handler, core *vault.Core) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
corsConf := core.CORSConfig()

origin := req.Header.Get("Origin")
requestMethod := req.Header.Get("Access-Control-Request-Method")

// If CORS is not enabled or if no Origin header is present (i.e. the request
// is from the Vault CLI. A browser will always send an Origin header), then
// just return a 204.
if !corsConf.IsEnabled() || origin == "" {
h.ServeHTTP(w, req)
return
}

// Return a 403 if the origin is not
// allowed to make cross-origin requests.
if !corsConf.IsValidOrigin(origin) {
w.WriteHeader(http.StatusForbidden)
return
}

if req.Method == http.MethodOptions && !strutil.StrListContains(allowedMethods, requestMethod) {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Vary", "Origin")

// apply headers for preflight requests
if req.Method == http.MethodOptions {
w.Header().Set("Access-Control-Allow-Methods", strings.Join(allowedMethods, ","))

for k, v := range preflightHeaders {
w.Header().Set(k, v)
}
return
}

h.ServeHTTP(w, req)
return
})
}
3 changes: 2 additions & 1 deletion http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@ func Handler(core *vault.Core) http.Handler {

// Wrap the handler in another handler to trigger all help paths.
helpWrappedHandler := wrapHelpHandler(mux, core)
corsWrappedHandler := wrapCORSHandler(helpWrappedHandler, core)

// Wrap the help wrapped handler with another layer with a generic
// handler
genericWrappedHandler := wrapGenericHandler(helpWrappedHandler)
genericWrappedHandler := wrapGenericHandler(corsWrappedHandler)

return genericWrappedHandler
}
Expand Down
81 changes: 81 additions & 0 deletions http/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,87 @@ import (
"github.com/hashicorp/vault/vault"
)

func TestHandler_cors(t *testing.T) {
core, _, _ := vault.TestCoreUnsealed(t)
ln, addr := TestServer(t, core)
defer ln.Close()

// Enable CORS and allow from any origin for testing.
corsConfig := core.CORSConfig()
err := corsConfig.Enable([]string{addr})
if err != nil {
t.Fatalf("Error enabling CORS: %s", err)
}

req, err := http.NewRequest(http.MethodOptions, addr+"/v1/sys/seal-status", nil)
if err != nil {
t.Fatalf("err: %s", err)
}
req.Header.Set("Origin", "BAD ORIGIN")

// Requests from unacceptable origins will be rejected with a 403.
client := cleanhttp.DefaultClient()
resp, err := client.Do(req)
if err != nil {
t.Fatalf("err: %s", err)
}

if resp.StatusCode != http.StatusForbidden {
t.Fatalf("Bad status:\nexpected: 403 Forbidden\nactual: %s", resp.Status)
}

//
// Test preflight requests
//

// Set a valid origin
req.Header.Set("Origin", addr)

// Server should NOT accept arbitrary methods.
req.Header.Set("Access-Control-Request-Method", "FOO")

client = cleanhttp.DefaultClient()
resp, err = client.Do(req)
if err != nil {
t.Fatalf("err: %s", err)
}

// Fail if an arbitrary method is accepted.
if resp.StatusCode != http.StatusMethodNotAllowed {
t.Fatalf("Bad status:\nexpected: 405 Method Not Allowed\nactual: %s", resp.Status)
}

// Server SHOULD accept acceptable methods.
req.Header.Set("Access-Control-Request-Method", http.MethodPost)

client = cleanhttp.DefaultClient()
resp, err = client.Do(req)
if err != nil {
t.Fatalf("err: %s", err)
}

//
// Test that the CORS headers are applied correctly.
//
expHeaders := map[string]string{
"Access-Control-Allow-Origin": addr,
"Access-Control-Allow-Headers": "*",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also add Access-Control-Max-Age here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can indeed.

"Access-Control-Max-Age": "300",
"Vary": "Origin",
}

for expHeader, expected := range expHeaders {
actual := resp.Header.Get(expHeader)
if actual == "" {
t.Fatalf("bad:\nHeader: %#v was not on response.", expHeader)
}

if actual != expected {
t.Fatalf("bad:\nExpected: %#v\nActual: %#v\n", expected, actual)
}
}
}

func TestHandler_CacheControlNoStore(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := TestServer(t, core)
Expand Down
6 changes: 6 additions & 0 deletions http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net/http"
"regexp"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -55,6 +56,11 @@ func testHttpData(t *testing.T, method string, token string, addr string, body i
t.Fatalf("err: %s", err)
}

// Get the address of the local listener in order to attach it to an Origin header.
// This will allow for the testing of requests that require CORS, without using a browser.
hostURLRegexp, _ := regexp.Compile("http[s]?://.+:[0-9]+")
req.Header.Set("Origin", hostURLRegexp.FindString(addr))

req.Header.Set("Content-Type", "application/json")

if len(token) != 0 {
Expand Down
1 change: 1 addition & 0 deletions http/logical.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques
op = logical.UpdateOperation
case "LIST":
op = logical.ListOperation
case "OPTIONS":
default:
return nil, http.StatusMethodNotAllowed, nil
}
Expand Down
15 changes: 15 additions & 0 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,9 @@ type Core struct {
// The grpc forwarding client
rpcForwardingClient *forwardingClient

// CORS Information
corsConfig *CORSConfig

// replicationState keeps the current replication state cached for quick
// lookup
replicationState consts.ReplicationState
Expand Down Expand Up @@ -447,6 +450,7 @@ func NewCore(conf *CoreConfig) (*Core, error) {
clusterName: conf.ClusterName,
clusterListenerShutdownCh: make(chan struct{}),
clusterListenerShutdownSuccessCh: make(chan struct{}),
corsConfig: &CORSConfig{},
clusterPeerClusterAddrsCache: cache.New(3*heartbeatInterval, time.Second),
enableMlock: !conf.DisableMlock,
}
Expand Down Expand Up @@ -555,6 +559,11 @@ func (c *Core) Shutdown() error {
return c.sealInternal()
}

// CORSConfig returns the current CORS configuration
func (c *Core) CORSConfig() *CORSConfig {
return c.corsConfig
}

// LookupToken returns the properties of the token from the token store. This
// is particularly useful to fetch the accessor of the client token and get it
// populated in the logical request along with the client token. The accessor
Expand Down Expand Up @@ -1291,6 +1300,9 @@ func (c *Core) postUnseal() (retErr error) {
if err := c.setupPolicyStore(); err != nil {
return err
}
if err := c.loadCORSConfig(); err != nil {
return err
}
if err := c.loadCredentials(); err != nil {
return err
}
Expand Down Expand Up @@ -1356,6 +1368,9 @@ func (c *Core) preSeal() error {
if err := c.teardownPolicyStore(); err != nil {
result = multierror.Append(result, errwrap.Wrapf("error tearing down policy store: {{err}}", err))
}
if err := c.saveCORSConfig(); err != nil {
result = multierror.Append(result, errwrap.Wrapf("error tearing down CORS config: {{err}}", err))
}
if err := c.stopRollback(); err != nil {
result = multierror.Append(result, errwrap.Wrapf("error stopping rollback: {{err}}", err))
}
Expand Down
Loading