Middleware: Add Custom Headers to HTTP responses (#59018)

* Middleware: Add Custom Headers to HTTP responses

* Update docs/sources/setup-grafana/configure-grafana/_index.md

Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>

* Update conf/defaults.ini

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

* Update conf/sample.ini

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

* Update _index.md

Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
Co-authored-by: Dave Henderson <dave.henderson@grafana.com>
This commit is contained in:
João Calisto 2022-11-30 17:12:34 +00:00 committed by GitHub
parent 9cdb6b07c7
commit bba42b113c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 0 deletions

View File

@ -85,6 +85,11 @@ cdn_url =
# `0` means there is no timeout for reading the request.
read_timeout = 0
# This setting enables you to specify additional headers that the server adds to HTTP(S) responses.
[server.custom_response_headers]
#exampleHeader1 = exampleValue1
#exampleHeader2 = exampleValue2
#################################### Database ############################
[database]
# You can configure the database connection by specifying type, host, name, user and password

View File

@ -86,6 +86,11 @@
# `0` means there is no timeout for reading the request.
;read_timeout = 0
# This setting enables you to specify additional headers that the server adds to HTTP(S) responses.
[server.custom_response_headers]
#exampleHeader1 = exampleValue1
#exampleHeader2 = exampleValue2
#################################### Database ####################################
[database]
# You can configure the database connection by specifying type, host, name, user and password

View File

@ -292,6 +292,17 @@ Sets the maximum time using a duration format (5s/5m/5ms) before timing out read
<hr />
## [server.custom_response_headers]
This setting enables you to specify additional headers that the server adds to HTTP(S) responses.
```
exampleHeader1 = exampleValue1
exampleHeader2 = exampleValue2
```
<hr />
## [database]
Grafana needs a database to store users and dashboards (and other

View File

@ -598,6 +598,10 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
hs.mapStatic(m, hs.Cfg.ImagesDir, "", "/public/img/attachments")
}
if len(hs.Cfg.CustomResponseHeaders) > 0 {
m.Use(middleware.AddCustomResponseHeaders(hs.Cfg))
}
m.Use(middleware.AddDefaultResponseHeaders(hs.Cfg))
if hs.Cfg.ServeFromSubPath && hs.Cfg.AppSubURL != "" {

View File

@ -77,3 +77,21 @@ func addNoCacheHeaders(w web.ResponseWriter) {
func addXFrameOptionsDenyHeader(w web.ResponseWriter) {
w.Header().Set("X-Frame-Options", "deny")
}
func AddCustomResponseHeaders(cfg *setting.Cfg) web.Handler {
return func(c *web.Context) {
c.Resp.Before(func(w web.ResponseWriter) {
if w.Written() {
return
}
for header, value := range cfg.CustomResponseHeaders {
// do not override existing headers
if w.Header().Get(header) != "" {
continue
}
w.Header().Set(header, value)
}
})
}
}

View File

@ -514,6 +514,17 @@ func TestMiddlewareContext(t *testing.T) {
cfg.AnonymousOrgRole = string(org.RoleEditor)
})
middlewareScenario(t, "middleware should add custom response headers", func(t *testing.T, sc *scenarioContext) {
sc.fakeReq("GET", "/api/").exec()
assert.Regexp(t, "test", sc.resp.Header().Get("X-Custom-Header"))
assert.Regexp(t, "other-test", sc.resp.Header().Get("X-Other-Header"))
}, func(cfg *setting.Cfg) {
cfg.CustomResponseHeaders = map[string]string{
"X-Custom-Header": "test",
"X-Other-Header": "other-test",
}
})
t.Run("auth_proxy", func(t *testing.T) {
const userID int64 = 33
const orgID int64 = 4
@ -811,6 +822,7 @@ func middlewareScenario(t *testing.T, desc string, fn scenarioFunc, cbs ...func(
require.Truef(t, exists, "Views directory should exist at %q", viewsPath)
sc.m = web.New()
sc.m.Use(AddCustomResponseHeaders(cfg))
sc.m.Use(AddDefaultResponseHeaders(cfg))
sc.m.UseMiddleware(ContentSecurityPolicy(cfg, logger))
sc.m.UseMiddleware(web.Renderer(viewsPath, "[[", "]]"))

View File

@ -486,6 +486,8 @@ type Cfg struct {
GRPCServerNetwork string
GRPCServerAddress string
GRPCServerTLSConfig *tls.Config
CustomResponseHeaders map[string]string
}
type CommandLineArgs struct {
@ -1692,6 +1694,14 @@ func (cfg *Cfg) readServerSettings(iniFile *ini.File) error {
cfg.ReadTimeout = server.Key("read_timeout").MustDuration(0)
headersSection := cfg.Raw.Section("server.custom_response_headers")
keys := headersSection.Keys()
cfg.CustomResponseHeaders = make(map[string]string, len(keys))
for _, key := range keys {
cfg.CustomResponseHeaders[key.Name()] = key.Value()
}
return nil
}