mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 07:35:45 -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"},
|
jsonSecret{tableName: "plugin_setting"},
|
||||||
b64Secret{simpleSecret: simpleSecret{tableName: "signing_key", columnName: "private_key"}, encoding: base64.StdEncoding},
|
b64Secret{simpleSecret: simpleSecret{tableName: "signing_key", columnName: "private_key"}, encoding: base64.StdEncoding},
|
||||||
alertingSecret{},
|
alertingSecret{},
|
||||||
|
ssoSettingsSecret{},
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SecretsMigrator{
|
return &SecretsMigrator{
|
||||||
@ -157,6 +158,8 @@ type jsonSecret struct {
|
|||||||
|
|
||||||
type alertingSecret struct{}
|
type alertingSecret struct{}
|
||||||
|
|
||||||
|
type ssoSettingsSecret struct{}
|
||||||
|
|
||||||
func nowInUTC() string {
|
func nowInUTC() string {
|
||||||
return time.Now().UTC().Format("2006-01-02 15:04:05")
|
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"
|
||||||
"github.com/grafana/grafana/pkg/services/secrets/manager"
|
"github.com/grafana/grafana/pkg/services/secrets/manager"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"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 {
|
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
|
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/encryption"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
||||||
"github.com/grafana/grafana/pkg/services/secrets/manager"
|
"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(
|
func (s simpleSecret) Rollback(
|
||||||
@ -294,3 +296,71 @@ func (s alertingSecret) Rollback(
|
|||||||
|
|
||||||
return anyFailure
|
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) {
|
func (s *Service) encryptSecrets(ctx context.Context, settings map[string]any) (map[string]any, error) {
|
||||||
result := make(map[string]any)
|
result := make(map[string]any)
|
||||||
for k, v := range settings {
|
for k, v := range settings {
|
||||||
if isSecret(k) && v != "" {
|
if IsSecretField(k) && v != "" {
|
||||||
strValue, ok := v.(string)
|
strValue, ok := v.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return result, fmt.Errorf("failed to encrypt %s setting because it is not a string: %v", k, v)
|
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) {
|
func (s *Service) decryptSecrets(ctx context.Context, settings map[string]any) (map[string]any, error) {
|
||||||
for k, v := range settings {
|
for k, v := range settings {
|
||||||
if isSecret(k) && v != "" {
|
if IsSecretField(k) && v != "" {
|
||||||
strValue, ok := v.(string)
|
strValue, ok := v.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
s.logger.Error("Failed to parse secret value, it is not a string", "key", k)
|
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 {
|
func removeSecrets(settings map[string]any) map[string]any {
|
||||||
result := make(map[string]any)
|
result := make(map[string]any)
|
||||||
for k, v := range settings {
|
for k, v := range settings {
|
||||||
if isSecret(k) {
|
if IsSecretField(k) {
|
||||||
result[k] = setting.RedactedPassword
|
result[k] = setting.RedactedPassword
|
||||||
continue
|
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) {
|
func mergeSecrets(settings map[string]any, storedSettings map[string]any) (map[string]any, error) {
|
||||||
settingsWithSecrets := map[string]any{}
|
settingsWithSecrets := map[string]any{}
|
||||||
for k, v := range settings {
|
for k, v := range settings {
|
||||||
if isSecret(k) {
|
if IsSecretField(k) {
|
||||||
strValue, ok := v.(string)
|
strValue, ok := v.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("secret value is not a string")
|
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
|
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"}
|
secretFieldPatterns := []string{"secret", "private", "certificate"}
|
||||||
|
|
||||||
for _, v := range secretFieldPatterns {
|
for _, v := range secretFieldPatterns {
|
||||||
|
Loading…
Reference in New Issue
Block a user