From 532e71554f15ccf84139204a95f8978682297b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20L=C3=B3pez=20de=20la=20Franca=20Beltran?= <5459617+joanlopez@users.noreply.github.com> Date: Fri, 7 Jan 2022 13:52:28 +0100 Subject: [PATCH] Usage Stats: Add metrics to count enabled kms providers per kind (#43640) * Usage Insights: Add metrics to count enabled kms providers per kind * Add backwards compatibility --- conf/defaults.ini | 2 +- conf/sample.ini | 6 +- pkg/services/kmsproviders/kmsproviders.go | 12 ++- .../osskmsproviders/osskmsproviders.go | 4 +- pkg/services/secrets/manager/manager.go | 87 +++++++++++++------ pkg/services/secrets/manager/manager_test.go | 10 ++- pkg/services/secrets/secrets.go | 15 ++++ pkg/services/secrets/types.go | 2 +- 8 files changed, 98 insertions(+), 40 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 43196b6a68d..207401eda1a 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -235,7 +235,7 @@ admin_password = admin secret_key = SW2YcwTIb9zpOOhoPsMm # current key provider used for envelope encryption, default to static value specified by secret_key -encryption_provider = secretKey +encryption_provider = secretKey.v1 # list of configured key providers, space separated (Enterprise only): e.g., awskms.v1 azurekv.v1 available_encryption_providers = diff --git a/conf/sample.ini b/conf/sample.ini index 0a98f786626..87ded5d82e2 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -235,7 +235,7 @@ ;secret_key = SW2YcwTIb9zpOOhoPsMm # current key provider used for envelope encryption, default to static value specified by secret_key -;encryption_provider = secretKey +;encryption_provider = secretKey.v1 # list of configured key providers, space separated (Enterprise only): e.g., awskms.v1 azurekv.v1 ;available_encryption_providers = @@ -1055,8 +1055,8 @@ ;license_path = [feature_toggles] -# there are currently two ways to enable feature toggles in the `grafana.ini`. -# you can either pass an array of feature you want to enable to the `enable` field or +# there are currently two ways to enable feature toggles in the `grafana.ini`. +# you can either pass an array of feature you want to enable to the `enable` field or # configure each toggle by setting the name of the toggle to true/false. Toggles set to true/false # will take presidence over toggles in the `enable` list. diff --git a/pkg/services/kmsproviders/kmsproviders.go b/pkg/services/kmsproviders/kmsproviders.go index 44e460eb761..6de895f5847 100644 --- a/pkg/services/kmsproviders/kmsproviders.go +++ b/pkg/services/kmsproviders/kmsproviders.go @@ -3,9 +3,17 @@ package kmsproviders import "github.com/grafana/grafana/pkg/services/secrets" const ( - Default = "secretKey" + // Legacy is used for historical reasons (keeping backwards). + // In older versions, the default value was a non-valid identifier, + // so it was updated to a valid one. See Default. + Legacy = "secretKey" + + // Default is the identifier of the default kms provider + // which fallbacks to Grafana's secret key. See the + // defaultprovider package for further information. + Default = "secretKey.v1" ) type Service interface { - Provide() (map[string]secrets.Provider, error) + Provide() (map[secrets.ProviderID]secrets.Provider, error) } diff --git a/pkg/services/kmsproviders/osskmsproviders/osskmsproviders.go b/pkg/services/kmsproviders/osskmsproviders/osskmsproviders.go index 6c547a62864..2e01653f3fa 100644 --- a/pkg/services/kmsproviders/osskmsproviders/osskmsproviders.go +++ b/pkg/services/kmsproviders/osskmsproviders/osskmsproviders.go @@ -20,12 +20,12 @@ func ProvideService(enc encryption.Internal, settings setting.Provider) Service } } -func (s Service) Provide() (map[string]secrets.Provider, error) { +func (s Service) Provide() (map[secrets.ProviderID]secrets.Provider, error) { if !s.settings.IsFeatureToggleEnabled(secrets.EnvelopeEncryptionFeatureToggle) { return nil, nil } - return map[string]secrets.Provider{ + return map[secrets.ProviderID]secrets.Provider{ kmsproviders.Default: grafana.New(s.settings, s.enc), }, nil } diff --git a/pkg/services/secrets/manager/manager.go b/pkg/services/secrets/manager/manager.go index 3263dcccfcf..637f5349e99 100644 --- a/pkg/services/secrets/manager/manager.go +++ b/pkg/services/secrets/manager/manager.go @@ -24,10 +24,10 @@ type SecretsService struct { settings setting.Provider usageStats usagestats.Service - currentProvider string - providers map[string]secrets.Provider - dataKeyCache map[string]dataKeyCacheItem - log log.Logger + currentProviderID secrets.ProviderID + providers map[secrets.ProviderID]secrets.Provider + dataKeyCache map[string]dataKeyCacheItem + log log.Logger } func ProvideSecretsService( @@ -44,27 +44,27 @@ func ProvideSecretsService( logger := log.New("secrets") enabled := settings.IsFeatureToggleEnabled(secrets.EnvelopeEncryptionFeatureToggle) - currentProvider := settings.KeyValue("security", "encryption_provider").MustString(kmsproviders.Default) + currentProviderID := readCurrentProviderID(settings) - if _, ok := providers[currentProvider]; enabled && !ok { - return nil, fmt.Errorf("missing configuration for current encryption provider %s", currentProvider) + if _, ok := providers[currentProviderID]; enabled && !ok { + return nil, fmt.Errorf("missing configuration for current encryption provider %s", currentProviderID) } - if !enabled && currentProvider != kmsproviders.Default { + if !enabled && currentProviderID != kmsproviders.Default { logger.Warn("Changing encryption provider requires enabling envelope encryption feature") } - logger.Debug("Envelope encryption state", "enabled", enabled, "current provider", currentProvider) + logger.Debug("Envelope encryption state", "enabled", enabled, "current provider", currentProviderID) s := &SecretsService{ - store: store, - enc: enc, - settings: settings, - usageStats: usageStats, - providers: providers, - currentProvider: currentProvider, - dataKeyCache: make(map[string]dataKeyCacheItem), - log: logger, + store: store, + enc: enc, + settings: settings, + usageStats: usageStats, + providers: providers, + currentProviderID: currentProviderID, + dataKeyCache: make(map[string]dataKeyCacheItem), + log: logger, } s.registerUsageMetrics() @@ -72,15 +72,48 @@ func ProvideSecretsService( return s, nil } +func readCurrentProviderID(settings setting.Provider) secrets.ProviderID { + currentProvider := settings.KeyValue("security", "encryption_provider").MustString(kmsproviders.Default) + if currentProvider == kmsproviders.Legacy { + currentProvider = kmsproviders.Default + } + + return secrets.ProviderID(currentProvider) +} + func (s *SecretsService) registerUsageMetrics() { s.usageStats.RegisterMetricsFunc(func(context.Context) (map[string]interface{}, error) { - enabled := 0 + usageMetrics := make(map[string]interface{}) + + // Enabled / disabled + usageMetrics["stats.encryption.envelope_encryption_enabled.count"] = 0 if s.settings.IsFeatureToggleEnabled(secrets.EnvelopeEncryptionFeatureToggle) { - enabled = 1 + usageMetrics["stats.encryption.envelope_encryption_enabled.count"] = 1 } - return map[string]interface{}{ - "stats.encryption.envelope_encryption_enabled.count": enabled, - }, nil + + // Current provider + kind, err := s.currentProviderID.Kind() + if err != nil { + return nil, err + } + usageMetrics[fmt.Sprintf("stats.encryption.current_provider.%s.count", kind)] = 1 + + // Count by kind + countByKind := make(map[string]int) + for id := range s.providers { + kind, err := id.Kind() + if err != nil { + return nil, err + } + + countByKind[kind]++ + } + + for kind, count := range countByKind { + usageMetrics[fmt.Sprintf(`stats.encryption.providers.%s.count`, kind)] = count + } + + return usageMetrics, nil }) } @@ -135,7 +168,7 @@ func (s *SecretsService) EncryptWithDBSession(ctx context.Context, payload []byt } func (s *SecretsService) keyName(scope string) string { - return fmt.Sprintf("%s/%s@%s", now().Format("2006-01-02"), scope, s.currentProvider) + return fmt.Sprintf("%s/%s@%s", now().Format("2006-01-02"), scope, s.currentProviderID) } func (s *SecretsService) Decrypt(ctx context.Context, payload []byte) ([]byte, error) { @@ -237,9 +270,9 @@ func (s *SecretsService) newDataKey(ctx context.Context, name string, scope stri if err != nil { return nil, err } - provider, exists := s.providers[s.currentProvider] + provider, exists := s.providers[s.currentProviderID] if !exists { - return nil, fmt.Errorf("could not find encryption provider '%s'", s.currentProvider) + return nil, fmt.Errorf("could not find encryption provider '%s'", s.currentProviderID) } // 2. Encrypt it @@ -252,7 +285,7 @@ func (s *SecretsService) newDataKey(ctx context.Context, name string, scope stri dek := secrets.DataKey{ Active: true, // TODO: right now we never mark a key as deactivated Name: name, - Provider: s.currentProvider, + Provider: s.currentProviderID, EncryptedData: encrypted, Scope: scope, } @@ -310,7 +343,7 @@ func (s *SecretsService) dataKey(ctx context.Context, name string) ([]byte, erro return decrypted, nil } -func (s *SecretsService) GetProviders() map[string]secrets.Provider { +func (s *SecretsService) GetProviders() map[secrets.ProviderID]secrets.Provider { return s.providers } diff --git a/pkg/services/secrets/manager/manager_test.go b/pkg/services/secrets/manager/manager_test.go index 33dd401eb9b..df442b49d79 100644 --- a/pkg/services/secrets/manager/manager_test.go +++ b/pkg/services/secrets/manager/manager_test.go @@ -87,6 +87,8 @@ func TestSecretsService_EnvelopeEncryption(t *testing.T) { require.NoError(t, err) assert.Equal(t, 1, reports.Metrics["stats.encryption.envelope_encryption_enabled.count"]) + assert.Equal(t, 1, reports.Metrics["stats.encryption.current_provider.secretKey.count"]) + assert.Equal(t, 1, reports.Metrics["stats.encryption.providers.secretKey.count"]) }) } @@ -159,7 +161,7 @@ func TestSecretsService_DataKeys(t *testing.T) { func TestSecretsService_UseCurrentProvider(t *testing.T) { t.Run("When encryption_provider is not specified explicitly, should use 'secretKey' as a current provider", func(t *testing.T) { svc := SetupTestService(t, database.ProvideSecretsStore(sqlstore.InitTestDB(t))) - assert.Equal(t, "secretKey", svc.currentProvider) + assert.Equal(t, secrets.ProviderID("secretKey.v1"), svc.currentProviderID) }) t.Run("Should use encrypt/decrypt methods of the current encryption provider", func(t *testing.T) { @@ -175,7 +177,7 @@ func TestSecretsService_UseCurrentProvider(t *testing.T) { raw, err := ini.Load([]byte(rawCfg)) require.NoError(t, err) - providerID := "fakeProvider.v1" + providerID := secrets.ProviderID("fakeProvider.v1") settings := &setting.OSSImpl{ Cfg: &setting.Cfg{ Raw: raw, @@ -195,7 +197,7 @@ func TestSecretsService_UseCurrentProvider(t *testing.T) { ) require.NoError(t, err) - assert.Equal(t, providerID, svcEncrypt.currentProvider) + assert.Equal(t, providerID, svcEncrypt.currentProviderID) assert.Equal(t, 2, len(svcEncrypt.GetProviders())) encrypted, _ := svcEncrypt.Encrypt(context.Background(), []byte{}, secrets.WithoutScope()) @@ -244,7 +246,7 @@ func newFakeKMS(kms osskmsproviders.Service) fakeKMS { } } -func (f *fakeKMS) Provide() (map[string]secrets.Provider, error) { +func (f *fakeKMS) Provide() (map[secrets.ProviderID]secrets.Provider, error) { providers, err := f.kms.Provide() if err != nil { return providers, err diff --git a/pkg/services/secrets/secrets.go b/pkg/services/secrets/secrets.go index 02d2fd199e5..55af27a78ac 100644 --- a/pkg/services/secrets/secrets.go +++ b/pkg/services/secrets/secrets.go @@ -2,6 +2,8 @@ package secrets import ( "context" + "fmt" + "strings" "xorm.io/xorm" ) @@ -42,3 +44,16 @@ type Provider interface { Encrypt(ctx context.Context, blob []byte) ([]byte, error) Decrypt(ctx context.Context, blob []byte) ([]byte, error) } + +type ProviderID string + +func (id ProviderID) Kind() (string, error) { + idStr := string(id) + + parts := strings.SplitN(idStr, ".", 2) + if len(parts) != 2 { + return "", fmt.Errorf("malformatted provider identifier %s: expected format .", idStr) + } + + return parts[0], nil +} diff --git a/pkg/services/secrets/types.go b/pkg/services/secrets/types.go index 90e98cb7365..9fdb10641c7 100644 --- a/pkg/services/secrets/types.go +++ b/pkg/services/secrets/types.go @@ -11,7 +11,7 @@ type DataKey struct { Active bool Name string Scope string - Provider string + Provider ProviderID EncryptedData []byte Created time.Time Updated time.Time