mirror of
https://github.com/grafana/grafana.git
synced 2026-08-18 17:15:08 -05:00
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
This commit is contained in:
+1
-1
@@ -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 =
|
||||
|
||||
+1
-1
@@ -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 =
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ type SecretsService struct {
|
||||
settings setting.Provider
|
||||
usageStats usagestats.Service
|
||||
|
||||
currentProvider string
|
||||
providers map[string]secrets.Provider
|
||||
currentProviderID secrets.ProviderID
|
||||
providers map[secrets.ProviderID]secrets.Provider
|
||||
dataKeyCache map[string]dataKeyCacheItem
|
||||
log log.Logger
|
||||
}
|
||||
@@ -44,17 +44,17 @@ 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,
|
||||
@@ -62,7 +62,7 @@ func ProvideSecretsService(
|
||||
settings: settings,
|
||||
usageStats: usageStats,
|
||||
providers: providers,
|
||||
currentProvider: currentProvider,
|
||||
currentProviderID: currentProviderID,
|
||||
dataKeyCache: make(map[string]dataKeyCacheItem),
|
||||
log: logger,
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <provider>.<keyName>", idStr)
|
||||
}
|
||||
|
||||
return parts[0], nil
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user