Security: Store datasource passwords encrypted in secureJsonData (#16175)
* Store passwords in secureJsonData * Revert unnecessary refactors * Fix for nil jsonSecureData value * Remove copied encryption code from migration * Fix wrong field reference * Remove migration and provisioning changes * Use password getters in datasource proxy * Refactor password handling in datasource configs * Add provisioning warnings * Update documentation * Remove migration command, moved to separate PR * Remove unused code * Set the upgrade version * Remove unused code * Remove double reference
This commit is contained in:
@@ -3,6 +3,7 @@ package pluginproxy
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/grafana/grafana/pkg/components/securejsondata"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -274,12 +275,6 @@ func TestDSRouteRule(t *testing.T) {
|
||||
Convey("Should add db to url", func() {
|
||||
So(req.URL.Path, ShouldEqual, "/db/site/")
|
||||
})
|
||||
|
||||
Convey("Should add username and password", func() {
|
||||
queryVals := req.URL.Query()
|
||||
So(queryVals["u"][0], ShouldEqual, "user")
|
||||
So(queryVals["p"][0], ShouldEqual, "password")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When proxying a data source with no keepCookies specified", func() {
|
||||
@@ -481,6 +476,26 @@ func TestDSRouteRule(t *testing.T) {
|
||||
So(req.Header.Get("X-Grafana-User"), ShouldEqual, "")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When proxying data source proxy should handle authentication", func() {
|
||||
tests := []*Test{
|
||||
createAuthTest(m.DS_INFLUXDB_08, AUTHTYPE_PASSWORD, AUTHCHECK_QUERY, false),
|
||||
createAuthTest(m.DS_INFLUXDB_08, AUTHTYPE_PASSWORD, AUTHCHECK_QUERY, true),
|
||||
createAuthTest(m.DS_INFLUXDB, AUTHTYPE_PASSWORD, AUTHCHECK_HEADER, true),
|
||||
createAuthTest(m.DS_INFLUXDB, AUTHTYPE_PASSWORD, AUTHCHECK_HEADER, false),
|
||||
createAuthTest(m.DS_INFLUXDB, AUTHTYPE_BASIC, AUTHCHECK_HEADER, true),
|
||||
createAuthTest(m.DS_INFLUXDB, AUTHTYPE_BASIC, AUTHCHECK_HEADER, false),
|
||||
|
||||
// These two should be enough for any other datasource at the moment. Proxy has special handling
|
||||
// only for Influx, others have the same path and only BasicAuth. Non BasicAuth datasources
|
||||
// do not go through proxy but through TSDB API which is not tested here.
|
||||
createAuthTest(m.DS_ES, AUTHTYPE_BASIC, AUTHCHECK_HEADER, false),
|
||||
createAuthTest(m.DS_ES, AUTHTYPE_BASIC, AUTHCHECK_HEADER, true),
|
||||
}
|
||||
for _, test := range tests {
|
||||
runDatasourceAuthTest(test)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -524,3 +539,90 @@ func newFakeHTTPClient(fakeBody []byte) httpClient {
|
||||
fakeBody: fakeBody,
|
||||
}
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
datasource *m.DataSource
|
||||
checkReq func(req *http.Request)
|
||||
}
|
||||
|
||||
const (
|
||||
AUTHTYPE_PASSWORD = "password"
|
||||
AUTHTYPE_BASIC = "basic"
|
||||
)
|
||||
|
||||
const (
|
||||
AUTHCHECK_QUERY = "query"
|
||||
AUTHCHECK_HEADER = "header"
|
||||
)
|
||||
|
||||
func createAuthTest(dsType string, authType string, authCheck string, useSecureJsonData bool) *Test {
|
||||
// Basic user:password
|
||||
base64AthHeader := "Basic dXNlcjpwYXNzd29yZA=="
|
||||
|
||||
test := &Test{
|
||||
datasource: &m.DataSource{
|
||||
Type: dsType,
|
||||
JsonData: simplejson.New(),
|
||||
},
|
||||
}
|
||||
var message string
|
||||
if authType == AUTHTYPE_PASSWORD {
|
||||
message = fmt.Sprintf("%v should add username and password", dsType)
|
||||
test.datasource.User = "user"
|
||||
if useSecureJsonData {
|
||||
test.datasource.SecureJsonData = securejsondata.GetEncryptedJsonData(map[string]string{
|
||||
"password": "password",
|
||||
})
|
||||
} else {
|
||||
test.datasource.Password = "password"
|
||||
}
|
||||
} else {
|
||||
message = fmt.Sprintf("%v should add basic auth username and password", dsType)
|
||||
test.datasource.BasicAuth = true
|
||||
test.datasource.BasicAuthUser = "user"
|
||||
if useSecureJsonData {
|
||||
test.datasource.SecureJsonData = securejsondata.GetEncryptedJsonData(map[string]string{
|
||||
"basicAuthPassword": "password",
|
||||
})
|
||||
} else {
|
||||
test.datasource.BasicAuthPassword = "password"
|
||||
}
|
||||
}
|
||||
|
||||
if useSecureJsonData {
|
||||
message += " from securejsondata"
|
||||
}
|
||||
|
||||
if authCheck == AUTHCHECK_QUERY {
|
||||
message += " to query params"
|
||||
test.checkReq = func(req *http.Request) {
|
||||
Convey(message, func() {
|
||||
queryVals := req.URL.Query()
|
||||
So(queryVals["u"][0], ShouldEqual, "user")
|
||||
So(queryVals["p"][0], ShouldEqual, "password")
|
||||
})
|
||||
}
|
||||
} else {
|
||||
message += " to auth header"
|
||||
test.checkReq = func(req *http.Request) {
|
||||
Convey(message, func() {
|
||||
So(req.Header.Get("Authorization"), ShouldEqual, base64AthHeader)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return test
|
||||
}
|
||||
|
||||
func runDatasourceAuthTest(test *Test) {
|
||||
plugin := &plugins.DataSourcePlugin{}
|
||||
ctx := &m.ReqContext{}
|
||||
proxy := NewDataSourceProxy(test.datasource, plugin, ctx, "", &setting.Cfg{})
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
proxy.getDirector()(req)
|
||||
|
||||
test.checkReq(req)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user