Proxy: Improve header handling for reverse proxy (#67279)

This commit is contained in:
Emil Tullstedt 2023-04-27 13:30:11 +02:00 committed by GitHub
parent 2306fb38dc
commit cefeef7134
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 2 deletions

View File

@ -1,6 +1,7 @@
package proxyutil
import (
"fmt"
"net"
"net/http"
"sort"
@ -75,6 +76,16 @@ func SetProxyResponseHeaders(header http.Header) {
header.Set("Content-Security-Policy", "sandbox")
}
// SetViaHeader adds Grafana's reverse proxy to the proxy chain.
// Defined in RFC 9110 7.6.3 https://datatracker.ietf.org/doc/html/rfc9110#name-via
func SetViaHeader(header http.Header, major, minor int) {
via := fmt.Sprintf("%d.%d grafana", major, minor)
if old := header.Get("Via"); old != "" {
via = fmt.Sprintf("%s, %s", via, old)
}
header.Set("Via", via)
}
// ApplyUserHeader Set the X-Grafana-User header if needed (and remove if not).
func ApplyUserHeader(sendUserHeader bool, req *http.Request, user *user.SignedInUser) {
req.Header.Del(UserHeaderName)

View File

@ -79,11 +79,30 @@ func wrapDirector(d func(*http.Request)) func(req *http.Request) {
}
}
// deletedHeaders lists a number of headers that we don't want to
// pass-through from the upstream when using a reverse proxy.
//
// These are related to the connection between Grafana and the proxy
// or instructions that would alter how a browser will interact with
// future requests to Grafana (such as enabling Strict Transport
// Security)
var deletedHeaders = []string{
"Alt-Svc",
"Close",
"Server",
"Set-Cookie",
"Strict-Transport-Security",
}
// modifyResponse enforces certain constraints on http.Response.
func modifyResponse(logger glog.Logger) func(resp *http.Response) error {
return func(resp *http.Response) error {
resp.Header.Del("Set-Cookie")
for _, header := range deletedHeaders {
resp.Header.Del(header)
}
SetProxyResponseHeaders(resp.Header)
SetViaHeader(resp.Header, resp.ProtoMajor, resp.ProtoMinor)
return nil
}
}

View File

@ -21,6 +21,8 @@ func TestReverseProxy(t *testing.T) {
upstream := newUpstreamServer(t, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
actualReq = req
http.SetCookie(w, &http.Cookie{Name: "test"})
w.Header().Set("Strict-Transport-Security", "max-age=31536000")
w.Header().Set("X-Custom-Hdr", "Ok!")
w.WriteHeader(http.StatusOK)
}))
t.Cleanup(upstream.Close)
@ -52,11 +54,14 @@ func TestReverseProxy(t *testing.T) {
require.Empty(t, actualReq.Header.Get("Referer"))
require.Equal(t, "https://test.com/api", actualReq.Header.Get("X-Grafana-Referer"))
require.Equal(t, "value", actualReq.Header.Get("X-KEY"))
require.Empty(t, actualReq.Header.Get("Authorization"))
resp := rec.Result()
require.Empty(t, resp.Cookies())
require.Equal(t, "sandbox", resp.Header.Get("Content-Security-Policy"))
require.Contains(t, resp.Header, "X-Custom-Hdr")
require.NotContains(t, resp.Header, "Strict-Transport-Security")
require.Contains(t, resp.Header.Get("Via"), "grafana")
require.NoError(t, resp.Body.Close())
require.Empty(t, actualReq.Header.Get("Authorization"))
})
t.Run("When proxying a request using WithModifyResponse should call it before default ModifyResponse func", func(t *testing.T) {