diff --git a/_meta/beat.yml b/_meta/beat.yml index 4d202c081b3..6a236cfd1b3 100644 --- a/_meta/beat.yml +++ b/_meta/beat.yml @@ -245,6 +245,10 @@ apm-server: # "Content-Encoding", and "Accept" #allow_headers : [] + # Custom HTTP headers to add to RUM responses, e.g. for security policy compliance. + #response_headers : + # X-My-Header: Contents of the header + # 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|~" diff --git a/apm-server.docker.yml b/apm-server.docker.yml index ba225b8a0d7..84ced40a83b 100644 --- a/apm-server.docker.yml +++ b/apm-server.docker.yml @@ -245,6 +245,10 @@ apm-server: # "Content-Encoding", and "Accept" #allow_headers : [] + # Custom HTTP headers to add to RUM responses, e.g. for security policy compliance. + #response_headers : + # X-My-Header: Contents of the header + # 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|~" diff --git a/apm-server.yml b/apm-server.yml index 6a3f9141fea..b94f051c9b8 100644 --- a/apm-server.yml +++ b/apm-server.yml @@ -245,6 +245,10 @@ apm-server: # "Content-Encoding", and "Accept" #allow_headers : [] + # Custom HTTP headers to add to RUM responses, e.g. for security policy compliance. + #response_headers : + # X-My-Header: Contents of the header + # 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|~" diff --git a/beater/api/mux.go b/beater/api/mux.go index fb951cfe06e..b50af3ce827 100644 --- a/beater/api/mux.go +++ b/beater/api/mux.go @@ -213,6 +213,7 @@ func rumMiddleware(cfg *config.Config, _ *authorization.Handler, m map[request.R "Configure the `apm-server.rum` section in apm-server.yml to enable ingestion of RUM events. " + "If you are not using the RUM agent, you can safely ignore this error." rumMiddleware := append(apmMiddleware(m), + middleware.ResponseHeadersMiddleware(cfg.RumConfig.ResponseHeaders), middleware.SetRumFlagMiddleware(), middleware.SetIPRateLimitMiddleware(cfg.RumConfig.EventRate), middleware.CORSMiddleware(cfg.RumConfig.AllowOrigins, cfg.RumConfig.AllowHeaders), diff --git a/beater/config/rum.go b/beater/config/rum.go index 2f2fa94644e..8849a0020d9 100644 --- a/beater/config/rum.go +++ b/beater/config/rum.go @@ -43,13 +43,14 @@ const ( // RumConfig holds config information related to the RUM endpoint 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"` + Enabled *bool `config:"enabled"` + EventRate *EventRate `config:"event_rate"` + AllowOrigins []string `config:"allow_origins"` + AllowHeaders []string `config:"allow_headers"` + ResponseHeaders map[string][]string `config:"response_headers"` + LibraryPattern string `config:"library_pattern"` + ExcludeFromGrouping string `config:"exclude_from_grouping"` + SourceMapping *SourceMapping `config:"source_mapping"` BeatVersion string } diff --git a/beater/middleware/response_headers_middleware.go b/beater/middleware/response_headers_middleware.go new file mode 100644 index 00000000000..1efea7774ea --- /dev/null +++ b/beater/middleware/response_headers_middleware.go @@ -0,0 +1,36 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package middleware + +import ( + "github.com/elastic/apm-server/beater/request" +) + +// ResponseHeadersMiddleware sets the configured headers in the response +func ResponseHeadersMiddleware(headers map[string][]string) Middleware { + return func(h request.Handler) (request.Handler, error) { + return func(c *request.Context) { + for k, v := range headers { + for _, s := range v { + c.Header().Add(k, s) + } + } + h(c) + }, nil + } +} diff --git a/beater/middleware/response_headers_middleware_test.go b/beater/middleware/response_headers_middleware_test.go new file mode 100644 index 00000000000..22575489a7f --- /dev/null +++ b/beater/middleware/response_headers_middleware_test.go @@ -0,0 +1,35 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package middleware + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/apm-server/beater/beatertest" +) + +func TestResponseHeadersMiddleware(t *testing.T) { + h := map[string][]string{ + "X-Custom-Header": {"custom-header-value"}, + } + c, _ := beatertest.DefaultContextWithResponseRecorder() + Apply(ResponseHeadersMiddleware(h), beatertest.HandlerIdle)(c) + assert.Equal(t, "custom-header-value", c.Header().Get("X-Custom-Header")) +} diff --git a/changelogs/head.asciidoc b/changelogs/head.asciidoc index 77632d4522b..acb2b3dc0a7 100644 --- a/changelogs/head.asciidoc +++ b/changelogs/head.asciidoc @@ -14,3 +14,4 @@ https://github.com/elastic/apm-server/compare/7.8\...master[View commits] [float] ==== Added +* Support configurable response headers for the RUM endpoints {pull}3820[3820]