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