Encryption: Add support for multiple data keys per day (#47765)
* Add database migrations * Use short uids as data key ids * Add support for manual data key rotation * Fix duplicated mutex unlocks * Fix migration * Manage current data keys per name * Adjust key re-encryption and test * Modify rename column migration for MySQL compatibility * Refactor secrets manager and data keys cache * Multiple o11y adjustments * Fix stats query * Apply suggestions from code review Co-authored-by: Tania <yalyna.ts@gmail.com> * Fix linter * Docs: Rotate data encryption keys API endpoint Co-authored-by: Tania <yalyna.ts@gmail.com>
This commit is contained in:
committed by
GitHub
parent
ae8c11bfa4
commit
e43879e55d
@@ -26,7 +26,30 @@ func ProvideSecretsStore(sqlStore *sqlstore.SQLStore) *SecretsStoreImpl {
|
||||
}
|
||||
}
|
||||
|
||||
func (ss *SecretsStoreImpl) GetDataKey(ctx context.Context, name string) (*secrets.DataKey, error) {
|
||||
func (ss *SecretsStoreImpl) GetDataKey(ctx context.Context, id string) (*secrets.DataKey, error) {
|
||||
dataKey := &secrets.DataKey{}
|
||||
var exists bool
|
||||
|
||||
err := ss.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
var err error
|
||||
exists, err = sess.Table(dataKeysTable).
|
||||
Where("id = ?", id).
|
||||
Get(dataKey)
|
||||
return err
|
||||
})
|
||||
|
||||
if !exists {
|
||||
return nil, secrets.ErrDataKeyNotFound
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed getting data key: %w", err)
|
||||
}
|
||||
|
||||
return dataKey, nil
|
||||
}
|
||||
|
||||
func (ss *SecretsStoreImpl) GetCurrentDataKey(ctx context.Context, name string) (*secrets.DataKey, error) {
|
||||
dataKey := &secrets.DataKey{}
|
||||
var exists bool
|
||||
|
||||
@@ -43,7 +66,6 @@ func (ss *SecretsStoreImpl) GetDataKey(ctx context.Context, name string) (*secre
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ss.log.Error("Failed to get data key", "err", err, "name", name)
|
||||
return nil, fmt.Errorf("failed getting data key: %w", err)
|
||||
}
|
||||
|
||||
@@ -59,13 +81,13 @@ func (ss *SecretsStoreImpl) GetAllDataKeys(ctx context.Context) ([]*secrets.Data
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (ss *SecretsStoreImpl) CreateDataKey(ctx context.Context, dataKey secrets.DataKey) error {
|
||||
func (ss *SecretsStoreImpl) CreateDataKey(ctx context.Context, dataKey *secrets.DataKey) error {
|
||||
return ss.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
return ss.CreateDataKeyWithDBSession(ctx, dataKey, sess.Session)
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SecretsStoreImpl) CreateDataKeyWithDBSession(_ context.Context, dataKey secrets.DataKey, sess *xorm.Session) error {
|
||||
func (ss *SecretsStoreImpl) CreateDataKeyWithDBSession(_ context.Context, dataKey *secrets.DataKey, sess *xorm.Session) error {
|
||||
if !dataKey.Active {
|
||||
return fmt.Errorf("cannot insert deactivated data keys")
|
||||
}
|
||||
@@ -73,17 +95,26 @@ func (ss *SecretsStoreImpl) CreateDataKeyWithDBSession(_ context.Context, dataKe
|
||||
dataKey.Created = time.Now()
|
||||
dataKey.Updated = dataKey.Created
|
||||
|
||||
_, err := sess.Table(dataKeysTable).Insert(&dataKey)
|
||||
_, err := sess.Table(dataKeysTable).Insert(dataKey)
|
||||
return err
|
||||
}
|
||||
|
||||
func (ss *SecretsStoreImpl) DeleteDataKey(ctx context.Context, name string) error {
|
||||
if len(name) == 0 {
|
||||
return fmt.Errorf("data key name is missing")
|
||||
func (ss *SecretsStoreImpl) DisableDataKeys(ctx context.Context) error {
|
||||
return ss.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
_, err := sess.Table(dataKeysTable).
|
||||
Where("active = ?", ss.sqlStore.Dialect.BooleanStr(true)).
|
||||
UseBool("active").Update(&secrets.DataKey{Active: false})
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SecretsStoreImpl) DeleteDataKey(ctx context.Context, id string) error {
|
||||
if len(id) == 0 {
|
||||
return fmt.Errorf("data key id is missing")
|
||||
}
|
||||
|
||||
return ss.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
_, err := sess.Table(dataKeysTable).Delete(&secrets.DataKey{Name: name})
|
||||
_, err := sess.Table(dataKeysTable).Delete(&secrets.DataKey{Id: id})
|
||||
|
||||
return err
|
||||
})
|
||||
@@ -94,60 +125,71 @@ func (ss *SecretsStoreImpl) ReEncryptDataKeys(
|
||||
providers map[secrets.ProviderID]secrets.Provider,
|
||||
currProvider secrets.ProviderID,
|
||||
) error {
|
||||
return ss.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
keys := make([]*secrets.DataKey, 0)
|
||||
if err := sess.Table(dataKeysTable).Find(&keys); err != nil {
|
||||
return err
|
||||
}
|
||||
keys := make([]*secrets.DataKey, 0)
|
||||
if err := ss.sqlStore.NewSession(ctx).Table(dataKeysTable).Find(&keys); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
for _, k := range keys {
|
||||
err := ss.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
provider, ok := providers[kmsproviders.NormalizeProviderID(k.Provider)]
|
||||
if !ok {
|
||||
ss.log.Warn(
|
||||
"Could not find provider to re-encrypt data encryption key",
|
||||
"key_id", k.Name,
|
||||
"id", k.Id,
|
||||
"name", k.Name,
|
||||
"provider", k.Provider,
|
||||
)
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
|
||||
decrypted, err := provider.Decrypt(ctx, k.EncryptedData)
|
||||
if err != nil {
|
||||
ss.log.Warn(
|
||||
"Error while decrypting data encryption key to re-encrypt it",
|
||||
"key_id", k.Name,
|
||||
"id", k.Id,
|
||||
"name", k.Name,
|
||||
"provider", k.Provider,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
|
||||
// Updating current data key by re-encrypting it with current provider.
|
||||
// Accessing the current provider within providers map should be safe.
|
||||
k.Provider = currProvider
|
||||
k.Name = secrets.KeyName(k.Scope, currProvider)
|
||||
k.Updated = time.Now()
|
||||
k.EncryptedData, err = providers[currProvider].Encrypt(ctx, decrypted)
|
||||
if err != nil {
|
||||
ss.log.Warn(
|
||||
"Error while re-encrypting data encryption key",
|
||||
"key_id", k.Name,
|
||||
"id", k.Id,
|
||||
"name", k.Name,
|
||||
"provider", k.Provider,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := sess.Table(dataKeysTable).Where("name = ?", k.Name).Update(k); err != nil {
|
||||
if _, err := sess.Table(dataKeysTable).Where("id = ?", k.Id).Update(k); err != nil {
|
||||
ss.log.Warn(
|
||||
"Error while re-encrypting data encryption key",
|
||||
"key_id", k.Name,
|
||||
"id", k.Id,
|
||||
"name", k.Name,
|
||||
"provider", k.Provider,
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user