[main] Plugin fixes (#57399)
* Plugins: Remove support for V1 manifests
* Plugins: Make proxy endpoints not leak sensitive HTTP headers
* Security: Fix do not forward login cookie in outgoing requests
(cherry picked from commit 4539c33fce)
Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
co-authored by
Will Browne
parent
af17123b5f
commit
6f8fcae01b
@@ -142,6 +142,9 @@ func (h *ContextHandler) initContextWithJWT(ctx *models.ReqContext, orgId int64)
|
||||
return true
|
||||
}
|
||||
|
||||
newCtx := WithAuthHTTPHeader(ctx.Req.Context(), h.Cfg.JWTAuthHeaderName)
|
||||
*ctx.Req = *ctx.Req.WithContext(newCtx)
|
||||
|
||||
ctx.SignedInUser = queryResult
|
||||
ctx.IsSignedIn = true
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ func (h *ContextHandler) initContextWithAPIKey(reqContext *models.ReqContext) bo
|
||||
_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithAPIKey")
|
||||
defer span.End()
|
||||
|
||||
ctx := WithAuthHTTPHeader(reqContext.Req.Context(), "Authorization")
|
||||
*reqContext.Req = *reqContext.Req.WithContext(ctx)
|
||||
|
||||
var (
|
||||
apikey *apikey.APIKey
|
||||
errKey error
|
||||
@@ -356,7 +359,7 @@ func (h *ContextHandler) initContextWithBasicAuth(reqContext *models.ReqContext,
|
||||
return false
|
||||
}
|
||||
|
||||
ctx, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithBasicAuth")
|
||||
_, span := h.tracer.Start(reqContext.Req.Context(), "initContextWithBasicAuth")
|
||||
defer span.End()
|
||||
|
||||
username, password, err := util.DecodeBasicAuthHeader(header)
|
||||
@@ -365,12 +368,15 @@ func (h *ContextHandler) initContextWithBasicAuth(reqContext *models.ReqContext,
|
||||
return true
|
||||
}
|
||||
|
||||
ctx := WithAuthHTTPHeader(reqContext.Req.Context(), "Authorization")
|
||||
*reqContext.Req = *reqContext.Req.WithContext(ctx)
|
||||
|
||||
authQuery := models.LoginUserQuery{
|
||||
Username: username,
|
||||
Password: password,
|
||||
Cfg: h.Cfg,
|
||||
}
|
||||
if err := h.authenticator.AuthenticateUser(reqContext.Req.Context(), &authQuery); err != nil {
|
||||
if err := h.authenticator.AuthenticateUser(ctx, &authQuery); err != nil {
|
||||
reqContext.Logger.Debug(
|
||||
"Failed to authorize the user",
|
||||
"username", username,
|
||||
@@ -651,6 +657,15 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext,
|
||||
|
||||
logger.Debug("Successfully got user info", "userID", user.UserID, "username", user.Login)
|
||||
|
||||
ctx := WithAuthHTTPHeader(reqContext.Req.Context(), h.Cfg.AuthProxyHeaderName)
|
||||
for _, header := range h.Cfg.AuthProxyHeaders {
|
||||
if header != "" {
|
||||
ctx = WithAuthHTTPHeader(ctx, header)
|
||||
}
|
||||
}
|
||||
|
||||
*reqContext.Req = *reqContext.Req.WithContext(ctx)
|
||||
|
||||
// Add user info to context
|
||||
reqContext.SignedInUser = user
|
||||
reqContext.IsSignedIn = true
|
||||
@@ -670,3 +685,38 @@ func (h *ContextHandler) initContextWithAuthProxy(reqContext *models.ReqContext,
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type authHTTPHeaderListContextKey struct{}
|
||||
|
||||
var authHTTPHeaderListKey = authHTTPHeaderListContextKey{}
|
||||
|
||||
// AuthHTTPHeaderList used to record HTTP headers that being when verifying authentication
|
||||
// of an incoming HTTP request.
|
||||
type AuthHTTPHeaderList struct {
|
||||
Items []string
|
||||
}
|
||||
|
||||
// WithAuthHTTPHeader returns a copy of parent in which the named HTTP header will be included
|
||||
// and later retrievable by AuthHTTPHeaderListFromContext.
|
||||
func WithAuthHTTPHeader(parent context.Context, name string) context.Context {
|
||||
list := AuthHTTPHeaderListFromContext(parent)
|
||||
|
||||
if list == nil {
|
||||
list = &AuthHTTPHeaderList{
|
||||
Items: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
list.Items = append(list.Items, name)
|
||||
|
||||
return context.WithValue(parent, authHTTPHeaderListKey, list)
|
||||
}
|
||||
|
||||
// AuthHTTPHeaderListFromContext returns the AuthHTTPHeaderList in a context.Context, if any,
|
||||
// and will include any HTTP headers used when verifying authentication of an incoming HTTP request.
|
||||
func AuthHTTPHeaderListFromContext(c context.Context) *AuthHTTPHeaderList {
|
||||
if list, ok := c.Value(authHTTPHeaderListKey).(*AuthHTTPHeaderList); ok {
|
||||
return list
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ func (s *Service) handleQuerySingleDatasource(ctx context.Context, user *user.Si
|
||||
middlewares := []httpclient.Middleware{}
|
||||
if parsedReq.httpRequest != nil {
|
||||
middlewares = append(middlewares,
|
||||
httpclientprovider.ForwardedCookiesMiddleware(parsedReq.httpRequest.Cookies(), ds.AllowedCookies()),
|
||||
httpclientprovider.ForwardedCookiesMiddleware(parsedReq.httpRequest.Cookies(), ds.AllowedCookies(), []string{s.cfg.LoginCookieName}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ func (s *Service) handleQuerySingleDatasource(ctx context.Context, user *user.Si
|
||||
}
|
||||
|
||||
if parsedReq.httpRequest != nil {
|
||||
proxyutil.ClearCookieHeader(parsedReq.httpRequest, ds.AllowedCookies())
|
||||
proxyutil.ClearCookieHeader(parsedReq.httpRequest, ds.AllowedCookies(), []string{s.cfg.LoginCookieName})
|
||||
if cookieStr := parsedReq.httpRequest.Header.Get("Cookie"); cookieStr != "" {
|
||||
req.Headers["Cookie"] = cookieStr
|
||||
}
|
||||
|
||||
@@ -353,6 +353,29 @@ func TestQueryData(t *testing.T) {
|
||||
|
||||
require.Equal(t, map[string]string{"Cookie": "bar=rab; foo=oof"}, tc.pluginContext.req.Headers)
|
||||
})
|
||||
|
||||
t.Run("it doesn't adds cookie header to the request when keepCookies configured with login cookie name", func(t *testing.T) {
|
||||
tc := setup(t)
|
||||
tc.queryService.cfg.LoginCookieName = "grafana_session"
|
||||
json, err := simplejson.NewJson([]byte(`{"keepCookies": [ "grafana_session", "bar" ]}`))
|
||||
require.NoError(t, err)
|
||||
tc.dataSourceCache.ds.JsonData = json
|
||||
|
||||
metricReq := metricRequest()
|
||||
httpReq, err := http.NewRequest(http.MethodGet, "/", nil)
|
||||
require.NoError(t, err)
|
||||
httpReq.AddCookie(&http.Cookie{Name: "a"})
|
||||
httpReq.AddCookie(&http.Cookie{Name: "bar", Value: "rab"})
|
||||
httpReq.AddCookie(&http.Cookie{Name: "b"})
|
||||
httpReq.AddCookie(&http.Cookie{Name: "foo", Value: "oof"})
|
||||
httpReq.AddCookie(&http.Cookie{Name: "c"})
|
||||
httpReq.AddCookie(&http.Cookie{Name: tc.queryService.cfg.LoginCookieName, Value: "val"})
|
||||
metricReq.HTTPRequest = httpReq
|
||||
_, err = tc.queryService.QueryData(context.Background(), tc.signedInUser, true, metricReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, map[string]string{"Cookie": "bar=rab"}, tc.pluginContext.req.Headers)
|
||||
})
|
||||
}
|
||||
|
||||
func setup(t *testing.T) *testContext {
|
||||
@@ -372,7 +395,7 @@ func setup(t *testing.T) *testContext {
|
||||
SimulatePluginFailure: false,
|
||||
}
|
||||
exprService := expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, pc, fakeDatasourceService)
|
||||
queryService := ProvideService(nil, dc, exprService, rv, ds, pc, tc) // provider belonging to this package
|
||||
queryService := ProvideService(setting.NewCfg(), dc, exprService, rv, ds, pc, tc) // provider belonging to this package
|
||||
return &testContext{
|
||||
pluginContext: pc,
|
||||
secretStore: ss,
|
||||
|
||||
Reference in New Issue
Block a user