mirror of
https://github.com/grafana/grafana.git
synced 2025-02-09 23:16:16 -06:00
SSO: add SSO settings to secrets migrator (#86913)
* add sso settings to secrets migrator * unify SSO settings in all log lines
This commit is contained in:
parent
ac152ca416
commit
4bf9405ce4
@ -45,6 +45,7 @@ func ProvideSecretsMigrator(
|
||||
jsonSecret{tableName: "plugin_setting"},
|
||||
b64Secret{simpleSecret: simpleSecret{tableName: "signing_key", columnName: "private_key"}, encoding: base64.StdEncoding},
|
||||
alertingSecret{},
|
||||
ssoSettingsSecret{},
|
||||
}
|
||||
|
||||
return &SecretsMigrator{
|
||||
@ -157,6 +158,8 @@ type jsonSecret struct {
|
||||
|
||||
type alertingSecret struct{}
|
||||
|
||||
type ssoSettingsSecret struct{}
|
||||
|
||||
func nowInUTC() string {
|
||||
return time.Now().UTC().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/manager"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings/models"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingsimpl"
|
||||
)
|
||||
|
||||
func (s simpleSecret) ReEncrypt(ctx context.Context, secretsSrv *manager.SecretsService, sqlStore db.DB) bool {
|
||||
@ -289,3 +291,90 @@ func (s alertingSecret) ReEncrypt(ctx context.Context, secretsSrv *manager.Secre
|
||||
|
||||
return !anyFailure
|
||||
}
|
||||
|
||||
func (s ssoSettingsSecret) ReEncrypt(ctx context.Context, secretsSrv *manager.SecretsService, sqlStore db.DB) bool {
|
||||
results := make([]*models.SSOSettings, 0)
|
||||
|
||||
err := sqlStore.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
return sess.Find(&results)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Warn("Failed to fetch SSO settings to re-encrypt", "err", err)
|
||||
return false
|
||||
}
|
||||
|
||||
var anyFailure bool
|
||||
|
||||
for _, result := range results {
|
||||
err := sqlStore.InTransaction(ctx, func(ctx context.Context) error {
|
||||
for field, value := range result.Settings {
|
||||
if ssosettingsimpl.IsSecretField(field) {
|
||||
decrypted, err := s.decryptValue(ctx, value, secretsSrv)
|
||||
if err != nil {
|
||||
logger.Warn("Could not decrypt SSO settings secret", "id", result.ID, "field", field, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if decrypted == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
reencrypted, err := secretsSrv.Encrypt(ctx, decrypted, secrets.WithoutScope())
|
||||
if err != nil {
|
||||
logger.Warn("Could not re-encrypt SSO settings secret", "id", result.ID, "field", field, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
result.Settings[field] = base64.RawStdEncoding.EncodeToString(reencrypted)
|
||||
}
|
||||
}
|
||||
|
||||
err = sqlStore.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
_, err := sess.Where("id = ?", result.ID).Update(result)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warn("Could not update SSO settings secrets while re-encrypting it", "id", result.ID, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
anyFailure = true
|
||||
}
|
||||
}
|
||||
|
||||
if anyFailure {
|
||||
logger.Warn("SSO settings secrets have been re-encrypted with errors")
|
||||
} else {
|
||||
logger.Info("SSO settings secrets have been re-encrypted successfully")
|
||||
}
|
||||
|
||||
return !anyFailure
|
||||
}
|
||||
|
||||
func (s ssoSettingsSecret) decryptValue(ctx context.Context, value any, secretsSrv *manager.SecretsService) ([]byte, error) {
|
||||
strValue, ok := value.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("SSO secret value is not a string")
|
||||
}
|
||||
|
||||
if strValue == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
decoded, err := base64.RawStdEncoding.DecodeString(strValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not decode base64-encoded SSO settings secret: %w", err)
|
||||
}
|
||||
|
||||
decrypted, err := secretsSrv.Decrypt(ctx, decoded)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not decrypt SSO settings secret: %w", err)
|
||||
}
|
||||
|
||||
return decrypted, nil
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/encryption"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/manager"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings/models"
|
||||
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingsimpl"
|
||||
)
|
||||
|
||||
func (s simpleSecret) Rollback(
|
||||
@ -294,3 +296,71 @@ func (s alertingSecret) Rollback(
|
||||
|
||||
return anyFailure
|
||||
}
|
||||
|
||||
func (s ssoSettingsSecret) Rollback(
|
||||
ctx context.Context,
|
||||
secretsSrv *manager.SecretsService,
|
||||
encryptionSrv encryption.Internal,
|
||||
sqlStore db.DB,
|
||||
secretKey string,
|
||||
) (anyFailure bool) {
|
||||
results := make([]*models.SSOSettings, 0)
|
||||
|
||||
err := sqlStore.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
return sess.Find(&results)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Warn("Failed to fetch SSO settings to roll back")
|
||||
return true
|
||||
}
|
||||
|
||||
for _, result := range results {
|
||||
err := sqlStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||
for field, value := range result.Settings {
|
||||
if ssosettingsimpl.IsSecretField(field) {
|
||||
decrypted, err := s.decryptValue(ctx, value, secretsSrv)
|
||||
if err != nil {
|
||||
logger.Warn("Could not decrypt SSO settings secret", "id", result.ID, "field", field, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if decrypted == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
reencrypted, err := encryptionSrv.Encrypt(ctx, decrypted, secretKey)
|
||||
if err != nil {
|
||||
logger.Warn("Could not re-encrypt SSO settings secret", "id", result.ID, "field", field, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
result.Settings[field] = base64.RawStdEncoding.EncodeToString(reencrypted)
|
||||
}
|
||||
}
|
||||
|
||||
err = sqlStore.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
_, err := sess.Where("id = ?", result.ID).Update(result)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warn("Could not update SSO settings secrets while re-encrypting it", "id", result.ID, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
anyFailure = true
|
||||
}
|
||||
}
|
||||
|
||||
if anyFailure {
|
||||
logger.Warn("SSO settings secrets have been rolled back with errors")
|
||||
} else {
|
||||
logger.Info("SSO settings secrets have been rolled back successfully")
|
||||
}
|
||||
|
||||
return anyFailure
|
||||
}
|
||||
|
@ -324,7 +324,7 @@ func (s *Service) getFallbackStrategyFor(provider string) (ssosettings.FallbackS
|
||||
func (s *Service) encryptSecrets(ctx context.Context, settings map[string]any) (map[string]any, error) {
|
||||
result := make(map[string]any)
|
||||
for k, v := range settings {
|
||||
if isSecret(k) && v != "" {
|
||||
if IsSecretField(k) && v != "" {
|
||||
strValue, ok := v.(string)
|
||||
if !ok {
|
||||
return result, fmt.Errorf("failed to encrypt %s setting because it is not a string: %v", k, v)
|
||||
@ -414,7 +414,7 @@ func (s *Service) mergeSSOSettings(dbSettings, systemSettings *models.SSOSetting
|
||||
|
||||
func (s *Service) decryptSecrets(ctx context.Context, settings map[string]any) (map[string]any, error) {
|
||||
for k, v := range settings {
|
||||
if isSecret(k) && v != "" {
|
||||
if IsSecretField(k) && v != "" {
|
||||
strValue, ok := v.(string)
|
||||
if !ok {
|
||||
s.logger.Error("Failed to parse secret value, it is not a string", "key", k)
|
||||
@ -449,7 +449,7 @@ func (s *Service) isProviderConfigurable(provider string) bool {
|
||||
func removeSecrets(settings map[string]any) map[string]any {
|
||||
result := make(map[string]any)
|
||||
for k, v := range settings {
|
||||
if isSecret(k) {
|
||||
if IsSecretField(k) {
|
||||
result[k] = setting.RedactedPassword
|
||||
continue
|
||||
}
|
||||
@ -487,7 +487,7 @@ func mergeSettings(storedSettings, systemSettings map[string]any) map[string]any
|
||||
func mergeSecrets(settings map[string]any, storedSettings map[string]any) (map[string]any, error) {
|
||||
settingsWithSecrets := map[string]any{}
|
||||
for k, v := range settings {
|
||||
if isSecret(k) {
|
||||
if IsSecretField(k) {
|
||||
strValue, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret value is not a string")
|
||||
@ -515,7 +515,8 @@ func overrideMaps(maps ...map[string]any) map[string]any {
|
||||
return result
|
||||
}
|
||||
|
||||
func isSecret(fieldName string) bool {
|
||||
// IsSecretField returns true if the SSO settings field provided is a secret
|
||||
func IsSecretField(fieldName string) bool {
|
||||
secretFieldPatterns := []string{"secret", "private", "certificate"}
|
||||
|
||||
for _, v := range secretFieldPatterns {
|
||||
|
Loading…
Reference in New Issue
Block a user