The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/secrets/database/database.go

195 lines
4.9 KiB

package database
import (
"context"
"fmt"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/kmsproviders"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/sqlstore"
"xorm.io/xorm"
)
const dataKeysTable = "data_keys"
type SecretsStoreImpl struct {
sqlStore *sqlstore.SQLStore
log log.Logger
}
func ProvideSecretsStore(sqlStore *sqlstore.SQLStore) *SecretsStoreImpl {
return &SecretsStoreImpl{
sqlStore: sqlStore,
log: log.New("secrets.store"),
}
}
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("name = ?", 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, label 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("label = ? AND active = ?", label, ss.sqlStore.Dialect.BooleanStr(true)).
Get(dataKey)
return err
})
if !exists {
return nil, secrets.ErrDataKeyNotFound
}
if err != nil {
return nil, fmt.Errorf("failed getting current data key: %w", err)
}
return dataKey, nil
}
func (ss *SecretsStoreImpl) GetAllDataKeys(ctx context.Context) ([]*secrets.DataKey, error) {
result := make([]*secrets.DataKey, 0)
err := ss.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
err := sess.Table(dataKeysTable).Find(&result)
return err
})
return result, err
}
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 {
if !dataKey.Active {
return fmt.Errorf("cannot insert deactivated data keys")
}
dataKey.Created = time.Now()
dataKey.Updated = dataKey.Created
_, err := sess.Table(dataKeysTable).Insert(dataKey)
return err
}
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{Id: id})
return err
})
}
func (ss *SecretsStoreImpl) ReEncryptDataKeys(
ctx context.Context,
providers map[secrets.ProviderID]secrets.Provider,
currProvider secrets.ProviderID,
) error {
keys := make([]*secrets.DataKey, 0)
if err := ss.sqlStore.NewSession(ctx).Table(dataKeysTable).Find(&keys); err != nil {
return err
}
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",
"id", k.Id,
"label", k.Label,
"provider", k.Provider,
)
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",
"id", k.Id,
"label", k.Label,
"provider", k.Provider,
"err", err,
)
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.Label = secrets.KeyLabel(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",
"id", k.Id,
"label", k.Label,
"provider", k.Provider,
"err", err,
)
return nil
}
if _, err := sess.Table(dataKeysTable).Where("name = ?", k.Id).Update(k); err != nil {
ss.log.Warn(
"Error while re-encrypting data encryption key",
"id", k.Id,
"label", k.Label,
"provider", k.Provider,
"err", err,
)
return nil
}
return nil
})
if err != nil {
return err
}
}
return nil
}