From 5652bde4476b6291550edde2e6e05191aac0811f Mon Sep 17 00:00:00 2001 From: Tania B Date: Thu, 4 Nov 2021 18:47:21 +0200 Subject: [PATCH] Encryption: Use secrets service (#40251) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use secrets service in pluginproxy * Use secrets service in pluginxontext * Use secrets service in pluginsettings * Use secrets service in provisioning * Use secrets service in authinfoservice * Use secrets service in api * Use secrets service in sqlstore * Use secrets service in dashboardshapshots * Use secrets service in tsdb * Use secrets service in datasources * Use secrets service in alerting * Use secrets service in ngalert * Break cyclic dependancy * Refactor service * Break cyclic dependancy * Add FakeSecretsStore * Setup Secrets Service in sqlstore * Fix * Continue secrets service refactoring * Fix cyclic dependancy in sqlstore tests * Fix secrets service references * Fix linter errors * Add fake secrets service for tests * Refactor SetupTestSecretsService * Update setting up secret service in tests * Fix missing secrets service in multiorg_alertmanager_test * Use fake db in tests and sort imports * Use fake db in datasources tests * Fix more tests * Fix linter issues * Attempt to fix plugin proxy tests * Pass secrets service to getPluginProxiedRequest in pluginproxy tests * Fix pluginproxy tests * Revert using secrets service in alerting and provisioning * Update decryptFn in alerting migration * Rename defaultProvider to currentProvider * Use fake secrets service in alert channels tests * Refactor secrets service test helper * Update setting up secrets service in tests * Revert alerting changes in api * Add comments * Remove secrets service from background services * Convert global encryption functions into vars * Revert "Convert global encryption functions into vars" This reverts commit 498eb19859eba364a2400a6d7e73236b1c9a5b37. * Add feature toggle for envelope encryption * Rename toggle Co-authored-by: Emil Tullstedt Co-authored-by: Joan López de la Franca Beltran --- pkg/api/app_routes.go | 2 +- pkg/api/datasources.go | 5 +- pkg/api/http_server.go | 5 +- pkg/api/login.go | 5 +- pkg/api/login_test.go | 17 +-- pkg/api/pluginproxy/ds_proxy.go | 2 +- pkg/api/pluginproxy/ds_proxy_test.go | 110 +++++++++++------- pkg/api/pluginproxy/pluginproxy.go | 6 +- pkg/api/pluginproxy/pluginproxy_test.go | 26 +++-- pkg/plugins/plugincontext/plugincontext.go | 11 +- .../backgroundsvcs/background_services.go | 3 +- .../dashboardsnapshots/dashboardsnapshots.go | 21 ++-- .../dashboardsnapshots_test.go | 11 +- pkg/services/datasources/service.go | 22 ++-- pkg/services/datasources/service_test.go | 107 +++++++++-------- pkg/services/encryption/encryption.go | 2 + .../encryption/ossencryption/ossencryption.go | 2 + .../login/authinfoservice/database.go | 8 +- pkg/services/login/authinfoservice/service.go | 9 +- .../login/authinfoservice/user_auth_test.go | 13 +-- pkg/services/ngalert/api/api.go | 6 +- pkg/services/ngalert/api/api_alertmanager.go | 17 ++- .../api/tooling/definitions/alertmanager.go | 6 +- pkg/services/ngalert/ngalert.go | 56 ++++----- .../ngalert/notifier/alertmanager_test.go | 7 +- .../ngalert/notifier/channels/alertmanager.go | 5 +- .../notifier/channels/alertmanager_test.go | 14 ++- .../ngalert/notifier/channels/line.go | 3 +- .../ngalert/notifier/channels/line_test.go | 7 +- .../ngalert/notifier/channels/opsgenie.go | 3 +- .../notifier/channels/opsgenie_test.go | 13 ++- .../ngalert/notifier/channels/pagerduty.go | 3 +- .../notifier/channels/pagerduty_test.go | 13 ++- .../ngalert/notifier/channels/pushover.go | 5 +- .../notifier/channels/pushover_test.go | 8 +- .../ngalert/notifier/channels/sensugo.go | 3 +- .../ngalert/notifier/channels/sensugo_test.go | 13 ++- .../ngalert/notifier/channels/slack.go | 4 +- .../ngalert/notifier/channels/slack_test.go | 11 +- .../ngalert/notifier/channels/telegram.go | 3 +- .../notifier/channels/telegram_test.go | 9 +- .../ngalert/notifier/channels/threema.go | 3 +- .../ngalert/notifier/channels/threema_test.go | 13 ++- .../ngalert/notifier/channels/webhook.go | 3 +- .../ngalert/notifier/channels/webhook_test.go | 13 ++- .../notifier/multiorg_alertmanager_test.go | 13 ++- .../ngalert/schedule/schedule_unit_test.go | 6 +- pkg/services/ngalert/tests/util.go | 10 +- pkg/services/pluginsettings/service.go | 23 ++-- pkg/services/pluginsettings/service_test.go | 30 ++--- pkg/services/secrets/manager/helpers.go | 19 ++- pkg/services/secrets/manager/manager.go | 29 +++-- pkg/services/secrets/manager/manager_test.go | 5 +- pkg/services/secrets/secrets.go | 4 + .../sqlstore/dashboard_snapshot_test.go | 13 +-- .../sqlstore/migrations/ualert/ualert.go | 6 +- pkg/tsdb/service_test.go | 11 +- pkg/util/encryption.go | 8 +- 58 files changed, 441 insertions(+), 364 deletions(-) diff --git a/pkg/api/app_routes.go b/pkg/api/app_routes.go index 5c51bd4d38e..0d2753c5854 100644 --- a/pkg/api/app_routes.go +++ b/pkg/api/app_routes.go @@ -60,7 +60,7 @@ func AppPluginRoute(route *plugins.Route, appID string, hs *HTTPServer) web.Hand return func(c *models.ReqContext) { path := web.Params(c.Req)["*"] - proxy := pluginproxy.NewApiPluginProxy(c, path, route, appID, hs.Cfg, hs.EncryptionService) + proxy := pluginproxy.NewApiPluginProxy(c, path, route, appID, hs.Cfg, hs.SecretsService) proxy.Transport = pluginProxyTransport proxy.ServeHTTP(c.Resp, c.Req) } diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index 2ba2d363857..a12ab566cbb 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -15,7 +15,6 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins/adapters" - "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/web" ) @@ -292,7 +291,7 @@ func (hs *HTTPServer) fillWithSecureJSONData(ctx context.Context, cmd *models.Up return models.ErrDatasourceIsReadOnly } - secureJSONData, err := hs.EncryptionService.DecryptJsonData(ctx, ds.SecureJsonData, setting.SecretKey) + secureJSONData, err := hs.SecretsService.DecryptJsonData(ctx, ds.SecureJsonData) if err != nil { return err } @@ -492,7 +491,7 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Respo func (hs *HTTPServer) decryptSecureJsonDataFn() func(map[string][]byte) map[string]string { return func(m map[string][]byte) map[string]string { - decryptedJsonData, err := hs.EncryptionService.DecryptJsonData(context.Background(), m, setting.SecretKey) + decryptedJsonData, err := hs.SecretsService.DecryptJsonData(context.Background(), m) if err != nil { hs.log.Error("Failed to decrypt secure json data", "error", err) } diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index c60c37d4995..4c62dd720fb 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -49,6 +49,7 @@ import ( "github.com/grafana/grafana/pkg/services/schemaloader" "github.com/grafana/grafana/pkg/services/search" "github.com/grafana/grafana/pkg/services/searchusers" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/shorturls" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/updatechecker" @@ -107,6 +108,7 @@ type HTTPServer struct { OAuthTokenService oauthtoken.OAuthTokenService Listener net.Listener EncryptionService encryption.Service + SecretsService secrets.Service DataSourcesService *datasources.Service cleanUpService *cleanup.CleanUpService tracingService *tracing.TracingService @@ -139,7 +141,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi internalMetricsSvc *metrics.InternalMetricsService, quotaService *quota.QuotaService, socialService social.Service, oauthTokenService oauthtoken.OAuthTokenService, encryptionService encryption.Service, updateChecker *updatechecker.Service, searchUsersService searchusers.Service, - dataSourcesService *datasources.Service) (*HTTPServer, error) { + dataSourcesService *datasources.Service, secretsService secrets.Service) (*HTTPServer, error) { web.Env = cfg.Env m := web.New() @@ -190,6 +192,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi SocialService: socialService, OAuthTokenService: oauthTokenService, EncryptionService: encryptionService, + SecretsService: secretsService, DataSourcesService: dataSourcesService, searchUsersService: searchUsersService, } diff --git a/pkg/api/login.go b/pkg/api/login.go index c135d1ce4f2..2089838e1c6 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/middleware/cookies" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/web" @@ -315,12 +316,12 @@ func (hs *HTTPServer) tryGetEncryptedCookie(ctx *models.ReqContext, cookieName s return "", false } - decryptedError, err := hs.EncryptionService.Decrypt(ctx.Req.Context(), decoded, setting.SecretKey) + decryptedError, err := hs.SecretsService.Decrypt(ctx.Req.Context(), decoded) return string(decryptedError), err == nil } func (hs *HTTPServer) trySetEncryptedCookie(ctx *models.ReqContext, cookieName string, value string, maxAge int) error { - encryptedError, err := hs.EncryptionService.Encrypt(ctx.Req.Context(), []byte(value), setting.SecretKey) + encryptedError, err := hs.SecretsService.Encrypt(ctx.Req.Context(), []byte(value), secrets.WithoutScope()) if err != nil { return err } diff --git a/pkg/api/login_test.go b/pkg/api/login_test.go index 7bbbf8c2ed8..ade901eaecc 100644 --- a/pkg/api/login_test.go +++ b/pkg/api/login_test.go @@ -23,9 +23,11 @@ import ( "github.com/grafana/grafana/pkg/login/social" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/auth" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" "github.com/grafana/grafana/pkg/services/hooks" "github.com/grafana/grafana/pkg/services/licensing" + "github.com/grafana/grafana/pkg/services/secrets" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -107,12 +109,13 @@ func TestLoginErrorCookieAPIEndpoint(t *testing.T) { sc := setupScenarioContext(t, "/login") cfg := setting.NewCfg() + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) hs := &HTTPServer{ - Cfg: cfg, - SettingsProvider: &setting.OSSImpl{Cfg: cfg}, - License: &licensing.OSSLicensingService{}, - SocialService: &mockSocialService{}, - EncryptionService: ossencryption.ProvideService(), + Cfg: cfg, + SettingsProvider: &setting.OSSImpl{Cfg: cfg}, + License: &licensing.OSSLicensingService{}, + SocialService: &mockSocialService{}, + SecretsService: secretsService, } sc.defaultHandler = routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) { @@ -125,7 +128,7 @@ func TestLoginErrorCookieAPIEndpoint(t *testing.T) { setting.OAuthAutoLogin = true oauthError := errors.New("User not a member of one of the required organizations") - encryptedError, err := hs.EncryptionService.Encrypt(context.Background(), []byte(oauthError.Error()), setting.SecretKey) + encryptedError, err := hs.SecretsService.Encrypt(context.Background(), []byte(oauthError.Error()), secrets.WithoutScope()) require.NoError(t, err) expCookiePath := "/" if len(setting.AppSubUrl) > 0 { diff --git a/pkg/api/pluginproxy/ds_proxy.go b/pkg/api/pluginproxy/ds_proxy.go index 8a994a16645..5b1806cf7b7 100644 --- a/pkg/api/pluginproxy/ds_proxy.go +++ b/pkg/api/pluginproxy/ds_proxy.go @@ -251,7 +251,7 @@ func (proxy *DataSourceProxy) director(req *http.Request) { } } - secureJsonData, err := proxy.dataSourcesService.EncryptionService.DecryptJsonData(req.Context(), proxy.ds.SecureJsonData, setting.SecretKey) + secureJsonData, err := proxy.dataSourcesService.SecretsService.DecryptJsonData(req.Context(), proxy.ds.SecureJsonData) if err != nil { logger.Error("Error interpolating proxy url", "error", err) return diff --git a/pkg/api/pluginproxy/ds_proxy_test.go b/pkg/api/pluginproxy/ds_proxy_test.go index 72359ffeaeb..846c551d8eb 100644 --- a/pkg/api/pluginproxy/ds_proxy_test.go +++ b/pkg/api/pluginproxy/ds_proxy_test.go @@ -19,8 +19,10 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/datasources" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" "github.com/grafana/grafana/pkg/services/oauthtoken" + "github.com/grafana/grafana/pkg/services/secrets" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/web" "github.com/stretchr/testify/assert" @@ -83,7 +85,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) { }) setting.SecretKey = "password" //nolint:goconst - key, err := ossencryption.ProvideService().Encrypt(context.Background(), []byte("123"), "password") + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + key, err := secretsService.Encrypt(context.Background(), []byte("123"), secrets.WithoutScope()) require.NoError(t, err) ds := &models.DataSource{ @@ -122,7 +125,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { t.Run("When matching route path", func(t *testing.T) { ctx, req := setUp() - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -135,7 +138,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { t.Run("When matching route path and has dynamic url", func(t *testing.T) { ctx, req := setUp() - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/common/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) proxy.matchedRoute = routes[3] @@ -147,7 +150,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { t.Run("When matching route path with no url", func(t *testing.T) { ctx, req := setUp() - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) proxy.matchedRoute = routes[4] @@ -158,7 +161,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { t.Run("When matching route path and has dynamic body", func(t *testing.T) { ctx, req := setUp() - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/body", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) proxy.matchedRoute = routes[5] @@ -172,7 +175,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { t.Run("Validating request", func(t *testing.T) { t.Run("plugin route with valid role", func(t *testing.T) { ctx, _ := setUp() - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) err = proxy.validateRequest() @@ -181,7 +184,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { t.Run("plugin route with admin role and user is editor", func(t *testing.T) { ctx, _ := setUp() - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) err = proxy.validateRequest() @@ -191,7 +194,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { t.Run("plugin route with admin role and user is admin", func(t *testing.T) { ctx, _ := setUp() ctx.SignedInUser.OrgRole = models.ROLE_ADMIN - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) err = proxy.validateRequest() @@ -236,7 +239,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) { }) setting.SecretKey = "password" - key, err := ossencryption.ProvideService().Encrypt(context.Background(), []byte("123"), "password") + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + key, err := secretsService.Encrypt(context.Background(), []byte("123"), secrets.WithoutScope()) require.NoError(t, err) ds := &models.DataSource{ @@ -281,7 +285,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { }, } - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg) @@ -297,7 +301,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { req, err := http.NewRequest("GET", "http://localhost/asd", nil) require.NoError(t, err) client = newFakeHTTPClient(t, json2) - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken2", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[1], dsInfo, cfg) @@ -314,7 +318,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { require.NoError(t, err) client = newFakeHTTPClient(t, []byte{}) - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg) @@ -335,7 +339,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) { ds := &models.DataSource{Url: "htttp://graphite:8080", Type: models.DS_GRAPHITE} ctx := &models.ReqContext{} - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{BuildVersion: "5.3.0"}, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) @@ -360,7 +365,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) { ctx := &models.ReqContext{} var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -383,7 +389,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) { ctx := &models.ReqContext{} var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -410,7 +417,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) { ctx := &models.ReqContext{} var pluginRoutes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, pluginRoutes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -432,7 +440,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) { } ctx := &models.ReqContext{} var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) @@ -488,7 +497,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) { oAuthEnabled: true, } var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &mockAuthToken, dsService) require.NoError(t, err) req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) @@ -539,22 +549,24 @@ func TestDataSourceProxy_routeRule(t *testing.T) { }) t.Run("When proxying data source proxy should handle authentication", func(t *testing.T) { + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + tests := []*testCase{ - createAuthTest(t, models.DS_INFLUXDB_08, authTypePassword, authCheckQuery, false), - createAuthTest(t, models.DS_INFLUXDB_08, authTypePassword, authCheckQuery, true), - createAuthTest(t, models.DS_INFLUXDB, authTypePassword, authCheckHeader, true), - createAuthTest(t, models.DS_INFLUXDB, authTypePassword, authCheckHeader, false), - createAuthTest(t, models.DS_INFLUXDB, authTypeBasic, authCheckHeader, true), - createAuthTest(t, models.DS_INFLUXDB, authTypeBasic, authCheckHeader, false), + createAuthTest(t, secretsService, models.DS_INFLUXDB_08, authTypePassword, authCheckQuery, false), + createAuthTest(t, secretsService, models.DS_INFLUXDB_08, authTypePassword, authCheckQuery, true), + createAuthTest(t, secretsService, models.DS_INFLUXDB, authTypePassword, authCheckHeader, true), + createAuthTest(t, secretsService, models.DS_INFLUXDB, authTypePassword, authCheckHeader, false), + createAuthTest(t, secretsService, models.DS_INFLUXDB, authTypeBasic, authCheckHeader, true), + createAuthTest(t, secretsService, models.DS_INFLUXDB, authTypeBasic, authCheckHeader, 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(t, models.DS_ES, authTypeBasic, authCheckHeader, false), - createAuthTest(t, models.DS_ES, authTypeBasic, authCheckHeader, true), + createAuthTest(t, secretsService, models.DS_ES, authTypeBasic, authCheckHeader, false), + createAuthTest(t, secretsService, models.DS_ES, authTypeBasic, authCheckHeader, true), } for _, test := range tests { - runDatasourceAuthTest(t, test) + runDatasourceAuthTest(t, secretsService, test) } }) } @@ -613,9 +625,9 @@ func TestDataSourceProxy_requestHandling(t *testing.T) { t.Run("When response header Set-Cookie is not set should remove proxied Set-Cookie header", func(t *testing.T) { ctx, ds := setUp(t) - var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -632,7 +644,8 @@ func TestDataSourceProxy_requestHandling(t *testing.T) { }, }) var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -653,7 +666,8 @@ func TestDataSourceProxy_requestHandling(t *testing.T) { }, }) var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -677,7 +691,8 @@ func TestDataSourceProxy_requestHandling(t *testing.T) { ctx.Req = httptest.NewRequest("GET", "/api/datasources/proxy/1/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F", nil) var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -700,7 +715,8 @@ func TestNewDataSourceProxy_InvalidURL(t *testing.T) { } cfg := setting.Cfg{} var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) _, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService) require.Error(t, err) assert.True(t, strings.HasPrefix(err.Error(), `validation of data source URL "://host/root" failed`)) @@ -718,7 +734,8 @@ func TestNewDataSourceProxy_ProtocolLessURL(t *testing.T) { cfg := setting.Cfg{} var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) _, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -757,7 +774,8 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) { } var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) p, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService) if tc.err == nil { require.NoError(t, err) @@ -781,7 +799,8 @@ func getDatasourceProxiedRequest(t *testing.T, ctx *models.ReqContext, cfg *sett } var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService) require.NoError(t, err) req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil) @@ -834,7 +853,7 @@ const ( authCheckHeader = "header" ) -func createAuthTest(t *testing.T, dsType string, authType string, authCheck string, useSecureJsonData bool) *testCase { +func createAuthTest(t *testing.T, secretsService secrets.Service, dsType string, authType string, authCheck string, useSecureJsonData bool) *testCase { ctx := context.Background() // Basic user:password @@ -853,11 +872,11 @@ func createAuthTest(t *testing.T, dsType string, authType string, authCheck stri message = fmt.Sprintf("%v should add username and password", dsType) test.datasource.User = "user" if useSecureJsonData { - test.datasource.SecureJsonData, err = ossencryption.ProvideService().EncryptJsonData( + test.datasource.SecureJsonData, err = secretsService.EncryptJsonData( ctx, map[string]string{ "password": "password", - }, setting.SecretKey) + }, secrets.WithoutScope()) } else { test.datasource.Password = "password" } @@ -866,11 +885,11 @@ func createAuthTest(t *testing.T, dsType string, authType string, authCheck stri test.datasource.BasicAuth = true test.datasource.BasicAuthUser = "user" if useSecureJsonData { - test.datasource.SecureJsonData, err = ossencryption.ProvideService().EncryptJsonData( + test.datasource.SecureJsonData, err = secretsService.EncryptJsonData( ctx, map[string]string{ "basicAuthPassword": "password", - }, setting.SecretKey) + }, secrets.WithoutScope()) } else { test.datasource.BasicAuthPassword = "password" } @@ -898,10 +917,10 @@ func createAuthTest(t *testing.T, dsType string, authType string, authCheck stri return test } -func runDatasourceAuthTest(t *testing.T, test *testCase) { +func runDatasourceAuthTest(t *testing.T, secretsService secrets.Service, test *testCase) { ctx := &models.ReqContext{} var routes []*plugins.Route - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(test.datasource, routes, ctx, "", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService) require.NoError(t, err) @@ -940,7 +959,8 @@ func Test_PathCheck(t *testing.T) { return ctx, req } ctx, _ := setUp() - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) proxy, err := NewDataSourceProxy(&models.DataSource{}, routes, ctx, "b", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService) require.NoError(t, err) diff --git a/pkg/api/pluginproxy/pluginproxy.go b/pkg/api/pluginproxy/pluginproxy.go index 7ec81301d7c..a972d3d500a 100644 --- a/pkg/api/pluginproxy/pluginproxy.go +++ b/pkg/api/pluginproxy/pluginproxy.go @@ -9,7 +9,7 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/services/encryption" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util/proxyutil" @@ -22,7 +22,7 @@ type templateData struct { // NewApiPluginProxy create a plugin proxy func NewApiPluginProxy(ctx *models.ReqContext, proxyPath string, route *plugins.Route, - appID string, cfg *setting.Cfg, encryptionService encryption.Service) *httputil.ReverseProxy { + appID string, cfg *setting.Cfg, secretsService secrets.Service) *httputil.ReverseProxy { director := func(req *http.Request) { query := models.GetPluginSettingByIdQuery{OrgId: ctx.OrgId, PluginId: appID} if err := bus.DispatchCtx(ctx.Req.Context(), &query); err != nil { @@ -30,7 +30,7 @@ func NewApiPluginProxy(ctx *models.ReqContext, proxyPath string, route *plugins. return } - secureJsonData, err := encryptionService.DecryptJsonData(ctx.Req.Context(), query.Result.SecureJsonData, setting.SecretKey) + secureJsonData, err := secretsService.DecryptJsonData(ctx.Req.Context(), query.Result.SecureJsonData) if err != nil { ctx.JsonApiErr(500, "Failed to decrypt plugin settings", err) return diff --git a/pkg/api/pluginproxy/pluginproxy_test.go b/pkg/api/pluginproxy/pluginproxy_test.go index 4ea7b20d029..e6585f038a6 100644 --- a/pkg/api/pluginproxy/pluginproxy_test.go +++ b/pkg/api/pluginproxy/pluginproxy_test.go @@ -9,7 +9,9 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/services/secrets" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,6 +19,9 @@ import ( ) func TestPluginProxy(t *testing.T) { + setting.SecretKey = "password" + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + t.Run("When getting proxy headers", func(t *testing.T) { route := &plugins.Route{ Headers: []plugins.Header{ @@ -24,10 +29,8 @@ func TestPluginProxy(t *testing.T) { }, } - setting.SecretKey = "password" - bus.AddHandlerCtx("test", func(ctx context.Context, query *models.GetPluginSettingByIdQuery) error { - key, err := ossencryption.ProvideService().Encrypt(ctx, []byte("123"), "password") + key, err := secretsService.Encrypt(ctx, []byte("123"), secrets.WithoutScope()) if err != nil { return err } @@ -45,6 +48,7 @@ func TestPluginProxy(t *testing.T) { req := getPluginProxiedRequest( t, + secretsService, &models.ReqContext{ SignedInUser: &models.SignedInUser{ Login: "test_user", @@ -66,6 +70,7 @@ func TestPluginProxy(t *testing.T) { req := getPluginProxiedRequest( t, + secretsService, &models.ReqContext{ SignedInUser: &models.SignedInUser{ Login: "test_user", @@ -88,6 +93,7 @@ func TestPluginProxy(t *testing.T) { req := getPluginProxiedRequest( t, + secretsService, &models.ReqContext{ SignedInUser: &models.SignedInUser{ Login: "test_user", @@ -109,6 +115,7 @@ func TestPluginProxy(t *testing.T) { req := getPluginProxiedRequest( t, + secretsService, &models.ReqContext{ SignedInUser: &models.SignedInUser{IsAnonymous: true}, Context: &macaron.Context{ @@ -143,6 +150,7 @@ func TestPluginProxy(t *testing.T) { req := getPluginProxiedRequest( t, + secretsService, &models.ReqContext{ SignedInUser: &models.SignedInUser{ Login: "test_user", @@ -174,6 +182,7 @@ func TestPluginProxy(t *testing.T) { req := getPluginProxiedRequest( t, + secretsService, &models.ReqContext{ SignedInUser: &models.SignedInUser{ Login: "test_user", @@ -196,10 +205,10 @@ func TestPluginProxy(t *testing.T) { } bus.AddHandlerCtx("test", func(ctx context.Context, query *models.GetPluginSettingByIdQuery) error { - encryptedJsonData, err := ossencryption.ProvideService().EncryptJsonData( + encryptedJsonData, err := secretsService.EncryptJsonData( ctx, map[string]string{"key": "123"}, - setting.SecretKey, + secrets.WithoutScope(), ) if err != nil { @@ -220,6 +229,7 @@ func TestPluginProxy(t *testing.T) { req := getPluginProxiedRequest( t, + secretsService, &models.ReqContext{ SignedInUser: &models.SignedInUser{ Login: "test_user", @@ -238,7 +248,7 @@ func TestPluginProxy(t *testing.T) { } // getPluginProxiedRequest is a helper for easier setup of tests based on global config and ReqContext. -func getPluginProxiedRequest(t *testing.T, ctx *models.ReqContext, cfg *setting.Cfg, route *plugins.Route) *http.Request { +func getPluginProxiedRequest(t *testing.T, secretsService secrets.Service, ctx *models.ReqContext, cfg *setting.Cfg, route *plugins.Route) *http.Request { // insert dummy route if none is specified if route == nil { route = &plugins.Route{ @@ -247,7 +257,7 @@ func getPluginProxiedRequest(t *testing.T, ctx *models.ReqContext, cfg *setting. ReqRole: models.ROLE_EDITOR, } } - proxy := NewApiPluginProxy(ctx, "", route, "", cfg, ossencryption.ProvideService()) + proxy := NewApiPluginProxy(ctx, "", route, "", cfg, secretsService) req, err := http.NewRequest(http.MethodGet, "/api/plugin-proxy/grafana-simple-app/api/v4/alerts", nil) require.NoError(t, err) diff --git a/pkg/plugins/plugincontext/plugincontext.go b/pkg/plugins/plugincontext/plugincontext.go index e8496f8e134..bcea5ea034c 100644 --- a/pkg/plugins/plugincontext/plugincontext.go +++ b/pkg/plugins/plugincontext/plugincontext.go @@ -15,21 +15,20 @@ import ( "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/adapters" "github.com/grafana/grafana/pkg/services/datasources" - "github.com/grafana/grafana/pkg/services/encryption" "github.com/grafana/grafana/pkg/services/pluginsettings" - "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/util/errutil" ) func ProvideService(bus bus.Bus, cacheService *localcache.CacheService, pluginStore plugins.Store, - dataSourceCache datasources.CacheService, encryptionService encryption.Service, + dataSourceCache datasources.CacheService, secretsService secrets.Service, pluginSettingsService *pluginsettings.Service) *Provider { return &Provider{ Bus: bus, CacheService: cacheService, pluginStore: pluginStore, DataSourceCache: dataSourceCache, - EncryptionService: encryptionService, + SecretsService: secretsService, PluginSettingsService: pluginSettingsService, logger: log.New("plugincontext"), } @@ -40,7 +39,7 @@ type Provider struct { CacheService *localcache.CacheService pluginStore plugins.Store DataSourceCache datasources.CacheService - EncryptionService encryption.Service + SecretsService secrets.Service PluginSettingsService *pluginsettings.Service logger log.Logger } @@ -125,7 +124,7 @@ func (p *Provider) getCachedPluginSettings(ctx context.Context, pluginID string, func (p *Provider) decryptSecureJsonDataFn() func(map[string][]byte) map[string]string { return func(m map[string][]byte) map[string]string { - decryptedJsonData, err := p.EncryptionService.DecryptJsonData(context.Background(), m, setting.SecretKey) + decryptedJsonData, err := p.SecretsService.DecryptJsonData(context.Background(), m) if err != nil { p.logger.Error("Failed to decrypt secure json data", "error", err) } diff --git a/pkg/server/backgroundsvcs/background_services.go b/pkg/server/backgroundsvcs/background_services.go index 8e7708f4f9d..098a9ff777b 100644 --- a/pkg/server/backgroundsvcs/background_services.go +++ b/pkg/server/backgroundsvcs/background_services.go @@ -20,7 +20,6 @@ import ( "github.com/grafana/grafana/pkg/services/pluginsettings" "github.com/grafana/grafana/pkg/services/provisioning" "github.com/grafana/grafana/pkg/services/rendering" - "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/updatechecker" "github.com/grafana/grafana/pkg/tsdb/azuremonitor" "github.com/grafana/grafana/pkg/tsdb/cloudmonitoring" @@ -49,7 +48,7 @@ func ProvideBackgroundServiceRegistry( // Need to make sure these are initialized, is there a better place to put them? _ *azuremonitor.Service, _ *cloudwatch.CloudWatchService, _ *elasticsearch.Service, _ *graphite.Service, _ *influxdb.Service, _ *loki.Service, _ *opentsdb.Service, _ *prometheus.Service, _ *tempo.Service, - _ *testdatasource.Service, _ *plugindashboards.Service, _ *dashboardsnapshots.Service, _ secrets.Service, + _ *testdatasource.Service, _ *plugindashboards.Service, _ *dashboardsnapshots.Service, _ *postgres.Service, _ *mysql.Service, _ *mssql.Service, _ *grafanads.Service, _ *cloudmonitoring.Service, _ *pluginsettings.Service, _ *alerting.AlertNotificationService, ) *BackgroundServiceRegistry { diff --git a/pkg/services/dashboardsnapshots/dashboardsnapshots.go b/pkg/services/dashboardsnapshots/dashboardsnapshots.go index 617f112cc98..5d5cf15f742 100644 --- a/pkg/services/dashboardsnapshots/dashboardsnapshots.go +++ b/pkg/services/dashboardsnapshots/dashboardsnapshots.go @@ -6,22 +6,21 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/encryption" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/sqlstore" - "github.com/grafana/grafana/pkg/setting" ) type Service struct { - Bus bus.Bus - SQLStore *sqlstore.SQLStore - EncryptionService encryption.Service + Bus bus.Bus + SQLStore *sqlstore.SQLStore + SecretsService secrets.Service } -func ProvideService(bus bus.Bus, store *sqlstore.SQLStore, encryptionService encryption.Service) *Service { +func ProvideService(bus bus.Bus, store *sqlstore.SQLStore, secretsService secrets.Service) *Service { s := &Service{ - Bus: bus, - SQLStore: store, - EncryptionService: encryptionService, + Bus: bus, + SQLStore: store, + SecretsService: secretsService, } s.Bus.AddHandlerCtx(s.CreateDashboardSnapshot) @@ -39,7 +38,7 @@ func (s *Service) CreateDashboardSnapshot(ctx context.Context, cmd *models.Creat return err } - encryptedDashboard, err := s.EncryptionService.Encrypt(ctx, marshalledData, setting.SecretKey) + encryptedDashboard, err := s.SecretsService.Encrypt(ctx, marshalledData, secrets.WithoutScope()) if err != nil { return err } @@ -56,7 +55,7 @@ func (s *Service) GetDashboardSnapshot(ctx context.Context, query *models.GetDas } if query.Result.DashboardEncrypted != nil { - decryptedDashboard, err := s.EncryptionService.Decrypt(ctx, query.Result.DashboardEncrypted, setting.SecretKey) + decryptedDashboard, err := s.SecretsService.Decrypt(ctx, query.Result.DashboardEncrypted) if err != nil { return err } diff --git a/pkg/services/dashboardsnapshots/dashboardsnapshots_test.go b/pkg/services/dashboardsnapshots/dashboardsnapshots_test.go index 1187323fab5..56969b471bd 100644 --- a/pkg/services/dashboardsnapshots/dashboardsnapshots_test.go +++ b/pkg/services/dashboardsnapshots/dashboardsnapshots_test.go @@ -4,9 +4,11 @@ import ( "context" "testing" + "github.com/grafana/grafana/pkg/services/secrets/database" + "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" "github.com/stretchr/testify/require" @@ -14,10 +16,11 @@ import ( func TestDashboardSnapshotsService(t *testing.T) { sqlStore := sqlstore.InitTestDB(t) + secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore)) s := &Service{ - SQLStore: sqlStore, - EncryptionService: ossencryption.ProvideService(), + SQLStore: sqlStore, + SecretsService: secretsService, } origSecret := setting.SecretKey @@ -44,7 +47,7 @@ func TestDashboardSnapshotsService(t *testing.T) { err = s.CreateDashboardSnapshot(ctx, &cmd) require.NoError(t, err) - decrypted, err := s.EncryptionService.Decrypt(ctx, cmd.Result.DashboardEncrypted, setting.SecretKey) + decrypted, err := s.SecretsService.Decrypt(ctx, cmd.Result.DashboardEncrypted) require.NoError(t, err) require.Equal(t, rawDashboard, decrypted) diff --git a/pkg/services/datasources/service.go b/pkg/services/datasources/service.go index d6232511524..706dfce7d3e 100644 --- a/pkg/services/datasources/service.go +++ b/pkg/services/datasources/service.go @@ -14,16 +14,16 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/encryption" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/azcredentials" ) type Service struct { - Bus bus.Bus - SQLStore *sqlstore.SQLStore - EncryptionService encryption.Service + Bus bus.Bus + SQLStore *sqlstore.SQLStore + SecretsService secrets.Service ptc proxyTransportCache dsDecryptionCache secureJSONDecryptionCache @@ -49,11 +49,11 @@ type cachedDecryptedJSON struct { json map[string]string } -func ProvideService(bus bus.Bus, store *sqlstore.SQLStore, encryptionService encryption.Service) *Service { +func ProvideService(bus bus.Bus, store *sqlstore.SQLStore, secretsService secrets.Service) *Service { s := &Service{ - Bus: bus, - SQLStore: store, - EncryptionService: encryptionService, + Bus: bus, + SQLStore: store, + SecretsService: secretsService, ptc: proxyTransportCache{ cache: make(map[int64]cachedRoundTripper), }, @@ -87,7 +87,7 @@ func (s *Service) GetDataSourcesByType(query *models.GetDataSourcesByTypeQuery) func (s *Service) AddDataSource(ctx context.Context, cmd *models.AddDataSourceCommand) error { var err error - cmd.EncryptedSecureJsonData, err = s.EncryptionService.EncryptJsonData(ctx, cmd.SecureJsonData, setting.SecretKey) + cmd.EncryptedSecureJsonData, err = s.SecretsService.EncryptJsonData(ctx, cmd.SecureJsonData, secrets.WithoutScope()) if err != nil { return err } @@ -101,7 +101,7 @@ func (s *Service) DeleteDataSource(ctx context.Context, cmd *models.DeleteDataSo func (s *Service) UpdateDataSource(ctx context.Context, cmd *models.UpdateDataSourceCommand) error { var err error - cmd.EncryptedSecureJsonData, err = s.EncryptionService.EncryptJsonData(ctx, cmd.SecureJsonData, setting.SecretKey) + cmd.EncryptedSecureJsonData, err = s.SecretsService.EncryptJsonData(ctx, cmd.SecureJsonData, secrets.WithoutScope()) if err != nil { return err } @@ -170,7 +170,7 @@ func (s *Service) DecryptedValues(ds *models.DataSource) map[string]string { return item.json } - json, err := s.EncryptionService.DecryptJsonData(context.Background(), ds.SecureJsonData, setting.SecretKey) + json, err := s.SecretsService.DecryptJsonData(context.Background(), ds.SecureJsonData) if err != nil { return map[string]string{} } diff --git a/pkg/services/datasources/service_test.go b/pkg/services/datasources/service_test.go index a6e2bd77c3b..d307d30b330 100644 --- a/pkg/services/datasources/service_test.go +++ b/pkg/services/datasources/service_test.go @@ -13,7 +13,10 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/services/secrets" + "github.com/grafana/grafana/pkg/services/secrets/database" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/azcredentials" @@ -24,14 +27,15 @@ import ( func TestService(t *testing.T) { sqlStore := sqlstore.InitTestDB(t) - s := ProvideService(bus.New(), sqlStore, ossencryption.ProvideService()) - origSecret := setting.SecretKey setting.SecretKey = "datasources_service_test" t.Cleanup(func() { setting.SecretKey = origSecret }) + secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore)) + s := ProvideService(bus.New(), sqlStore, secretsService) + var ds *models.DataSource t.Run("create datasource should encrypt the secure json data", func(t *testing.T) { @@ -44,7 +48,7 @@ func TestService(t *testing.T) { require.NoError(t, err) ds = cmd.Result - decrypted, err := s.EncryptionService.DecryptJsonData(ctx, ds.SecureJsonData, setting.SecretKey) + decrypted, err := s.SecretsService.DecryptJsonData(ctx, ds.SecureJsonData) require.NoError(t, err) require.Equal(t, sjd, decrypted) }) @@ -56,7 +60,7 @@ func TestService(t *testing.T) { err := s.UpdateDataSource(ctx, &cmd) require.NoError(t, err) - decrypted, err := s.EncryptionService.DecryptJsonData(ctx, cmd.Result.SecureJsonData, setting.SecretKey) + decrypted, err := s.SecretsService.DecryptJsonData(ctx, cmd.Result.SecureJsonData) require.NoError(t, err) require.Equal(t, sjd, decrypted) }) @@ -78,7 +82,8 @@ func TestService_GetHttpTransport(t *testing.T) { Type: "Kubernetes", } - dsService := ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) rt1, err := dsService.GetHTTPTransport(&ds, provider) require.NoError(t, err) @@ -110,10 +115,10 @@ func TestService_GetHttpTransport(t *testing.T) { json := simplejson.New() json.Set("tlsAuthWithCACert", true) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) - tlsCaCert, err := encryptionService.Encrypt(context.Background(), []byte(caCert), "password") + tlsCaCert, err := secretsService.Encrypt(context.Background(), []byte(caCert), secrets.WithoutScope()) require.NoError(t, err) ds := models.DataSource{ @@ -160,13 +165,13 @@ func TestService_GetHttpTransport(t *testing.T) { json := simplejson.New() json.Set("tlsAuth", true) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) - tlsClientCert, err := encryptionService.Encrypt(context.Background(), []byte(clientCert), "password") + tlsClientCert, err := secretsService.Encrypt(context.Background(), []byte(clientCert), secrets.WithoutScope()) require.NoError(t, err) - tlsClientKey, err := encryptionService.Encrypt(context.Background(), []byte(clientKey), "password") + tlsClientKey, err := secretsService.Encrypt(context.Background(), []byte(clientKey), secrets.WithoutScope()) require.NoError(t, err) ds := models.DataSource{ @@ -203,10 +208,10 @@ func TestService_GetHttpTransport(t *testing.T) { json.Set("tlsAuthWithCACert", true) json.Set("serverName", "server-name") - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) - tlsCaCert, err := encryptionService.Encrypt(context.Background(), []byte(caCert), "password") + tlsCaCert, err := secretsService.Encrypt(context.Background(), []byte(caCert), secrets.WithoutScope()) require.NoError(t, err) ds := models.DataSource{ @@ -240,8 +245,8 @@ func TestService_GetHttpTransport(t *testing.T) { json := simplejson.New() json.Set("tlsSkipVerify", true) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) ds := models.DataSource{ Id: 1, @@ -271,10 +276,10 @@ func TestService_GetHttpTransport(t *testing.T) { "httpHeaderName1": "Authorization", }) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) - encryptedData, err := encryptionService.Encrypt(context.Background(), []byte(`Bearer xf5yhfkpsnmgo`), setting.SecretKey) + encryptedData, err := secretsService.Encrypt(context.Background(), []byte(`Bearer xf5yhfkpsnmgo`), secrets.WithoutScope()) require.NoError(t, err) ds := models.DataSource{ @@ -330,8 +335,8 @@ func TestService_GetHttpTransport(t *testing.T) { "timeout": 19, }) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) ds := models.DataSource{ Id: 1, @@ -363,8 +368,8 @@ func TestService_GetHttpTransport(t *testing.T) { json, err := simplejson.NewJson([]byte(`{ "sigV4Auth": true }`)) require.NoError(t, err) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) ds := models.DataSource{ Type: models.DS_ES, @@ -397,8 +402,8 @@ func TestService_getTimeout(t *testing.T) { {jsonData: simplejson.NewFromAny(map[string]interface{}{"timeout": "2"}), expectedTimeout: 2 * time.Second}, } - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) for _, tc := range testCases { ds := &models.DataSource{ @@ -410,14 +415,14 @@ func TestService_getTimeout(t *testing.T) { func TestService_DecryptedValue(t *testing.T) { t.Run("When datasource hasn't been updated, encrypted JSON should be fetched from cache", func(t *testing.T) { - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) - encryptedJsonData, err := encryptionService.EncryptJsonData( + encryptedJsonData, err := secretsService.EncryptJsonData( context.Background(), map[string]string{ "password": "password", - }, setting.SecretKey) + }, secrets.WithoutScope()) require.NoError(t, err) ds := models.DataSource{ @@ -433,11 +438,11 @@ func TestService_DecryptedValue(t *testing.T) { require.True(t, ok) require.Equal(t, "password", password) - encryptedJsonData, err = encryptionService.EncryptJsonData( + encryptedJsonData, err = secretsService.EncryptJsonData( context.Background(), map[string]string{ "password": "", - }, setting.SecretKey) + }, secrets.WithoutScope()) require.NoError(t, err) ds.SecureJsonData = encryptedJsonData @@ -448,13 +453,13 @@ func TestService_DecryptedValue(t *testing.T) { }) t.Run("When datasource is updated, encrypted JSON should not be fetched from cache", func(t *testing.T) { - encryptionService := ossencryption.ProvideService() + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) - encryptedJsonData, err := encryptionService.EncryptJsonData( + encryptedJsonData, err := secretsService.EncryptJsonData( context.Background(), map[string]string{ "password": "password", - }, setting.SecretKey) + }, secrets.WithoutScope()) require.NoError(t, err) ds := models.DataSource{ @@ -465,18 +470,18 @@ func TestService_DecryptedValue(t *testing.T) { SecureJsonData: encryptedJsonData, } - dsService := ProvideService(bus.New(), nil, encryptionService) + dsService := ProvideService(bus.New(), nil, secretsService) // Populate cache password, ok := dsService.DecryptedValue(&ds, "password") require.True(t, ok) require.Equal(t, "password", password) - ds.SecureJsonData, err = encryptionService.EncryptJsonData( + ds.SecureJsonData, err = secretsService.EncryptJsonData( context.Background(), map[string]string{ "password": "", - }, setting.SecretKey) + }, secrets.WithoutScope()) ds.Updated = time.Now() require.NoError(t, err) @@ -500,8 +505,8 @@ func TestService_HTTPClientOptions(t *testing.T) { t.Run("should be disabled if not enabled in JsonData", func(t *testing.T) { t.Cleanup(func() { ds.JsonData = emptyJsonData; ds.SecureJsonData = emptySecureJsonData }) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) opts, err := dsService.httpClientOptions(&ds) require.NoError(t, err) @@ -517,8 +522,8 @@ func TestService_HTTPClientOptions(t *testing.T) { "azureAuth": true, }) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) opts, err := dsService.httpClientOptions(&ds) require.NoError(t, err) @@ -537,8 +542,8 @@ func TestService_HTTPClientOptions(t *testing.T) { }, }) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) opts, err := dsService.httpClientOptions(&ds) require.NoError(t, err) @@ -561,8 +566,8 @@ func TestService_HTTPClientOptions(t *testing.T) { }, }) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) opts, err := dsService.httpClientOptions(&ds) require.NoError(t, err) @@ -579,8 +584,8 @@ func TestService_HTTPClientOptions(t *testing.T) { "azureCredentials": "invalid", }) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) _, err := dsService.httpClientOptions(&ds) assert.Error(t, err) @@ -593,8 +598,8 @@ func TestService_HTTPClientOptions(t *testing.T) { "azureEndpointResourceId": "https://api.example.com/abd5c4ce-ca73-41e9-9cb2-bed39aa2adb5", }) - encryptionService := ossencryption.ProvideService() - dsService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := ProvideService(bus.New(), nil, secretsService) opts, err := dsService.httpClientOptions(&ds) require.NoError(t, err) diff --git a/pkg/services/encryption/encryption.go b/pkg/services/encryption/encryption.go index 31cef0392ed..2ab4bb4c492 100644 --- a/pkg/services/encryption/encryption.go +++ b/pkg/services/encryption/encryption.go @@ -2,6 +2,8 @@ package encryption import "context" +// Service must not be used for encryption, +// use secrets.Service implementing envelope encryption instead. type Service interface { Encrypt(ctx context.Context, payload []byte, secret string) ([]byte, error) Decrypt(ctx context.Context, payload []byte, secret string) ([]byte, error) diff --git a/pkg/services/encryption/ossencryption/ossencryption.go b/pkg/services/encryption/ossencryption/ossencryption.go index 4cf351f8fc7..60168b48e9c 100644 --- a/pkg/services/encryption/ossencryption/ossencryption.go +++ b/pkg/services/encryption/ossencryption/ossencryption.go @@ -14,6 +14,8 @@ import ( "golang.org/x/crypto/pbkdf2" ) +// Service must not be used for encryption, +// use secrets.Service implementing envelope encryption instead. type Service struct{} func ProvideService() *Service { diff --git a/pkg/services/login/authinfoservice/database.go b/pkg/services/login/authinfoservice/database.go index a2a95a8ae57..a59183f5169 100644 --- a/pkg/services/login/authinfoservice/database.go +++ b/pkg/services/login/authinfoservice/database.go @@ -5,10 +5,10 @@ import ( "encoding/base64" "time" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" ) var getTime = time.Now @@ -161,7 +161,7 @@ func (s *Implementation) DeleteAuthInfo(cmd *models.DeleteAuthInfoCommand) error // decodeAndDecrypt will decode the string with the standard base64 decoder and then decrypt it func (s *Implementation) decodeAndDecrypt(str string) (string, error) { - // Bail out if empty string since it'll cause a segfault in util.Decrypt + // Bail out if empty string since it'll cause a segfault in Decrypt if str == "" { return "", nil } @@ -169,7 +169,7 @@ func (s *Implementation) decodeAndDecrypt(str string) (string, error) { if err != nil { return "", err } - decrypted, err := s.EncryptionService.Decrypt(context.Background(), decoded, setting.SecretKey) + decrypted, err := s.SecretsService.Decrypt(context.Background(), decoded) if err != nil { return "", err } @@ -179,7 +179,7 @@ func (s *Implementation) decodeAndDecrypt(str string) (string, error) { // encryptAndEncode will encrypt a string with grafana's secretKey, and // then encode it with the standard bas64 encoder func (s *Implementation) encryptAndEncode(str string) (string, error) { - encrypted, err := s.EncryptionService.Encrypt(context.Background(), []byte(str), setting.SecretKey) + encrypted, err := s.SecretsService.Encrypt(context.Background(), []byte(str), secrets.WithoutScope()) if err != nil { return "", err } diff --git a/pkg/services/login/authinfoservice/service.go b/pkg/services/login/authinfoservice/service.go index c691709ab40..8dd5cdd04d7 100644 --- a/pkg/services/login/authinfoservice/service.go +++ b/pkg/services/login/authinfoservice/service.go @@ -4,12 +4,11 @@ import ( "context" "errors" - "github.com/grafana/grafana/pkg/services/encryption" - "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/login" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/sqlstore" ) @@ -19,17 +18,17 @@ type Implementation struct { Bus bus.Bus SQLStore *sqlstore.SQLStore UserProtectionService login.UserProtectionService - EncryptionService encryption.Service + SecretsService secrets.Service logger log.Logger } func ProvideAuthInfoService(bus bus.Bus, store *sqlstore.SQLStore, userProtectionService login.UserProtectionService, - encryptionService encryption.Service) *Implementation { + secretsService secrets.Service) *Implementation { s := &Implementation{ Bus: bus, SQLStore: store, UserProtectionService: userProtectionService, - EncryptionService: encryptionService, + SecretsService: secretsService, logger: log.New("login.authinfo"), } diff --git a/pkg/services/login/authinfoservice/user_auth_test.go b/pkg/services/login/authinfoservice/user_auth_test.go index 3f4ae73be03..bba765b3fb9 100644 --- a/pkg/services/login/authinfoservice/user_auth_test.go +++ b/pkg/services/login/authinfoservice/user_auth_test.go @@ -9,21 +9,20 @@ import ( "testing" "time" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" - - "github.com/grafana/grafana/pkg/services/sqlstore" - "github.com/grafana/grafana/pkg/bus" - "github.com/stretchr/testify/require" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/secrets/database" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" + "github.com/grafana/grafana/pkg/services/sqlstore" + "github.com/stretchr/testify/require" "golang.org/x/oauth2" ) //nolint:goconst func TestUserAuth(t *testing.T) { sqlStore := sqlstore.InitTestDB(t) - srv := ProvideAuthInfoService(bus.New(), sqlStore, &OSSUserProtectionImpl{}, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore)) + srv := ProvideAuthInfoService(bus.New(), sqlStore, &OSSUserProtectionImpl{}, secretsService) t.Run("Given 5 users", func(t *testing.T) { for i := 0; i < 5; i++ { diff --git a/pkg/services/ngalert/api/api.go b/pkg/services/ngalert/api/api.go index b0b046ac78f..f7d34f0af80 100644 --- a/pkg/services/ngalert/api/api.go +++ b/pkg/services/ngalert/api/api.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/datasourceproxy" "github.com/grafana/grafana/pkg/services/datasources" - "github.com/grafana/grafana/pkg/services/encryption" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/metrics" "github.com/grafana/grafana/pkg/services/ngalert/notifier" @@ -17,6 +16,7 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/state" "github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/quota" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb" ) @@ -64,7 +64,7 @@ type API struct { DataProxy *datasourceproxy.DataSourceProxyService MultiOrgAlertmanager *notifier.MultiOrgAlertmanager StateManager *state.Manager - EncryptionService encryption.Service + SecretsService secrets.Service } // RegisterAPIEndpoints registers API handlers @@ -78,7 +78,7 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) { api.RegisterAlertmanagerApiEndpoints(NewForkedAM( api.DatasourceCache, NewLotexAM(proxy, logger), - AlertmanagerSrv{store: api.AlertingStore, mam: api.MultiOrgAlertmanager, enc: api.EncryptionService, log: logger}, + AlertmanagerSrv{store: api.AlertingStore, mam: api.MultiOrgAlertmanager, secrets: api.SecretsService, log: logger}, ), m) // Register endpoints for proxying to Prometheus-compatible backends. api.RegisterPrometheusApiEndpoints(NewForkedProm( diff --git a/pkg/services/ngalert/api/api_alertmanager.go b/pkg/services/ngalert/api/api_alertmanager.go index 852a20980a2..0196aa1507f 100644 --- a/pkg/services/ngalert/api/api_alertmanager.go +++ b/pkg/services/ngalert/api/api_alertmanager.go @@ -13,12 +13,11 @@ import ( "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/encryption" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/notifier" "github.com/grafana/grafana/pkg/services/ngalert/store" - "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/web" ) @@ -29,10 +28,10 @@ const ( ) type AlertmanagerSrv struct { - mam *notifier.MultiOrgAlertmanager - enc encryption.Service - store store.AlertingStore - log log.Logger + mam *notifier.MultiOrgAlertmanager + secrets secrets.Service + store store.AlertingStore + log log.Logger } type UnknownReceiverError struct { @@ -108,7 +107,7 @@ func (srv AlertmanagerSrv) getDecryptedSecret(r *apimodels.PostableGrafanaReceiv return "", err } - decryptedValue, err := srv.enc.Decrypt(context.Background(), decodeValue, setting.SecretKey) + decryptedValue, err := srv.secrets.Decrypt(context.Background(), decodeValue) if err != nil { return "", err } @@ -356,7 +355,7 @@ func (srv AlertmanagerSrv) RoutePostAlertingConfig(c *models.ReqContext, body ap return ErrResp(http.StatusInternalServerError, err, "") } - if err := body.ProcessConfig(srv.enc.Encrypt); err != nil { + if err := body.ProcessConfig(srv.secrets.Encrypt); err != nil { return ErrResp(http.StatusInternalServerError, err, "failed to post process Alertmanager configuration") } @@ -390,7 +389,7 @@ func (srv AlertmanagerSrv) RoutePostTestReceivers(c *models.ReqContext, body api return ErrResp(http.StatusInternalServerError, err, "") } - if err := body.ProcessConfig(srv.enc.Encrypt); err != nil { + if err := body.ProcessConfig(srv.secrets.Encrypt); err != nil { return ErrResp(http.StatusInternalServerError, err, "failed to post process Alertmanager configuration") } diff --git a/pkg/services/ngalert/api/tooling/definitions/alertmanager.go b/pkg/services/ngalert/api/tooling/definitions/alertmanager.go index 4c256e917e3..af6dc8d00e6 100644 --- a/pkg/services/ngalert/api/tooling/definitions/alertmanager.go +++ b/pkg/services/ngalert/api/tooling/definitions/alertmanager.go @@ -11,7 +11,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/util" "github.com/pkg/errors" amv2 "github.com/prometheus/alertmanager/api/v2/models" @@ -1053,7 +1053,7 @@ type PostableGrafanaReceivers struct { GrafanaManagedReceivers []*PostableGrafanaReceiver `yaml:"grafana_managed_receiver_configs,omitempty" json:"grafana_managed_receiver_configs,omitempty"` } -type EncryptFn func(ctx context.Context, payload []byte, secret string) ([]byte, error) +type EncryptFn func(ctx context.Context, payload []byte, scope secrets.EncryptionOptions) ([]byte, error) func processReceiverConfigs(c []*PostableApiReceiver, encrypt EncryptFn) error { seenUIDs := make(map[string]struct{}) @@ -1063,7 +1063,7 @@ func processReceiverConfigs(c []*PostableApiReceiver, encrypt EncryptFn) error { case GrafanaReceiverType: for _, gr := range r.PostableGrafanaReceivers.GrafanaManagedReceivers { for k, v := range gr.SecureSettings { - encryptedData, err := encrypt(context.Background(), []byte(v), setting.SecretKey) + encryptedData, err := encrypt(context.Background(), []byte(v), secrets.WithoutScope()) if err != nil { return fmt.Errorf("failed to encrypt secure settings: %w", err) } diff --git a/pkg/services/ngalert/ngalert.go b/pkg/services/ngalert/ngalert.go index ea0f1e34915..b2f91e9b382 100644 --- a/pkg/services/ngalert/ngalert.go +++ b/pkg/services/ngalert/ngalert.go @@ -11,7 +11,6 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/datasourceproxy" "github.com/grafana/grafana/pkg/services/datasources" - "github.com/grafana/grafana/pkg/services/encryption" "github.com/grafana/grafana/pkg/services/ngalert/api" "github.com/grafana/grafana/pkg/services/ngalert/eval" "github.com/grafana/grafana/pkg/services/ngalert/metrics" @@ -20,6 +19,7 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/state" "github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/quota" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb" @@ -39,19 +39,19 @@ const ( func ProvideService(cfg *setting.Cfg, dataSourceCache datasources.CacheService, routeRegister routing.RouteRegister, sqlStore *sqlstore.SQLStore, kvStore kvstore.KVStore, dataService *tsdb.Service, dataProxy *datasourceproxy.DataSourceProxyService, - quotaService *quota.QuotaService, encryptionService encryption.Service, m *metrics.NGAlert) (*AlertNG, error) { + quotaService *quota.QuotaService, secretsService secrets.Service, m *metrics.NGAlert) (*AlertNG, error) { ng := &AlertNG{ - Cfg: cfg, - DataSourceCache: dataSourceCache, - RouteRegister: routeRegister, - SQLStore: sqlStore, - KVStore: kvStore, - DataService: dataService, - DataProxy: dataProxy, - QuotaService: quotaService, - EncryptionService: encryptionService, - Metrics: m, - Log: log.New("ngalert"), + Cfg: cfg, + DataSourceCache: dataSourceCache, + RouteRegister: routeRegister, + SQLStore: sqlStore, + KVStore: kvStore, + DataService: dataService, + DataProxy: dataProxy, + QuotaService: quotaService, + SecretsService: secretsService, + Metrics: m, + Log: log.New("ngalert"), } if ng.IsDisabled() { @@ -67,19 +67,19 @@ func ProvideService(cfg *setting.Cfg, dataSourceCache datasources.CacheService, // AlertNG is the service for evaluating the condition of an alert definition. type AlertNG struct { - Cfg *setting.Cfg - DataSourceCache datasources.CacheService - RouteRegister routing.RouteRegister - SQLStore *sqlstore.SQLStore - KVStore kvstore.KVStore - DataService *tsdb.Service - DataProxy *datasourceproxy.DataSourceProxyService - QuotaService *quota.QuotaService - EncryptionService encryption.Service - Metrics *metrics.NGAlert - Log log.Logger - schedule schedule.ScheduleService - stateManager *state.Manager + Cfg *setting.Cfg + DataSourceCache datasources.CacheService + RouteRegister routing.RouteRegister + SQLStore *sqlstore.SQLStore + KVStore kvstore.KVStore + DataService *tsdb.Service + DataProxy *datasourceproxy.DataSourceProxyService + QuotaService *quota.QuotaService + SecretsService secrets.Service + Metrics *metrics.NGAlert + Log log.Logger + schedule schedule.ScheduleService + stateManager *state.Manager // Alerting notification services MultiOrgAlertmanager *notifier.MultiOrgAlertmanager @@ -101,7 +101,7 @@ func (ng *AlertNG) init() error { Logger: ng.Log, } - decryptFn := ng.EncryptionService.GetDecryptedValue + decryptFn := ng.SecretsService.GetDecryptedValue multiOrgMetrics := ng.Metrics.GetMultiOrgAlertmanagerMetrics() ng.MultiOrgAlertmanager, err = notifier.NewMultiOrgAlertmanager(ng.Cfg, store, store, ng.KVStore, decryptFn, multiOrgMetrics, log.New("ngalert.multiorg.alertmanager")) if err != nil { @@ -149,7 +149,7 @@ func (ng *AlertNG) init() error { Schedule: ng.schedule, DataProxy: ng.DataProxy, QuotaService: ng.QuotaService, - EncryptionService: ng.EncryptionService, + SecretsService: ng.SecretsService, InstanceStore: store, RuleStore: store, AlertingStore: store, diff --git a/pkg/services/ngalert/notifier/alertmanager_test.go b/pkg/services/ngalert/notifier/alertmanager_test.go index 232c16238e6..81e45c02ed0 100644 --- a/pkg/services/ngalert/notifier/alertmanager_test.go +++ b/pkg/services/ngalert/notifier/alertmanager_test.go @@ -9,14 +9,16 @@ import ( "testing" "time" + "github.com/grafana/grafana/pkg/services/secrets/database" + gokit_log "github.com/go-kit/kit/log" "github.com/go-openapi/strfmt" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/logging" "github.com/grafana/grafana/pkg/services/ngalert/metrics" "github.com/grafana/grafana/pkg/services/ngalert/store" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/api/v2/models" @@ -47,7 +49,8 @@ func setupAMTest(t *testing.T) *Alertmanager { } kvStore := newFakeKVStore(t) - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore)) + decryptFn := secretsService.GetDecryptedValue am, err := newAlertmanager(1, cfg, s, kvStore, &NilPeer{}, decryptFn, m) require.NoError(t, err) return am diff --git a/pkg/services/ngalert/notifier/channels/alertmanager.go b/pkg/services/ngalert/notifier/channels/alertmanager.go index 309eae51fe4..99485420e14 100644 --- a/pkg/services/ngalert/notifier/channels/alertmanager.go +++ b/pkg/services/ngalert/notifier/channels/alertmanager.go @@ -9,14 +9,13 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" ) // GetDecryptedValueFn is a function that returns the decrypted value of // the given key. If the key is not present, then it returns the fallback value. -type GetDecryptedValueFn func(ctx context.Context, sjd map[string][]byte, key string, fallback string, secret string) string +type GetDecryptedValueFn func(ctx context.Context, sjd map[string][]byte, key string, fallback string) string // NewAlertmanagerNotifier returns a new Alertmanager notifier. func NewAlertmanagerNotifier(model *NotificationChannelConfig, _ *template.Template, fn GetDecryptedValueFn) (*AlertmanagerNotifier, error) { @@ -45,7 +44,7 @@ func NewAlertmanagerNotifier(model *NotificationChannelConfig, _ *template.Templ urls = append(urls, u) } basicAuthUser := model.Settings.Get("basicAuthUser").MustString() - basicAuthPassword := fn(context.Background(), model.SecureSettings, "basicAuthPassword", model.Settings.Get("basicAuthPassword").MustString(), setting.SecretKey) + basicAuthPassword := fn(context.Background(), model.SecureSettings, "basicAuthPassword", model.Settings.Get("basicAuthPassword").MustString()) return &AlertmanagerNotifier{ Base: NewBase(&models.AlertNotification{ diff --git a/pkg/services/ngalert/notifier/channels/alertmanager_test.go b/pkg/services/ngalert/notifier/channels/alertmanager_test.go index 17211ee6a68..35790d4f224 100644 --- a/pkg/services/ngalert/notifier/channels/alertmanager_test.go +++ b/pkg/services/ngalert/notifier/channels/alertmanager_test.go @@ -7,15 +7,15 @@ import ( "net/url" "testing" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/infra/log" ) func TestNewAlertmanagerNotifier(t *testing.T) { @@ -56,7 +56,8 @@ func TestNewAlertmanagerNotifier(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue sn, err := NewAlertmanagerNotifier(m, tmpl, decryptFn) if c.expectedInitError != "" { require.Equal(t, c.expectedInitError, err.Error()) @@ -136,7 +137,8 @@ func TestAlertmanagerNotifier_Notify(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue sn, err := NewAlertmanagerNotifier(m, tmpl, decryptFn) require.NoError(t, err) diff --git a/pkg/services/ngalert/notifier/channels/line.go b/pkg/services/ngalert/notifier/channels/line.go index 4c7c4a7182d..9cebd032c60 100644 --- a/pkg/services/ngalert/notifier/channels/line.go +++ b/pkg/services/ngalert/notifier/channels/line.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" ) @@ -20,7 +19,7 @@ var ( // NewLineNotifier is the constructor for the LINE notifier func NewLineNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*LineNotifier, error) { - token := fn(context.Background(), model.SecureSettings, "token", model.Settings.Get("token").MustString(), setting.SecretKey) + token := fn(context.Background(), model.SecureSettings, "token", model.Settings.Get("token").MustString()) if token == "" { return nil, receiverInitError{Cfg: *model, Reason: "could not find token in settings"} } diff --git a/pkg/services/ngalert/notifier/channels/line_test.go b/pkg/services/ngalert/notifier/channels/line_test.go index 3915183db36..a32b215005e 100644 --- a/pkg/services/ngalert/notifier/channels/line_test.go +++ b/pkg/services/ngalert/notifier/channels/line_test.go @@ -5,8 +5,6 @@ import ( "net/url" "testing" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" - "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" @@ -15,6 +13,8 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" ) func TestLineNotifier(t *testing.T) { @@ -90,7 +90,8 @@ func TestLineNotifier(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue pn, err := NewLineNotifier(m, tmpl, decryptFn) if c.expInitError != "" { require.Error(t, err) diff --git a/pkg/services/ngalert/notifier/channels/opsgenie.go b/pkg/services/ngalert/notifier/channels/opsgenie.go index 1fbbbcf52b5..b33b806a19c 100644 --- a/pkg/services/ngalert/notifier/channels/opsgenie.go +++ b/pkg/services/ngalert/notifier/channels/opsgenie.go @@ -11,7 +11,6 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" @@ -45,7 +44,7 @@ type OpsgenieNotifier struct { func NewOpsgenieNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*OpsgenieNotifier, error) { autoClose := model.Settings.Get("autoClose").MustBool(true) overridePriority := model.Settings.Get("overridePriority").MustBool(true) - apiKey := fn(context.Background(), model.SecureSettings, "apiKey", model.Settings.Get("apiKey").MustString(), setting.SecretKey) + apiKey := fn(context.Background(), model.SecureSettings, "apiKey", model.Settings.Get("apiKey").MustString()) apiURL := model.Settings.Get("apiUrl").MustString() if apiKey == "" { return nil, receiverInitError{Cfg: *model, Reason: "could not find api key property in settings"} diff --git a/pkg/services/ngalert/notifier/channels/opsgenie_test.go b/pkg/services/ngalert/notifier/channels/opsgenie_test.go index 51ecdd0195e..8f07deb00e0 100644 --- a/pkg/services/ngalert/notifier/channels/opsgenie_test.go +++ b/pkg/services/ngalert/notifier/channels/opsgenie_test.go @@ -6,16 +6,16 @@ import ( "testing" "time" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/models" ) func TestOpsgenieNotifier(t *testing.T) { @@ -170,7 +170,8 @@ func TestOpsgenieNotifier(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue pn, err := NewOpsgenieNotifier(m, tmpl, decryptFn) if c.expInitError != "" { require.Error(t, err) diff --git a/pkg/services/ngalert/notifier/channels/pagerduty.go b/pkg/services/ngalert/notifier/channels/pagerduty.go index 997139b7159..1b3fb980a72 100644 --- a/pkg/services/ngalert/notifier/channels/pagerduty.go +++ b/pkg/services/ngalert/notifier/channels/pagerduty.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" @@ -46,7 +45,7 @@ func NewPagerdutyNotifier(model *NotificationChannelConfig, t *template.Template return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"} } - key := fn(context.Background(), model.SecureSettings, "integrationKey", model.Settings.Get("integrationKey").MustString(), setting.SecretKey) + key := fn(context.Background(), model.SecureSettings, "integrationKey", model.Settings.Get("integrationKey").MustString()) if key == "" { return nil, receiverInitError{Cfg: *model, Reason: "could not find integration key property in settings"} } diff --git a/pkg/services/ngalert/notifier/channels/pagerduty_test.go b/pkg/services/ngalert/notifier/channels/pagerduty_test.go index 4562ff7b1b7..a3d1c329b77 100644 --- a/pkg/services/ngalert/notifier/channels/pagerduty_test.go +++ b/pkg/services/ngalert/notifier/channels/pagerduty_test.go @@ -7,16 +7,16 @@ import ( "os" "testing" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/models" ) func TestPagerdutyNotifier(t *testing.T) { @@ -136,7 +136,8 @@ func TestPagerdutyNotifier(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue pn, err := NewPagerdutyNotifier(m, tmpl, decryptFn) if c.expInitError != "" { require.Error(t, err) diff --git a/pkg/services/ngalert/notifier/channels/pushover.go b/pkg/services/ngalert/notifier/channels/pushover.go index 7e810cb1f1d..ad7ff9bc830 100644 --- a/pkg/services/ngalert/notifier/channels/pushover.go +++ b/pkg/services/ngalert/notifier/channels/pushover.go @@ -10,7 +10,6 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" @@ -45,8 +44,8 @@ func NewPushoverNotifier(model *NotificationChannelConfig, t *template.Template, return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"} } - userKey := fn(context.Background(), model.SecureSettings, "userKey", model.Settings.Get("userKey").MustString(), setting.SecretKey) - APIToken := fn(context.Background(), model.SecureSettings, "apiToken", model.Settings.Get("apiToken").MustString(), setting.SecretKey) + userKey := fn(context.Background(), model.SecureSettings, "userKey", model.Settings.Get("userKey").MustString()) + APIToken := fn(context.Background(), model.SecureSettings, "apiToken", model.Settings.Get("apiToken").MustString()) device := model.Settings.Get("device").MustString() alertingPriority, err := strconv.Atoi(model.Settings.Get("priority").MustString("0")) // default Normal if err != nil { diff --git a/pkg/services/ngalert/notifier/channels/pushover_test.go b/pkg/services/ngalert/notifier/channels/pushover_test.go index 0e8acc354fe..e5d953ebf0e 100644 --- a/pkg/services/ngalert/notifier/channels/pushover_test.go +++ b/pkg/services/ngalert/notifier/channels/pushover_test.go @@ -11,11 +11,12 @@ import ( "strings" "testing" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" - "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" + "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" @@ -143,7 +144,8 @@ func TestPushoverNotifier(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue pn, err := NewPushoverNotifier(m, tmpl, decryptFn) if c.expInitError != "" { require.Error(t, err) diff --git a/pkg/services/ngalert/notifier/channels/sensugo.go b/pkg/services/ngalert/notifier/channels/sensugo.go index 1912374c1eb..d93e2bcb8ef 100644 --- a/pkg/services/ngalert/notifier/channels/sensugo.go +++ b/pkg/services/ngalert/notifier/channels/sensugo.go @@ -10,7 +10,6 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" @@ -41,7 +40,7 @@ func NewSensuGoNotifier(model *NotificationChannelConfig, t *template.Template, return nil, receiverInitError{Cfg: *model, Reason: "could not find URL property in settings"} } - apikey := fn(context.Background(), model.SecureSettings, "apikey", model.Settings.Get("apikey").MustString(), setting.SecretKey) + apikey := fn(context.Background(), model.SecureSettings, "apikey", model.Settings.Get("apikey").MustString()) if apikey == "" { return nil, receiverInitError{Cfg: *model, Reason: "could not find the API key property in settings"} } diff --git a/pkg/services/ngalert/notifier/channels/sensugo_test.go b/pkg/services/ngalert/notifier/channels/sensugo_test.go index f4d97683eb1..a84053928d9 100644 --- a/pkg/services/ngalert/notifier/channels/sensugo_test.go +++ b/pkg/services/ngalert/notifier/channels/sensugo_test.go @@ -7,16 +7,16 @@ import ( "testing" "time" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/models" ) func TestSensuGoNotifier(t *testing.T) { @@ -141,7 +141,8 @@ func TestSensuGoNotifier(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue sn, err := NewSensuGoNotifier(m, tmpl, decryptFn) if c.expInitError != "" { require.Error(t, err) diff --git a/pkg/services/ngalert/notifier/channels/slack.go b/pkg/services/ngalert/notifier/channels/slack.go index 7d9af7eb525..58d0a045f9b 100644 --- a/pkg/services/ngalert/notifier/channels/slack.go +++ b/pkg/services/ngalert/notifier/channels/slack.go @@ -49,7 +49,7 @@ func NewSlackNotifier(model *NotificationChannelConfig, t *template.Template, fn return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"} } - slackURL := fn(context.Background(), model.SecureSettings, "url", model.Settings.Get("url").MustString(), setting.SecretKey) + slackURL := fn(context.Background(), model.SecureSettings, "url", model.Settings.Get("url").MustString()) if slackURL == "" { slackURL = SlackAPIEndpoint } @@ -90,7 +90,7 @@ func NewSlackNotifier(model *NotificationChannelConfig, t *template.Template, fn } } - token := fn(context.Background(), model.SecureSettings, "token", model.Settings.Get("token").MustString(), setting.SecretKey) + token := fn(context.Background(), model.SecureSettings, "token", model.Settings.Get("token").MustString()) if token == "" && apiURL.String() == SlackAPIEndpoint { return nil, receiverInitError{Cfg: *model, Reason: "token must be specified when using the Slack chat API", diff --git a/pkg/services/ngalert/notifier/channels/slack_test.go b/pkg/services/ngalert/notifier/channels/slack_test.go index 8cd75d24867..109301f8b5d 100644 --- a/pkg/services/ngalert/notifier/channels/slack_test.go +++ b/pkg/services/ngalert/notifier/channels/slack_test.go @@ -9,15 +9,15 @@ import ( "net/url" "testing" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/infra/log" ) func TestSlackNotifier(t *testing.T) { @@ -172,7 +172,8 @@ func TestSlackNotifier(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue pn, err := NewSlackNotifier(m, tmpl, decryptFn) if c.expInitError != "" { require.Error(t, err) diff --git a/pkg/services/ngalert/notifier/channels/telegram.go b/pkg/services/ngalert/notifier/channels/telegram.go index d741626c4f6..cea09366e0f 100644 --- a/pkg/services/ngalert/notifier/channels/telegram.go +++ b/pkg/services/ngalert/notifier/channels/telegram.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" ) @@ -35,7 +34,7 @@ func NewTelegramNotifier(model *NotificationChannelConfig, t *template.Template, return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"} } - botToken := fn(context.Background(), model.SecureSettings, "bottoken", model.Settings.Get("bottoken").MustString(), setting.SecretKey) + botToken := fn(context.Background(), model.SecureSettings, "bottoken", model.Settings.Get("bottoken").MustString()) chatID := model.Settings.Get("chatid").MustString() message := model.Settings.Get("message").MustString(`{{ template "default.message" . }}`) diff --git a/pkg/services/ngalert/notifier/channels/telegram_test.go b/pkg/services/ngalert/notifier/channels/telegram_test.go index a5799abcdf5..b7e124a887e 100644 --- a/pkg/services/ngalert/notifier/channels/telegram_test.go +++ b/pkg/services/ngalert/notifier/channels/telegram_test.go @@ -5,14 +5,14 @@ import ( "net/url" "testing" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/components/simplejson" ) func TestTelegramNotifier(t *testing.T) { @@ -96,7 +96,8 @@ func TestTelegramNotifier(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue pn, err := NewTelegramNotifier(m, tmpl, decryptFn) if c.expInitError != "" { require.Error(t, err) diff --git a/pkg/services/ngalert/notifier/channels/threema.go b/pkg/services/ngalert/notifier/channels/threema.go index 09f75a0be72..d1ed3244511 100644 --- a/pkg/services/ngalert/notifier/channels/threema.go +++ b/pkg/services/ngalert/notifier/channels/threema.go @@ -10,7 +10,6 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" @@ -39,7 +38,7 @@ func NewThreemaNotifier(model *NotificationChannelConfig, t *template.Template, gatewayID := model.Settings.Get("gateway_id").MustString() recipientID := model.Settings.Get("recipient_id").MustString() - apiSecret := fn(context.Background(), model.SecureSettings, "api_secret", model.Settings.Get("api_secret").MustString(), setting.SecretKey) + apiSecret := fn(context.Background(), model.SecureSettings, "api_secret", model.Settings.Get("api_secret").MustString()) // Validation if gatewayID == "" { diff --git a/pkg/services/ngalert/notifier/channels/threema_test.go b/pkg/services/ngalert/notifier/channels/threema_test.go index d2d84f1bf76..dac8d345719 100644 --- a/pkg/services/ngalert/notifier/channels/threema_test.go +++ b/pkg/services/ngalert/notifier/channels/threema_test.go @@ -5,16 +5,16 @@ import ( "net/url" "testing" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/models" ) func TestThreemaNotifier(t *testing.T) { @@ -108,7 +108,8 @@ func TestThreemaNotifier(t *testing.T) { Settings: settingsJSON, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue pn, err := NewThreemaNotifier(m, tmpl, decryptFn) if c.expInitError != "" { require.Error(t, err) diff --git a/pkg/services/ngalert/notifier/channels/webhook.go b/pkg/services/ngalert/notifier/channels/webhook.go index 712c1d640ed..c2d2ea3f8e8 100644 --- a/pkg/services/ngalert/notifier/channels/webhook.go +++ b/pkg/services/ngalert/notifier/channels/webhook.go @@ -7,7 +7,6 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" @@ -49,7 +48,7 @@ func NewWebHookNotifier(model *NotificationChannelConfig, t *template.Template, orgID: model.OrgID, URL: url, User: model.Settings.Get("username").MustString(), - Password: fn(context.Background(), model.SecureSettings, "password", model.Settings.Get("password").MustString(), setting.SecretKey), + Password: fn(context.Background(), model.SecureSettings, "password", model.Settings.Get("password").MustString()), HTTPMethod: model.Settings.Get("httpMethod").MustString("POST"), MaxAlerts: model.Settings.Get("maxAlerts").MustInt(0), log: log.New("alerting.notifier.webhook"), diff --git a/pkg/services/ngalert/notifier/channels/webhook_test.go b/pkg/services/ngalert/notifier/channels/webhook_test.go index 2fc0e6fa2a8..4e6272a51ca 100644 --- a/pkg/services/ngalert/notifier/channels/webhook_test.go +++ b/pkg/services/ngalert/notifier/channels/webhook_test.go @@ -6,17 +6,17 @@ import ( "net/url" "testing" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/models" ) func TestWebhookNotifier(t *testing.T) { @@ -190,7 +190,8 @@ func TestWebhookNotifier(t *testing.T) { OrgID: orgID, } - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue pn, err := NewWebHookNotifier(m, tmpl, decryptFn) if c.expInitError != "" { require.Error(t, err) diff --git a/pkg/services/ngalert/notifier/multiorg_alertmanager_test.go b/pkg/services/ngalert/notifier/multiorg_alertmanager_test.go index 30b1f7a373d..16cbb19e59e 100644 --- a/pkg/services/ngalert/notifier/multiorg_alertmanager_test.go +++ b/pkg/services/ngalert/notifier/multiorg_alertmanager_test.go @@ -12,10 +12,12 @@ import ( "time" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" "github.com/grafana/grafana/pkg/services/ngalert/metrics" "github.com/grafana/grafana/pkg/services/ngalert/models" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/setting" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/require" @@ -32,7 +34,8 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgs(t *testing.T) { tmpDir, err := ioutil.TempDir("", "test") require.NoError(t, err) kvStore := newFakeKVStore(t) - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue reg := prometheus.NewPedanticRegistry() m := metrics.NewNGAlert(reg) cfg := &setting.Cfg{ @@ -159,7 +162,8 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgsWithFailures(t *testing.T) tmpDir, err := ioutil.TempDir("", "test") require.NoError(t, err) kvStore := newFakeKVStore(t) - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue reg := prometheus.NewPedanticRegistry() m := metrics.NewNGAlert(reg) cfg := &setting.Cfg{ @@ -216,7 +220,8 @@ func TestMultiOrgAlertmanager_AlertmanagerFor(t *testing.T) { UnifiedAlerting: setting.UnifiedAlertingSettings{AlertmanagerConfigPollInterval: 3 * time.Minute, DefaultConfiguration: setting.GetAlertmanagerDefaultConfiguration()}, // do not poll in tests. } kvStore := newFakeKVStore(t) - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue reg := prometheus.NewPedanticRegistry() m := metrics.NewNGAlert(reg) mam, err := NewMultiOrgAlertmanager(cfg, configStore, orgStore, kvStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), log.New("testlogger")) diff --git a/pkg/services/ngalert/schedule/schedule_unit_test.go b/pkg/services/ngalert/schedule/schedule_unit_test.go index 11ff60982f1..04fa3a081cd 100644 --- a/pkg/services/ngalert/schedule/schedule_unit_test.go +++ b/pkg/services/ngalert/schedule/schedule_unit_test.go @@ -18,7 +18,6 @@ import ( "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/eval" "github.com/grafana/grafana/pkg/services/ngalert/metrics" @@ -27,6 +26,8 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/sender" "github.com/grafana/grafana/pkg/services/ngalert/state" "github.com/grafana/grafana/pkg/services/ngalert/store" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/setting" ) @@ -580,7 +581,8 @@ func setupScheduler(t *testing.T, rs store.RuleStore, is store.InstanceStore, ac registry = prometheus.NewPedanticRegistry() } m := metrics.NewNGAlert(registry) - decryptFn := ossencryption.ProvideService().GetDecryptedValue + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + decryptFn := secretsService.GetDecryptedValue moa, err := notifier.NewMultiOrgAlertmanager(&setting.Cfg{}, ¬ifier.FakeConfigStore{}, ¬ifier.FakeOrgStore{}, ¬ifier.FakeKVStore{}, decryptFn, nil, log.New("testlogger")) require.NoError(t, err) diff --git a/pkg/services/ngalert/tests/util.go b/pkg/services/ngalert/tests/util.go index e1533c9052e..f14158f6388 100644 --- a/pkg/services/ngalert/tests/util.go +++ b/pkg/services/ngalert/tests/util.go @@ -7,14 +7,16 @@ import ( "testing" "time" + "github.com/grafana/grafana/pkg/services/secrets/database" + "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" "github.com/grafana/grafana/pkg/services/ngalert" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/metrics" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/store" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/client_golang/prometheus" @@ -36,9 +38,11 @@ func SetupTestEnv(t *testing.T, baseInterval time.Duration) (*ngalert.AlertNG, * cfg.UnifiedAlerting.Enabled = true m := metrics.NewNGAlert(prometheus.NewRegistry()) + sqlStore := sqlstore.InitTestDB(t) + secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore)) ng, err := ngalert.ProvideService( - cfg, nil, routing.NewRouteRegister(), sqlstore.InitTestDB(t), - nil, nil, nil, nil, ossencryption.ProvideService(), m, + cfg, nil, routing.NewRouteRegister(), sqlStore, + nil, nil, nil, nil, secretsService, m, ) require.NoError(t, err) return ng, &store.DBstore{ diff --git a/pkg/services/pluginsettings/service.go b/pkg/services/pluginsettings/service.go index bb492edb043..c80d508831e 100644 --- a/pkg/services/pluginsettings/service.go +++ b/pkg/services/pluginsettings/service.go @@ -8,15 +8,14 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/encryption" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/sqlstore" - "github.com/grafana/grafana/pkg/setting" ) type Service struct { - Bus bus.Bus - SQLStore *sqlstore.SQLStore - EncryptionService encryption.Service + Bus bus.Bus + SQLStore *sqlstore.SQLStore + SecretsService secrets.Service logger log.Logger pluginSettingDecryptionCache secureJSONDecryptionCache @@ -32,12 +31,12 @@ type secureJSONDecryptionCache struct { sync.Mutex } -func ProvideService(bus bus.Bus, store *sqlstore.SQLStore, encryptionService encryption.Service) *Service { +func ProvideService(bus bus.Bus, store *sqlstore.SQLStore, secretsService secrets.Service) *Service { s := &Service{ - Bus: bus, - SQLStore: store, - EncryptionService: encryptionService, - logger: log.New("pluginsettings"), + Bus: bus, + SQLStore: store, + SecretsService: secretsService, + logger: log.New("pluginsettings"), pluginSettingDecryptionCache: secureJSONDecryptionCache{ cache: make(map[int64]cachedDecryptedJSON), }, @@ -56,7 +55,7 @@ func (s *Service) GetPluginSettingById(ctx context.Context, query *models.GetPlu func (s *Service) UpdatePluginSetting(ctx context.Context, cmd *models.UpdatePluginSettingCmd) error { var err error - cmd.EncryptedSecureJsonData, err = s.EncryptionService.EncryptJsonData(ctx, cmd.SecureJsonData, setting.SecretKey) + cmd.EncryptedSecureJsonData, err = s.SecretsService.EncryptJsonData(ctx, cmd.SecureJsonData, secrets.WithoutScope()) if err != nil { return err } @@ -76,7 +75,7 @@ func (s *Service) DecryptedValues(ps *models.PluginSetting) map[string]string { return item.json } - json, err := s.EncryptionService.DecryptJsonData(context.Background(), ps.SecureJsonData, setting.SecretKey) + json, err := s.SecretsService.DecryptJsonData(context.Background(), ps.SecureJsonData) if err != nil { s.logger.Error("Failed to decrypt secure json data", "error", err) return map[string]string{} diff --git a/pkg/services/pluginsettings/service_test.go b/pkg/services/pluginsettings/service_test.go index 594318e677f..bdd7bb2269c 100644 --- a/pkg/services/pluginsettings/service_test.go +++ b/pkg/services/pluginsettings/service_test.go @@ -7,8 +7,10 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" - "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/services/secrets" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" + "github.com/stretchr/testify/require" ) @@ -16,14 +18,14 @@ func TestService_DecryptedValuesCache(t *testing.T) { t.Run("When plugin settings hasn't been updated, encrypted JSON should be fetched from cache", func(t *testing.T) { ctx := context.Background() - encryptionService := ossencryption.ProvideService() - psService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + psService := ProvideService(bus.New(), nil, secretsService) - encryptedJsonData, err := encryptionService.EncryptJsonData( + encryptedJsonData, err := secretsService.EncryptJsonData( ctx, map[string]string{ "password": "password", - }, setting.SecretKey) + }, secrets.WithoutScope()) require.NoError(t, err) ps := models.PluginSetting{ @@ -37,11 +39,11 @@ func TestService_DecryptedValuesCache(t *testing.T) { require.Equal(t, "password", password) require.True(t, ok) - encryptedJsonData, err = encryptionService.EncryptJsonData( + encryptedJsonData, err = secretsService.EncryptJsonData( ctx, map[string]string{ "password": "", - }, setting.SecretKey) + }, secrets.WithoutScope()) require.NoError(t, err) ps.SecureJsonData = encryptedJsonData @@ -54,14 +56,14 @@ func TestService_DecryptedValuesCache(t *testing.T) { t.Run("When plugin settings is updated, encrypted JSON should not be fetched from cache", func(t *testing.T) { ctx := context.Background() - encryptionService := ossencryption.ProvideService() - psService := ProvideService(bus.New(), nil, encryptionService) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + psService := ProvideService(bus.New(), nil, secretsService) - encryptedJsonData, err := encryptionService.EncryptJsonData( + encryptedJsonData, err := secretsService.EncryptJsonData( ctx, map[string]string{ "password": "password", - }, setting.SecretKey) + }, secrets.WithoutScope()) require.NoError(t, err) ps := models.PluginSetting{ @@ -75,11 +77,11 @@ func TestService_DecryptedValuesCache(t *testing.T) { require.Equal(t, "password", password) require.True(t, ok) - encryptedJsonData, err = encryptionService.EncryptJsonData( + encryptedJsonData, err = secretsService.EncryptJsonData( ctx, map[string]string{ "password": "", - }, setting.SecretKey) + }, secrets.WithoutScope()) require.NoError(t, err) ps.SecureJsonData = encryptedJsonData diff --git a/pkg/services/secrets/manager/helpers.go b/pkg/services/secrets/manager/helpers.go index f1590782692..18acdb6b647 100644 --- a/pkg/services/secrets/manager/helpers.go +++ b/pkg/services/secrets/manager/helpers.go @@ -6,23 +6,14 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/services/encryption/ossencryption" "github.com/grafana/grafana/pkg/services/secrets" - "github.com/grafana/grafana/pkg/services/secrets/database" - "github.com/grafana/grafana/pkg/services/secrets/fakes" - "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/ini.v1" ) -func SetupTestService(tb testing.TB, db *sqlstore.SQLStore) *SecretsService { - if db == nil { - return setupTestService(tb, fakes.NewFakeSecretsStore()) - } - return setupTestService(tb, database.ProvideSecretsStore(db)) -} - -func setupTestService(tb testing.TB, store secrets.Store) *SecretsService { +func SetupTestService(tb testing.TB, store secrets.Store) *SecretsService { tb.Helper() defaultKey := "SdlklWklckeLS" if len(setting.SecretKey) > 0 { @@ -32,7 +23,11 @@ func setupTestService(tb testing.TB, store secrets.Store) *SecretsService { [security] secret_key = ` + defaultKey)) require.NoError(tb, err) - settings := &setting.OSSImpl{Cfg: &setting.Cfg{Raw: raw}} + cfg := &setting.Cfg{Raw: raw} + cfg.FeatureToggles = map[string]bool{envelopeEncryptionFeatureToggle: true} + + settings := &setting.OSSImpl{Cfg: cfg} + assert.True(tb, settings.IsFeatureToggleEnabled(envelopeEncryptionFeatureToggle)) return ProvideSecretsService( store, diff --git a/pkg/services/secrets/manager/manager.go b/pkg/services/secrets/manager/manager.go index 0353ea716c1..acb86d69421 100644 --- a/pkg/services/secrets/manager/manager.go +++ b/pkg/services/secrets/manager/manager.go @@ -16,7 +16,10 @@ import ( "github.com/grafana/grafana/pkg/setting" ) -const defaultProvider = "secretKey" +const ( + defaultProvider = "secretKey" + envelopeEncryptionFeatureToggle = "envelopeEncryption" +) type SecretsService struct { store secrets.Store @@ -24,7 +27,7 @@ type SecretsService struct { enc encryption.Service settings setting.Provider - defaultProvider string + currentProvider string providers map[string]secrets.Provider dataKeyCache map[string]dataKeyCacheItem } @@ -39,7 +42,7 @@ func ProvideSecretsService(store secrets.Store, bus bus.Bus, enc encryption.Serv bus: bus, enc: enc, settings: settings, - defaultProvider: defaultProvider, + currentProvider: defaultProvider, providers: providers, dataKeyCache: make(map[string]dataKeyCacheItem), } @@ -55,8 +58,14 @@ type dataKeyCacheItem struct { var b64 = base64.RawStdEncoding func (s *SecretsService) Encrypt(ctx context.Context, payload []byte, opt secrets.EncryptionOptions) ([]byte, error) { + // Use legacy encryption service if envelopeEncryptionFeatureToggle toggle is off + if !s.settings.IsFeatureToggleEnabled(envelopeEncryptionFeatureToggle) { + return s.enc.Encrypt(ctx, payload, setting.SecretKey) + } + + // If encryption envelopeEncryptionFeatureToggle toggle is on, use envelope encryption scope := opt() - keyName := fmt.Sprintf("%s/%s@%s", time.Now().Format("2006-01-02"), scope, s.defaultProvider) + keyName := fmt.Sprintf("%s/%s@%s", time.Now().Format("2006-01-02"), scope, s.currentProvider) dataKey, err := s.dataKey(ctx, keyName) if err != nil { @@ -88,6 +97,12 @@ func (s *SecretsService) Encrypt(ctx context.Context, payload []byte, opt secret } func (s *SecretsService) Decrypt(ctx context.Context, payload []byte) ([]byte, error) { + // Use legacy encryption service if envelopeEncryptionFeatureToggle toggle is off + if !s.settings.IsFeatureToggleEnabled(envelopeEncryptionFeatureToggle) { + return s.enc.Decrypt(ctx, payload, setting.SecretKey) + } + + // If encryption envelopeEncryptionFeatureToggle toggle is on, use envelope encryption if len(payload) == 0 { return nil, fmt.Errorf("unable to decrypt empty payload") } @@ -175,9 +190,9 @@ func (s *SecretsService) newDataKey(ctx context.Context, name string, scope stri if err != nil { return nil, err } - provider, exists := s.providers[s.defaultProvider] + provider, exists := s.providers[s.currentProvider] if !exists { - return nil, fmt.Errorf("could not find encryption provider '%s'", s.defaultProvider) + return nil, fmt.Errorf("could not find encryption provider '%s'", s.currentProvider) } // 2. Encrypt it @@ -190,7 +205,7 @@ func (s *SecretsService) newDataKey(ctx context.Context, name string, scope stri err = s.store.CreateDataKey(ctx, secrets.DataKey{ Active: true, // TODO: right now we never mark a key as deactivated Name: name, - Provider: s.defaultProvider, + Provider: s.currentProvider, EncryptedData: encrypted, Scope: scope, }) diff --git a/pkg/services/secrets/manager/manager_test.go b/pkg/services/secrets/manager/manager_test.go index 3e78ac4f166..b3bca2b7ede 100644 --- a/pkg/services/secrets/manager/manager_test.go +++ b/pkg/services/secrets/manager/manager_test.go @@ -4,18 +4,17 @@ import ( "context" "testing" + "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/secrets/database" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/services/secrets" ) func TestSecrets_EnvelopeEncryption(t *testing.T) { store := database.ProvideSecretsStore(sqlstore.InitTestDB(t)) - svc := setupTestService(t, store) + svc := SetupTestService(t, store) ctx := context.Background() t.Run("encrypting with no entity_id should create DEK", func(t *testing.T) { diff --git a/pkg/services/secrets/secrets.go b/pkg/services/secrets/secrets.go index faa0916d3fa..08090897ae8 100644 --- a/pkg/services/secrets/secrets.go +++ b/pkg/services/secrets/secrets.go @@ -6,6 +6,8 @@ import ( "xorm.io/xorm" ) +// Service is an envelope encryption service in charge of encrypting/decrypting secrets. +// It is a replacement for encryption.Service type Service interface { Encrypt(ctx context.Context, payload []byte, opt EncryptionOptions) ([]byte, error) Decrypt(ctx context.Context, payload []byte) ([]byte, error) @@ -14,6 +16,7 @@ type Service interface { GetDecryptedValue(ctx context.Context, sjd map[string][]byte, key, fallback string) string } +// Store defines methods to interact with secrets storage type Store interface { GetDataKey(ctx context.Context, name string) (*DataKey, error) GetAllDataKeys(ctx context.Context) ([]*DataKey, error) @@ -22,6 +25,7 @@ type Store interface { DeleteDataKey(ctx context.Context, name string) error } +// Provider is a key encryption key provider for envelope encryption type Provider interface { Encrypt(ctx context.Context, blob []byte) ([]byte, error) Decrypt(ctx context.Context, blob []byte) ([]byte, error) diff --git a/pkg/services/sqlstore/dashboard_snapshot_test.go b/pkg/services/sqlstore/dashboard_snapshot_test.go index 8abea341740..059954c625d 100644 --- a/pkg/services/sqlstore/dashboard_snapshot_test.go +++ b/pkg/services/sqlstore/dashboard_snapshot_test.go @@ -10,7 +10,8 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/services/secrets" + "github.com/grafana/grafana/pkg/services/secrets/fakes" "github.com/grafana/grafana/pkg/setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -24,14 +25,14 @@ func TestDashboardSnapshotDBAccess(t *testing.T) { t.Cleanup(func() { setting.SecretKey = origSecret }) - + secretsService := fakes.NewFakeSecretsService() dashboard := simplejson.NewFromAny(map[string]interface{}{"hello": "mupp"}) t.Run("Given saved snapshot", func(t *testing.T) { rawDashboard, err := dashboard.Encode() require.NoError(t, err) - encryptedDashboard, err := ossencryption.ProvideService().Encrypt(context.Background(), rawDashboard, setting.SecretKey) + encryptedDashboard, err := secretsService.Encrypt(context.Background(), rawDashboard, secrets.WithoutScope()) require.NoError(t, err) cmd := models.CreateDashboardSnapshotCommand{ @@ -51,10 +52,9 @@ func TestDashboardSnapshotDBAccess(t *testing.T) { assert.NotNil(t, query.Result) - decryptedDashboard, err := ossencryption.ProvideService().Decrypt( + decryptedDashboard, err := secretsService.Decrypt( context.Background(), query.Result.DashboardEncrypted, - setting.SecretKey, ) require.NoError(t, err) @@ -133,10 +133,9 @@ func TestDashboardSnapshotDBAccess(t *testing.T) { }) t.Run("Should have encrypted dashboard data", func(t *testing.T) { - decryptedDashboard, err := ossencryption.ProvideService().Decrypt( + decryptedDashboard, err := secretsService.Decrypt( context.Background(), cmd.Result.DashboardEncrypted, - setting.SecretKey, ) require.NoError(t, err) diff --git a/pkg/services/sqlstore/migrations/ualert/ualert.go b/pkg/services/sqlstore/migrations/ualert/ualert.go index ad549d2abd4..7941d3d3210 100644 --- a/pkg/services/sqlstore/migrations/ualert/ualert.go +++ b/pkg/services/sqlstore/migrations/ualert/ualert.go @@ -10,6 +10,8 @@ import ( "strconv" "strings" + "github.com/grafana/grafana/pkg/setting" + ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/notifier/channels" "github.com/grafana/grafana/pkg/services/sqlstore/migrator" @@ -460,9 +462,9 @@ func (m *migration) validateAlertmanagerConfig(orgID int64, config *PostableUser // decryptFunc represents the legacy way of decrypting data. Before the migration, we don't need any new way, // given that the previous alerting will never support it. - decryptFunc := func(_ context.Context, sjd map[string][]byte, key string, fallback string, secret string) string { + decryptFunc := func(_ context.Context, sjd map[string][]byte, key string, fallback string) string { if value, ok := sjd[key]; ok { - decryptedData, err := util.Decrypt(value, secret) + decryptedData, err := util.Decrypt(value, setting.SecretKey) if err != nil { m.mg.Logger.Warn("unable to decrypt key '%s' for %s receiver with uid %s, returning fallback.", key, gr.Type, gr.UID) return fallback diff --git a/pkg/tsdb/service_test.go b/pkg/tsdb/service_test.go index 6bb5ed441db..7eca3e81e75 100644 --- a/pkg/tsdb/service_test.go +++ b/pkg/tsdb/service_test.go @@ -10,15 +10,17 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/datasources" - "github.com/grafana/grafana/pkg/services/encryption/ossencryption" + "github.com/grafana/grafana/pkg/services/secrets/fakes" + secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager" "github.com/grafana/grafana/pkg/setting" + "github.com/stretchr/testify/require" "golang.org/x/oauth2" ) func TestHandleRequest(t *testing.T) { t.Run("Should invoke plugin manager QueryData when handling request for query", func(t *testing.T) { - svc, _, pm := createService() + svc, _, pm := createService(t) backendPluginManagerCalled := false pm.QueryDataHandlerFunc = func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { backendPluginManagerCalled = true @@ -99,9 +101,10 @@ func (s *fakeOAuthTokenService) IsOAuthPassThruEnabled(*models.DataSource) bool return false } -func createService() (*Service, *fakeExecutor, *fakePluginsClient) { +func createService(t *testing.T) (*Service, *fakeExecutor, *fakePluginsClient) { fakePluginsClient := &fakePluginsClient{} - dsService := datasources.ProvideService(bus.New(), nil, ossencryption.ProvideService()) + secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + dsService := datasources.ProvideService(bus.New(), nil, secretsService) s := newService( setting.NewCfg(), diff --git a/pkg/util/encryption.go b/pkg/util/encryption.go index 8c5561d2c95..9c8c3a4c440 100644 --- a/pkg/util/encryption.go +++ b/pkg/util/encryption.go @@ -22,8 +22,8 @@ const ( ) // Decrypt decrypts a payload with a given secret. -// Deprecated. Do not use it. -// Use encryption.Service instead. +// DEPRECATED. Do not use it. +// Use secrets.Service instead. func Decrypt(payload []byte, secret string) ([]byte, error) { alg, payload, err := deriveEncryptionAlgorithm(payload) if err != nil { @@ -110,8 +110,8 @@ func decryptCFB(block cipher.Block, payload []byte) ([]byte, error) { } // Encrypt encrypts a payload with a given secret. -// Deprecated. Do not use it. -// Use encryption.Service instead. +// DEPRECATED. Do not use it. +// Use secrets.Service instead. func Encrypt(payload []byte, secret string) ([]byte, error) { salt, err := GetRandomString(saltLength) if err != nil {