SecretsManager: Add base encryption manager (#107562)
Co-authored-by: Michael Mandrus <michael.mandrus@grafana.com> Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com>
This commit is contained in:
co-authored by
Michael Mandrus
Matheus Macabu
parent
93c14c52da
commit
4d8678c7f2
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher"
|
||||
encryptionprovider "github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher/provider"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher/provider"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@@ -30,8 +30,9 @@ type Service struct {
|
||||
cfg *setting.Cfg
|
||||
usageMetrics usagestats.Service
|
||||
|
||||
ciphers map[string]cipher.Encrypter
|
||||
deciphers map[string]cipher.Decrypter
|
||||
cipher cipher.Encrypter
|
||||
decipher cipher.Decrypter
|
||||
algorithm string
|
||||
}
|
||||
|
||||
func NewEncryptionService(
|
||||
@@ -43,59 +44,29 @@ func NewEncryptionService(
|
||||
return nil, fmt.Errorf("`[secrets_manager]secret_key` is not set")
|
||||
}
|
||||
|
||||
if cfg.SecretsManagement.Encryption.Algorithm == "" {
|
||||
return nil, fmt.Errorf("`[secrets_manager.encryption]algorithm` is not set")
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
tracer: tracer,
|
||||
log: log.New("encryption"),
|
||||
|
||||
ciphers: encryptionprovider.ProvideCiphers(),
|
||||
deciphers: encryptionprovider.ProvideDeciphers(),
|
||||
// Use the AES-GCM cipher for encryption and decryption.
|
||||
// This is the only cipher supported by the secrets management system.
|
||||
cipher: provider.NewAesGcmCipher(),
|
||||
decipher: provider.NewAesGcmCipher(),
|
||||
algorithm: provider.AesGcm,
|
||||
|
||||
usageMetrics: usageMetrics,
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
algorithm := s.cfg.SecretsManagement.Encryption.Algorithm
|
||||
|
||||
if err := s.checkEncryptionAlgorithm(algorithm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.registerUsageMetrics()
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Service) checkEncryptionAlgorithm(algorithm string) error {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
s.log.Error("Wrong security encryption configuration", "algorithm", algorithm, "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if _, ok := s.ciphers[algorithm]; !ok {
|
||||
err = fmt.Errorf("no cipher registered for encryption algorithm '%s'", algorithm)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := s.deciphers[algorithm]; !ok {
|
||||
err = fmt.Errorf("no decipher registered for encryption algorithm '%s'", algorithm)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) registerUsageMetrics() {
|
||||
s.usageMetrics.RegisterMetricsFunc(func(context.Context) (map[string]any, error) {
|
||||
algorithm := s.cfg.SecretsManagement.Encryption.Algorithm
|
||||
|
||||
return map[string]any{
|
||||
fmt.Sprintf("stats.%s.encryption.cipher.%s.count", encryption.UsageInsightsPrefix, algorithm): 1,
|
||||
fmt.Sprintf("stats.%s.encryption.cipher.%s.count", encryption.UsageInsightsPrefix, s.algorithm): 1,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
@@ -120,16 +91,10 @@ func (s *Service) Decrypt(ctx context.Context, payload []byte, secret string) ([
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decipher, ok := s.deciphers[algorithm]
|
||||
if !ok {
|
||||
err = fmt.Errorf("no decipher available for algorithm '%s'", algorithm)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
span.SetAttributes(attribute.String("cipher.algorithm", algorithm))
|
||||
|
||||
var decrypted []byte
|
||||
decrypted, err = decipher.Decrypt(ctx, toDecrypt, secret)
|
||||
decrypted, err = s.decipher.Decrypt(ctx, toDecrypt, secret)
|
||||
|
||||
return decrypted, err
|
||||
}
|
||||
@@ -139,15 +104,8 @@ func (s *Service) deriveEncryptionAlgorithm(payload []byte) (string, []byte, err
|
||||
return "", nil, fmt.Errorf("unable to derive encryption algorithm")
|
||||
}
|
||||
|
||||
if payload[0] != encryptionAlgorithmDelimiter {
|
||||
return cipher.AesCfb, payload, nil // backwards compatibility
|
||||
}
|
||||
|
||||
payload = payload[1:]
|
||||
algorithmDelimiterIdx := bytes.Index(payload, []byte{encryptionAlgorithmDelimiter})
|
||||
if algorithmDelimiterIdx == -1 {
|
||||
return cipher.AesCfb, payload, nil // backwards compatibility
|
||||
}
|
||||
|
||||
algorithmB64 := payload[:algorithmDelimiterIdx]
|
||||
payload = payload[algorithmDelimiterIdx+1:]
|
||||
@@ -173,21 +131,13 @@ func (s *Service) Encrypt(ctx context.Context, payload []byte, secret string) ([
|
||||
}
|
||||
}()
|
||||
|
||||
algorithm := s.cfg.SecretsManagement.Encryption.Algorithm
|
||||
|
||||
cipher, ok := s.ciphers[algorithm]
|
||||
if !ok {
|
||||
err = fmt.Errorf("no cipher available for algorithm '%s'", algorithm)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
span.SetAttributes(attribute.String("cipher.algorithm", algorithm))
|
||||
span.SetAttributes(attribute.String("cipher.algorithm", s.algorithm))
|
||||
|
||||
var encrypted []byte
|
||||
encrypted, err = cipher.Encrypt(ctx, payload, secret)
|
||||
encrypted, err = s.cipher.Encrypt(ctx, payload, secret)
|
||||
|
||||
prefix := make([]byte, base64.RawStdEncoding.EncodedLen(len([]byte(algorithm)))+2)
|
||||
base64.RawStdEncoding.Encode(prefix[1:], []byte(algorithm))
|
||||
prefix := make([]byte, base64.RawStdEncoding.EncodedLen(len([]byte(s.algorithm)))+2)
|
||||
base64.RawStdEncoding.Encode(prefix[1:], []byte(s.algorithm))
|
||||
prefix[0] = encryptionAlgorithmDelimiter
|
||||
prefix[len(prefix)-1] = encryptionAlgorithmDelimiter
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@ package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@@ -21,11 +19,6 @@ func newGcmService(t *testing.T) *Service {
|
||||
SecretsManagement: setting.SecretsManagerSettings{
|
||||
SecretKey: "SdlklWklckeLS",
|
||||
EncryptionProvider: "secretKey.v1",
|
||||
Encryption: setting.EncryptionSettings{
|
||||
DataKeysCacheTTL: 5 * time.Minute,
|
||||
DataKeysCleanupInterval: 1 * time.Nanosecond,
|
||||
Algorithm: cipher.AesGcm,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -60,19 +53,4 @@ func TestService(t *testing.T) {
|
||||
assert.Equal(t, []byte("grafana"), decrypted)
|
||||
// We'll let the provider deal with testing details.
|
||||
})
|
||||
|
||||
t.Run("decrypting legacy ciphertext should work", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Raw slice of bytes that corresponds to the following ciphertext:
|
||||
// - 'grafana' as payload
|
||||
// - '1234' as secret
|
||||
// - no encryption algorithm metadata
|
||||
ciphertext := []byte{73, 71, 50, 57, 121, 110, 90, 109, 115, 23, 237, 13, 130, 188, 151, 118, 98, 103, 80, 209, 79, 143, 22, 122, 44, 40, 102, 41, 136, 16, 27}
|
||||
|
||||
svc := newGcmService(t)
|
||||
decrypted, err := svc.Decrypt(t.Context(), ciphertext, "1234")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("grafana"), decrypted)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user