1package manager
2
3import (
4	"bytes"
5	"context"
6	"crypto/rand"
7	"encoding/base64"
8	"errors"
9	"fmt"
10	"time"
11
12	"github.com/grafana/grafana/pkg/infra/log"
13	"github.com/grafana/grafana/pkg/infra/usagestats"
14	"github.com/grafana/grafana/pkg/services/encryption"
15	"github.com/grafana/grafana/pkg/services/kmsproviders"
16	"github.com/grafana/grafana/pkg/services/secrets"
17	"github.com/grafana/grafana/pkg/setting"
18	"xorm.io/xorm"
19)
20
21type SecretsService struct {
22	store      secrets.Store
23	enc        encryption.Internal
24	settings   setting.Provider
25	usageStats usagestats.Service
26
27	currentProvider string
28	providers       map[string]secrets.Provider
29	dataKeyCache    map[string]dataKeyCacheItem
30	log             log.Logger
31}
32
33func ProvideSecretsService(
34	store secrets.Store,
35	kmsProvidersService kmsproviders.Service,
36	enc encryption.Internal,
37	settings setting.Provider,
38	usageStats usagestats.Service,
39) (*SecretsService, error) {
40	providers, err := kmsProvidersService.Provide()
41	if err != nil {
42		return nil, err
43	}
44
45	logger := log.New("secrets")
46	enabled := settings.IsFeatureToggleEnabled(secrets.EnvelopeEncryptionFeatureToggle)
47	currentProvider := settings.KeyValue("security", "encryption_provider").MustString(kmsproviders.Default)
48
49	if _, ok := providers[currentProvider]; enabled && !ok {
50		return nil, fmt.Errorf("missing configuration for current encryption provider %s", currentProvider)
51	}
52
53	if !enabled && currentProvider != kmsproviders.Default {
54		logger.Warn("Changing encryption provider requires enabling envelope encryption feature")
55	}
56
57	logger.Debug("Envelope encryption state", "enabled", enabled, "current provider", currentProvider)
58
59	s := &SecretsService{
60		store:           store,
61		enc:             enc,
62		settings:        settings,
63		usageStats:      usageStats,
64		providers:       providers,
65		currentProvider: currentProvider,
66		dataKeyCache:    make(map[string]dataKeyCacheItem),
67		log:             logger,
68	}
69
70	s.registerUsageMetrics()
71
72	return s, nil
73}
74
75func (s *SecretsService) registerUsageMetrics() {
76	s.usageStats.RegisterMetricsFunc(func(context.Context) (map[string]interface{}, error) {
77		enabled := 0
78		if s.settings.IsFeatureToggleEnabled(secrets.EnvelopeEncryptionFeatureToggle) {
79			enabled = 1
80		}
81		return map[string]interface{}{
82			"stats.encryption.envelope_encryption_enabled.count": enabled,
83		}, nil
84	})
85}
86
87type dataKeyCacheItem struct {
88	expiry  time.Time
89	dataKey []byte
90}
91
92var b64 = base64.RawStdEncoding
93
94func (s *SecretsService) Encrypt(ctx context.Context, payload []byte, opt secrets.EncryptionOptions) ([]byte, error) {
95	return s.EncryptWithDBSession(ctx, payload, opt, nil)
96}
97
98func (s *SecretsService) EncryptWithDBSession(ctx context.Context, payload []byte, opt secrets.EncryptionOptions, sess *xorm.Session) ([]byte, error) {
99	// Use legacy encryption service if envelopeEncryptionFeatureToggle toggle is off
100	if !s.settings.IsFeatureToggleEnabled(secrets.EnvelopeEncryptionFeatureToggle) {
101		return s.enc.Encrypt(ctx, payload, setting.SecretKey)
102	}
103
104	// If encryption secrets.EnvelopeEncryptionFeatureToggle toggle is on, use envelope encryption
105	scope := opt()
106	keyName := s.keyName(scope)
107
108	dataKey, err := s.dataKey(ctx, keyName)
109	if err != nil {
110		if errors.Is(err, secrets.ErrDataKeyNotFound) {
111			dataKey, err = s.newDataKey(ctx, keyName, scope, sess)
112			if err != nil {
113				return nil, err
114			}
115		} else {
116			return nil, err
117		}
118	}
119
120	encrypted, err := s.enc.Encrypt(ctx, payload, string(dataKey))
121	if err != nil {
122		return nil, err
123	}
124
125	prefix := make([]byte, b64.EncodedLen(len(keyName))+2)
126	b64.Encode(prefix[1:], []byte(keyName))
127	prefix[0] = '#'
128	prefix[len(prefix)-1] = '#'
129
130	blob := make([]byte, len(prefix)+len(encrypted))
131	copy(blob, prefix)
132	copy(blob[len(prefix):], encrypted)
133
134	return blob, nil
135}
136
137func (s *SecretsService) keyName(scope string) string {
138	return fmt.Sprintf("%s/%s@%s", now().Format("2006-01-02"), scope, s.currentProvider)
139}
140
141func (s *SecretsService) Decrypt(ctx context.Context, payload []byte) ([]byte, error) {
142	// Use legacy encryption service if secrets.EnvelopeEncryptionFeatureToggle toggle is off
143	if !s.settings.IsFeatureToggleEnabled(secrets.EnvelopeEncryptionFeatureToggle) {
144		return s.enc.Decrypt(ctx, payload, setting.SecretKey)
145	}
146
147	// If encryption secrets.EnvelopeEncryptionFeatureToggle toggle is on, use envelope encryption
148	if len(payload) == 0 {
149		return nil, fmt.Errorf("unable to decrypt empty payload")
150	}
151
152	var dataKey []byte
153
154	if payload[0] != '#' {
155		secretKey := s.settings.KeyValue("security", "secret_key").Value()
156		dataKey = []byte(secretKey)
157	} else {
158		payload = payload[1:]
159		endOfKey := bytes.Index(payload, []byte{'#'})
160		if endOfKey == -1 {
161			return nil, fmt.Errorf("could not find valid key in encrypted payload")
162		}
163		b64Key := payload[:endOfKey]
164		payload = payload[endOfKey+1:]
165		key := make([]byte, b64.DecodedLen(len(b64Key)))
166		_, err := b64.Decode(key, b64Key)
167		if err != nil {
168			return nil, err
169		}
170
171		dataKey, err = s.dataKey(ctx, string(key))
172		if err != nil {
173			s.log.Error("Failed to lookup data key", "name", string(key), "error", err)
174			return nil, err
175		}
176	}
177
178	return s.enc.Decrypt(ctx, payload, string(dataKey))
179}
180
181func (s *SecretsService) EncryptJsonData(ctx context.Context, kv map[string]string, opt secrets.EncryptionOptions) (map[string][]byte, error) {
182	return s.EncryptJsonDataWithDBSession(ctx, kv, opt, nil)
183}
184
185func (s *SecretsService) EncryptJsonDataWithDBSession(ctx context.Context, kv map[string]string, opt secrets.EncryptionOptions, sess *xorm.Session) (map[string][]byte, error) {
186	encrypted := make(map[string][]byte)
187	for key, value := range kv {
188		encryptedData, err := s.EncryptWithDBSession(ctx, []byte(value), opt, sess)
189		if err != nil {
190			return nil, err
191		}
192
193		encrypted[key] = encryptedData
194	}
195	return encrypted, nil
196}
197
198func (s *SecretsService) DecryptJsonData(ctx context.Context, sjd map[string][]byte) (map[string]string, error) {
199	decrypted := make(map[string]string)
200	for key, data := range sjd {
201		decryptedData, err := s.Decrypt(ctx, data)
202		if err != nil {
203			return nil, err
204		}
205
206		decrypted[key] = string(decryptedData)
207	}
208	return decrypted, nil
209}
210
211func (s *SecretsService) GetDecryptedValue(ctx context.Context, sjd map[string][]byte, key, fallback string) string {
212	if value, ok := sjd[key]; ok {
213		decryptedData, err := s.Decrypt(ctx, value)
214		if err != nil {
215			return fallback
216		}
217
218		return string(decryptedData)
219	}
220
221	return fallback
222}
223
224func newRandomDataKey() ([]byte, error) {
225	rawDataKey := make([]byte, 16)
226	_, err := rand.Read(rawDataKey)
227	if err != nil {
228		return nil, err
229	}
230	return rawDataKey, nil
231}
232
233// newDataKey creates a new random DEK, caches it and returns its value
234func (s *SecretsService) newDataKey(ctx context.Context, name string, scope string, sess *xorm.Session) ([]byte, error) {
235	// 1. Create new DEK
236	dataKey, err := newRandomDataKey()
237	if err != nil {
238		return nil, err
239	}
240	provider, exists := s.providers[s.currentProvider]
241	if !exists {
242		return nil, fmt.Errorf("could not find encryption provider '%s'", s.currentProvider)
243	}
244
245	// 2. Encrypt it
246	encrypted, err := provider.Encrypt(ctx, dataKey)
247	if err != nil {
248		return nil, err
249	}
250
251	// 3. Store its encrypted value in db
252	dek := secrets.DataKey{
253		Active:        true, // TODO: right now we never mark a key as deactivated
254		Name:          name,
255		Provider:      s.currentProvider,
256		EncryptedData: encrypted,
257		Scope:         scope,
258	}
259
260	if sess == nil {
261		err = s.store.CreateDataKey(ctx, dek)
262	} else {
263		err = s.store.CreateDataKeyWithDBSession(ctx, dek, sess)
264	}
265
266	if err != nil {
267		return nil, err
268	}
269
270	// 4. Cache its unencrypted value and return it
271	s.dataKeyCache[name] = dataKeyCacheItem{
272		expiry:  now().Add(dekTTL),
273		dataKey: dataKey,
274	}
275
276	return dataKey, nil
277}
278
279// dataKey looks up DEK in cache or database, and decrypts it
280func (s *SecretsService) dataKey(ctx context.Context, name string) ([]byte, error) {
281	if item, exists := s.dataKeyCache[name]; exists {
282		item.expiry = now().Add(dekTTL)
283		s.dataKeyCache[name] = item
284		return item.dataKey, nil
285	}
286
287	// 1. get encrypted data key from database
288	dataKey, err := s.store.GetDataKey(ctx, name)
289	if err != nil {
290		return nil, err
291	}
292
293	// 2. decrypt data key
294	provider, exists := s.providers[dataKey.Provider]
295	if !exists {
296		return nil, fmt.Errorf("could not find encryption provider '%s'", dataKey.Provider)
297	}
298
299	decrypted, err := provider.Decrypt(ctx, dataKey.EncryptedData)
300	if err != nil {
301		return nil, err
302	}
303
304	// 3. cache data key
305	s.dataKeyCache[name] = dataKeyCacheItem{
306		expiry:  now().Add(dekTTL),
307		dataKey: decrypted,
308	}
309
310	return decrypted, nil
311}
312
313func (s *SecretsService) GetProviders() map[string]secrets.Provider {
314	return s.providers
315}
316
317// These variables are used to test the code
318// responsible for periodically cleaning up
319// data encryption keys cache.
320var (
321	now        = time.Now
322	dekTTL     = 15 * time.Minute
323	gcInterval = time.Minute
324)
325
326func (s *SecretsService) Run(ctx context.Context) error {
327	gc := time.NewTicker(gcInterval)
328
329	for {
330		select {
331		case <-gc.C:
332			s.log.Debug("removing expired data encryption keys from cache...")
333			s.removeExpiredItems()
334			s.log.Debug("done removing expired data encryption keys from cache")
335		case <-ctx.Done():
336			s.log.Debug("grafana is shutting down; stopping...")
337			gc.Stop()
338			return nil
339		}
340	}
341}
342
343func (s *SecretsService) removeExpiredItems() {
344	for id, dek := range s.dataKeyCache {
345		if dek.expiry.Before(now()) {
346			delete(s.dataKeyCache, id)
347		}
348	}
349}
350