grafana/pkg/services/secrets/migrator/migrator.go
Joan López de la Franca Beltran 9abe9fa702
Encryption: Expose secrets migrations through HTTP API (#51707)
* Encryption: Move secrets migrations into secrets.Migrator

* Encryption: Refactor secrets.Service initialization

* Encryption: Add support to run secrets migrations even when EE is disabled

* Encryption: Expose secrets migrations through HTTP API

* Update docs

* Fix docs links

* Some adjustments to makes errors explicit through HTTP response
2022-07-18 08:57:58 +02:00

151 lines
4.4 KiB
Go

package migrator
import (
"context"
"encoding/base64"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/encryption"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/secrets/manager"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
type SecretsMigrator struct {
encryptionSrv encryption.Internal
secretsSrv *manager.SecretsService
sqlStore *sqlstore.SQLStore
settings setting.Provider
features featuremgmt.FeatureToggles
}
func ProvideSecretsMigrator(
encryptionSrv encryption.Internal,
service *manager.SecretsService,
sqlStore *sqlstore.SQLStore,
settings setting.Provider,
features featuremgmt.FeatureToggles,
) *SecretsMigrator {
return &SecretsMigrator{
encryptionSrv: encryptionSrv,
secretsSrv: service,
sqlStore: sqlStore,
settings: settings,
features: features,
}
}
func (m *SecretsMigrator) ReEncryptSecrets(ctx context.Context) (bool, error) {
err := m.initProvidersIfNeeded()
if err != nil {
return false, err
}
toReencrypt := []interface {
reencrypt(context.Context, *manager.SecretsService, *sqlstore.SQLStore) bool
}{
simpleSecret{tableName: "dashboard_snapshot", columnName: "dashboard_encrypted"},
b64Secret{simpleSecret: simpleSecret{tableName: "user_auth", columnName: "o_auth_access_token"}, encoding: base64.StdEncoding},
b64Secret{simpleSecret: simpleSecret{tableName: "user_auth", columnName: "o_auth_refresh_token"}, encoding: base64.StdEncoding},
b64Secret{simpleSecret: simpleSecret{tableName: "user_auth", columnName: "o_auth_token_type"}, encoding: base64.StdEncoding},
b64Secret{simpleSecret: simpleSecret{tableName: "secrets", columnName: "value"}, hasUpdatedColumn: true, encoding: base64.RawStdEncoding},
jsonSecret{tableName: "data_source"},
jsonSecret{tableName: "plugin_setting"},
alertingSecret{},
}
var anyFailure bool
for _, r := range toReencrypt {
if success := r.reencrypt(ctx, m.secretsSrv, m.sqlStore); !success {
anyFailure = true
}
}
return !anyFailure, nil
}
func (m *SecretsMigrator) RollBackSecrets(ctx context.Context) (bool, error) {
err := m.initProvidersIfNeeded()
if err != nil {
return false, err
}
toRollback := []interface {
rollback(context.Context, *manager.SecretsService, encryption.Internal, *sqlstore.SQLStore, string) bool
}{
simpleSecret{tableName: "dashboard_snapshot", columnName: "dashboard_encrypted"},
b64Secret{simpleSecret: simpleSecret{tableName: "user_auth", columnName: "o_auth_access_token"}, encoding: base64.StdEncoding},
b64Secret{simpleSecret: simpleSecret{tableName: "user_auth", columnName: "o_auth_refresh_token"}, encoding: base64.StdEncoding},
b64Secret{simpleSecret: simpleSecret{tableName: "user_auth", columnName: "o_auth_token_type"}, encoding: base64.StdEncoding},
b64Secret{simpleSecret: simpleSecret{tableName: "secrets", columnName: "value"}, hasUpdatedColumn: true, encoding: base64.RawStdEncoding},
jsonSecret{tableName: "data_source"},
jsonSecret{tableName: "plugin_setting"},
alertingSecret{},
}
var anyFailure bool
for _, r := range toRollback {
if failed := r.rollback(ctx,
m.secretsSrv,
m.encryptionSrv,
m.sqlStore,
m.settings.KeyValue("security", "secret_key").Value(),
); failed {
anyFailure = true
}
}
if anyFailure {
logger.Warn("Some errors happened, not cleaning up data keys table...")
return false, nil
}
_, sqlErr := m.sqlStore.NewSession(ctx).Exec("DELETE FROM data_keys")
if sqlErr != nil {
logger.Warn("Error while cleaning up data keys table...", "error", sqlErr)
return false, nil
}
return true, nil
}
func (m *SecretsMigrator) initProvidersIfNeeded() error {
if m.features.IsEnabled(featuremgmt.FlagDisableEnvelopeEncryption) {
logger.Info("Envelope encryption is not enabled but trying to init providers anyway...")
if err := m.secretsSrv.InitProviders(); err != nil {
logger.Error("Envelope encryption providers initialization failed", "error", err)
return err
}
}
return nil
}
type simpleSecret struct {
tableName string
columnName string
}
type b64Secret struct {
simpleSecret
hasUpdatedColumn bool
encoding *base64.Encoding
}
type jsonSecret struct {
tableName string
}
type alertingSecret struct{}
func nowInUTC() string {
return time.Now().UTC().Format("2006-01-02 15:04:05")
}
var logger = log.New("secrets.migrations")