mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
9cdb6b07c7
commit
bba42b113c
@ -85,6 +85,11 @@ cdn_url =
|
|||||||
# `0` means there is no timeout for reading the request.
|
# `0` means there is no timeout for reading the request.
|
||||||
read_timeout = 0
|
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 ############################
|
||||||
[database]
|
[database]
|
||||||
# You can configure the database connection by specifying type, host, name, user and password
|
# You can configure the database connection by specifying type, host, name, user and password
|
||||||
|
@ -86,6 +86,11 @@
|
|||||||
# `0` means there is no timeout for reading the request.
|
# `0` means there is no timeout for reading the request.
|
||||||
;read_timeout = 0
|
;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 ####################################
|
||||||
[database]
|
[database]
|
||||||
# You can configure the database connection by specifying type, host, name, user and password
|
# You can configure the database connection by specifying type, host, name, user and password
|
||||||
|
@ -292,6 +292,17 @@ Sets the maximum time using a duration format (5s/5m/5ms) before timing out read
|
|||||||
|
|
||||||
<hr />
|
<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]
|
## [database]
|
||||||
|
|
||||||
Grafana needs a database to store users and dashboards (and other
|
Grafana needs a database to store users and dashboards (and other
|
||||||
|
@ -598,6 +598,10 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
|
|||||||
hs.mapStatic(m, hs.Cfg.ImagesDir, "", "/public/img/attachments")
|
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))
|
m.Use(middleware.AddDefaultResponseHeaders(hs.Cfg))
|
||||||
|
|
||||||
if hs.Cfg.ServeFromSubPath && hs.Cfg.AppSubURL != "" {
|
if hs.Cfg.ServeFromSubPath && hs.Cfg.AppSubURL != "" {
|
||||||
|
@ -77,3 +77,21 @@ func addNoCacheHeaders(w web.ResponseWriter) {
|
|||||||
func addXFrameOptionsDenyHeader(w web.ResponseWriter) {
|
func addXFrameOptionsDenyHeader(w web.ResponseWriter) {
|
||||||
w.Header().Set("X-Frame-Options", "deny")
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -514,6 +514,17 @@ func TestMiddlewareContext(t *testing.T) {
|
|||||||
cfg.AnonymousOrgRole = string(org.RoleEditor)
|
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) {
|
t.Run("auth_proxy", func(t *testing.T) {
|
||||||
const userID int64 = 33
|
const userID int64 = 33
|
||||||
const orgID int64 = 4
|
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)
|
require.Truef(t, exists, "Views directory should exist at %q", viewsPath)
|
||||||
|
|
||||||
sc.m = web.New()
|
sc.m = web.New()
|
||||||
|
sc.m.Use(AddCustomResponseHeaders(cfg))
|
||||||
sc.m.Use(AddDefaultResponseHeaders(cfg))
|
sc.m.Use(AddDefaultResponseHeaders(cfg))
|
||||||
sc.m.UseMiddleware(ContentSecurityPolicy(cfg, logger))
|
sc.m.UseMiddleware(ContentSecurityPolicy(cfg, logger))
|
||||||
sc.m.UseMiddleware(web.Renderer(viewsPath, "[[", "]]"))
|
sc.m.UseMiddleware(web.Renderer(viewsPath, "[[", "]]"))
|
||||||
|
@ -486,6 +486,8 @@ type Cfg struct {
|
|||||||
GRPCServerNetwork string
|
GRPCServerNetwork string
|
||||||
GRPCServerAddress string
|
GRPCServerAddress string
|
||||||
GRPCServerTLSConfig *tls.Config
|
GRPCServerTLSConfig *tls.Config
|
||||||
|
|
||||||
|
CustomResponseHeaders map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandLineArgs struct {
|
type CommandLineArgs struct {
|
||||||
@ -1692,6 +1694,14 @@ func (cfg *Cfg) readServerSettings(iniFile *ini.File) error {
|
|||||||
|
|
||||||
cfg.ReadTimeout = server.Key("read_timeout").MustDuration(0)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user