Skip to content

Commit

Permalink
Make list of Access-Control-Allow-Headers configurable. (#3307)
Browse files Browse the repository at this point in the history
The configurable list is appended to Content-Type, Content-Encoding and Accept, which are allways included.
Closes #2497
  • Loading branch information
jalvz authored Feb 11, 2020
1 parent fdda318 commit ef7ea51
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 10 deletions.
4 changes: 4 additions & 0 deletions _meta/beat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ apm-server:
# If an item in the list is a single '*', everything will be allowed.
#allow_origins : ['*']

# A list of Access-Control-Allow-Headers to allow RUM requests, in addition to "Content-Type",
# "Content-Encoding", and "Accept"
#allow_headers : []

# Regexp to be matched against a stacktrace frame's `file_name` and `abs_path` attributes.
# If the regexp matches, the stacktrace frame is considered to be a library frame.
#library_pattern: "node_modules|bower_components|~"
Expand Down
4 changes: 4 additions & 0 deletions apm-server.docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ apm-server:
# If an item in the list is a single '*', everything will be allowed.
#allow_origins : ['*']

# A list of Access-Control-Allow-Headers to allow RUM requests, in addition to "Content-Type",
# "Content-Encoding", and "Accept"
#allow_headers : []

# Regexp to be matched against a stacktrace frame's `file_name` and `abs_path` attributes.
# If the regexp matches, the stacktrace frame is considered to be a library frame.
#library_pattern: "node_modules|bower_components|~"
Expand Down
4 changes: 4 additions & 0 deletions apm-server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ apm-server:
# If an item in the list is a single '*', everything will be allowed.
#allow_origins : ['*']

# A list of Access-Control-Allow-Headers to allow RUM requests, in addition to "Content-Type",
# "Content-Encoding", and "Accept"
#allow_headers : []

# Regexp to be matched against a stacktrace frame's `file_name` and `abs_path` attributes.
# If the regexp matches, the stacktrace frame is considered to be a library frame.
#library_pattern: "node_modules|bower_components|~"
Expand Down
2 changes: 1 addition & 1 deletion beater/api/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func rumMiddleware(cfg *config.Config, _ *authorization.Handler, m map[request.R
return append(apmMiddleware(m),
middleware.SetRumFlagMiddleware(),
middleware.SetIPRateLimitMiddleware(cfg.RumConfig.EventRate),
middleware.CORSMiddleware(cfg.RumConfig.AllowOrigins),
middleware.CORSMiddleware(cfg.RumConfig.AllowOrigins, cfg.RumConfig.AllowHeaders),
middleware.KillSwitchMiddleware(cfg.RumConfig.IsEnabled(), msg))
}

Expand Down
3 changes: 3 additions & 0 deletions beater/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func Test_UnpackConfig(t *testing.T) {
"lru_size": 2000,
},
"allow_origins": []string{"example*"},
"allow_headers": []string{"Authorization"},
"source_mapping": map[string]interface{}{
"cache": map[string]interface{}{
"expiration": 8 * time.Minute,
Expand Down Expand Up @@ -135,6 +136,7 @@ func Test_UnpackConfig(t *testing.T) {
LruSize: 2000,
},
AllowOrigins: []string{"example*"},
AllowHeaders: []string{"Authorization"},
SourceMapping: &SourceMapping{
Cache: &Cache{Expiration: 8 * time.Minute},
IndexPattern: "apm-test*",
Expand Down Expand Up @@ -239,6 +241,7 @@ func Test_UnpackConfig(t *testing.T) {
LruSize: 1000,
},
AllowOrigins: []string{"*"},
AllowHeaders: []string{},
SourceMapping: &SourceMapping{
Cache: &Cache{
Expiration: 7 * time.Second,
Expand Down
2 changes: 2 additions & 0 deletions beater/config/rum.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type RumConfig struct {
Enabled *bool `config:"enabled"`
EventRate *EventRate `config:"event_rate"`
AllowOrigins []string `config:"allow_origins"`
AllowHeaders []string `config:"allow_headers"`
LibraryPattern string `config:"library_pattern"`
ExcludeFromGrouping string `config:"exclude_from_grouping"`
SourceMapping *SourceMapping `config:"source_mapping"`
Expand Down Expand Up @@ -146,6 +147,7 @@ func defaultRum(beatVersion string) *RumConfig {
LruSize: defaultEventRateLRUSize,
},
AllowOrigins: []string{allowAllOrigins},
AllowHeaders: []string{},
SourceMapping: &SourceMapping{
Cache: &Cache{
Expiration: defaultSourcemapCacheExpiration,
Expand Down
7 changes: 4 additions & 3 deletions beater/middleware/cors_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ import (
)

var (
supportedHeaders = strings.Join([]string{headers.ContentType, headers.ContentEncoding, headers.Accept}, ", ")
supportedHeaders = []string{headers.ContentType, headers.ContentEncoding, headers.Accept}
supportedMethods = strings.Join([]string{http.MethodPost, http.MethodOptions}, ", ")
)

// CORSMiddleware returns a middleware serving preflight OPTION requests and terminating requests if they do not
// match the required valid origin.
func CORSMiddleware(allowedOrigins []string) Middleware {
func CORSMiddleware(allowedOrigins, allowedHeaders []string) Middleware {
var isAllowed = func(origin string) bool {
for _, allowed := range allowedOrigins {
if glob.Glob(allowed, origin) {
Expand Down Expand Up @@ -65,7 +65,8 @@ func CORSMiddleware(allowedOrigins []string) Middleware {

// required if Access-Control-Request-Method and Access-Control-Request-Headers are in the requestHeaders
c.Header().Set(headers.AccessControlAllowMethods, supportedMethods)
c.Header().Set(headers.AccessControlAllowHeaders, supportedHeaders)
h := append(allowedHeaders, supportedHeaders...)
c.Header().Set(headers.AccessControlAllowHeaders, strings.Join(h, ", "))

c.Header().Set(headers.AccessControlExposeHeaders, headers.Etag)

Expand Down
18 changes: 12 additions & 6 deletions beater/middleware/cors_middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ import (

func TestCORSMiddleware(t *testing.T) {

cors := func(origin string, allowedOrigins []string, m string) (*request.Context, *httptest.ResponseRecorder) {
cors := func(origin string, allowedOrigins, allowedHeaders []string, m string) (*request.Context, *httptest.ResponseRecorder) {
c, rec := beatertest.ContextWithResponseRecorder(m, "/")
c.Request.Header.Set(headers.Origin, origin)
Apply(CORSMiddleware(allowedOrigins), beatertest.Handler202)(c)
Apply(CORSMiddleware(allowedOrigins, allowedHeaders), beatertest.Handler202)(c)
return c, rec
}

Expand All @@ -50,19 +50,19 @@ func TestCORSMiddleware(t *testing.T) {
}

t.Run("OPTIONSValidOrigin", func(t *testing.T) {
_, rec := cors("wxyz", []string{"w*yz"}, http.MethodOptions)
_, rec := cors("wxyz", []string{"w*yz"}, nil, http.MethodOptions)
checkPreflightHeaders(t, rec)
assert.Equal(t, "wxyz", rec.Header().Get(headers.AccessControlAllowOrigin))
})
t.Run("OPTIONSInvalidOrigin", func(t *testing.T) {
_, rec := cors("xyz", []string{"xy.*i"}, http.MethodOptions)
_, rec := cors("xyz", []string{"xy.*i"}, nil, http.MethodOptions)
checkPreflightHeaders(t, rec)
assert.Empty(t, rec.Header().Get(headers.AccessControlAllowOrigin))
})

t.Run("GETAllowedOrigins", func(t *testing.T) {
for _, origin := range []string{"", "wxyz", "testingx"} {
_, rec := cors(origin, []string{"*", "testing.*"}, http.MethodPost)
_, rec := cors(origin, []string{"*", "testing.*"}, nil, http.MethodPost)

assert.Equal(t, http.StatusAccepted, rec.Code)
assert.Equal(t, origin, rec.Header().Get(headers.AccessControlAllowOrigin))
Expand All @@ -71,7 +71,7 @@ func TestCORSMiddleware(t *testing.T) {

t.Run("GETForbidden", func(t *testing.T) {
for _, origin := range []string{"", "wxyz", "test"} {
_, rec := cors(origin, []string{"xyz", "testing.*"}, http.MethodPost)
_, rec := cors(origin, []string{"xyz", "testing.*"}, nil, http.MethodPost)

assert.Equal(t, http.StatusForbidden, rec.Code)
assert.Equal(t,
Expand All @@ -81,4 +81,10 @@ func TestCORSMiddleware(t *testing.T) {
rec.Body.String())
}
})

t.Run("AllowedHeaders", func(t *testing.T) {
_, rec := cors("xyz", []string{"xyz"}, []string{"Authorization"}, http.MethodOptions)
assert.Contains(t, rec.Header().Get(headers.AccessControlAllowHeaders), "Authorization")
})

}

0 comments on commit ef7ea51

Please sign in to comment.