|
|
|
@ -3,11 +3,13 @@ package notifier |
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"encoding/base64" |
|
|
|
|
"encoding/json" |
|
|
|
|
"errors" |
|
|
|
|
"fmt" |
|
|
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/store" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/secrets" |
|
|
|
|
) |
|
|
|
@ -57,17 +59,90 @@ func (c *alertmanagerCrypto) ProcessSecureSettings(ctx context.Context, orgId in |
|
|
|
|
|
|
|
|
|
// EncryptReceiverConfigs encrypts all SecureSettings in the given receivers.
|
|
|
|
|
func EncryptReceiverConfigs(c []*definitions.PostableApiReceiver, encrypt definitions.EncryptFn) error { |
|
|
|
|
return encryptReceiverConfigs(c, encrypt, true) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func EncryptReceiverConfigSettings(c []*definitions.PostableApiReceiver, encrypt definitions.EncryptFn) error { |
|
|
|
|
return encryptReceiverConfigs(c, encrypt, false) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// encryptReceiverConfigs encrypts all SecureSettings in the given receivers.
|
|
|
|
|
// encryptExisting determines whether to encrypt existing secure settings.
|
|
|
|
|
func encryptReceiverConfigs(c []*definitions.PostableApiReceiver, encrypt definitions.EncryptFn, encryptExisting bool) error { |
|
|
|
|
// encrypt secure settings for storing them in DB
|
|
|
|
|
for _, r := range c { |
|
|
|
|
switch r.Type() { |
|
|
|
|
case definitions.GrafanaReceiverType: |
|
|
|
|
for _, gr := range r.PostableGrafanaReceivers.GrafanaManagedReceivers { |
|
|
|
|
for k, v := range gr.SecureSettings { |
|
|
|
|
encryptedData, err := encrypt(context.Background(), []byte(v)) |
|
|
|
|
if encryptExisting { |
|
|
|
|
for k, v := range gr.SecureSettings { |
|
|
|
|
encryptedData, err := encrypt(context.Background(), []byte(v)) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("failed to encrypt secure settings: %w", err) |
|
|
|
|
} |
|
|
|
|
gr.SecureSettings[k] = base64.StdEncoding.EncodeToString(encryptedData) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if len(gr.Settings) > 0 { |
|
|
|
|
// We need to parse the settings to check for secret keys. If we find any, we encrypt them and
|
|
|
|
|
// store them in SecureSettings. This can happen from incorrect configuration or when an integration
|
|
|
|
|
// definition is updated to make a field secure.
|
|
|
|
|
settings := make(map[string]any) |
|
|
|
|
if err := json.Unmarshal(gr.Settings, &settings); err != nil { |
|
|
|
|
return fmt.Errorf("integration '%s' of receiver '%s' has settings that cannot be parsed as JSON: %w", gr.Type, gr.Name, err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
secretKeys, err := channels_config.GetSecretKeysForContactPointType(gr.Type) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("failed to encrypt secure settings: %w", err) |
|
|
|
|
return fmt.Errorf("failed to get secret keys for contact point type %s: %w", gr.Type, err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
secureSettings := gr.SecureSettings |
|
|
|
|
if secureSettings == nil { |
|
|
|
|
secureSettings = make(map[string]string) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
settingsChanged := false |
|
|
|
|
secureSettingsChanged := false |
|
|
|
|
for _, secretKey := range secretKeys { |
|
|
|
|
settingsValue, ok := settings[secretKey] |
|
|
|
|
if !ok { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Secrets should not be stored in settings regardless.
|
|
|
|
|
delete(settings, secretKey) |
|
|
|
|
settingsChanged = true |
|
|
|
|
|
|
|
|
|
// If the secret is already encrypted, we don't need to encrypt it again.
|
|
|
|
|
if _, ok := secureSettings[secretKey]; ok { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if strVal, isString := settingsValue.(string); isString { |
|
|
|
|
encrypted, err := encrypt(context.Background(), []byte(strVal)) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("failed to encrypt secure settings: %w", err) |
|
|
|
|
} |
|
|
|
|
secureSettings[secretKey] = base64.StdEncoding.EncodeToString(encrypted) |
|
|
|
|
secureSettingsChanged = true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Defensive checks to limit the risk of unintentional edge case changes in this legacy API.
|
|
|
|
|
if settingsChanged { |
|
|
|
|
// If we removed any secret keys from settings, we need to save the updated settings.
|
|
|
|
|
jsonBytes, err := json.Marshal(settings) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
gr.Settings = jsonBytes |
|
|
|
|
} |
|
|
|
|
if secureSettingsChanged { |
|
|
|
|
// If we added any secure settings, we need to save the updated secure settings.
|
|
|
|
|
gr.SecureSettings = secureSettings |
|
|
|
|
} |
|
|
|
|
gr.SecureSettings[k] = base64.StdEncoding.EncodeToString(encryptedData) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
default: |
|
|
|
@ -94,6 +169,14 @@ func (c *alertmanagerCrypto) LoadSecureSettings(ctx context.Context, orgId int64 |
|
|
|
|
if err != nil { |
|
|
|
|
c.log.Warn("Last known alertmanager configuration was invalid. Overwriting...") |
|
|
|
|
} else { |
|
|
|
|
// First we encrypt the secure settings in the existing configuration.
|
|
|
|
|
// This is done to ensure that any secure settings incorrectly stored in Settings are encrypted and moved to
|
|
|
|
|
// SecureSettings. This can happen if an integration definition is updated to make a field secure.
|
|
|
|
|
if err := EncryptReceiverConfigSettings(currentConfig.AlertmanagerConfig.Receivers, func(ctx context.Context, payload []byte) ([]byte, error) { |
|
|
|
|
return c.Encrypt(ctx, payload, secrets.WithoutScope()) |
|
|
|
|
}); err != nil { |
|
|
|
|
return fmt.Errorf("failed to encrypt receivers: %w", err) |
|
|
|
|
} |
|
|
|
|
currentReceiverMap = currentConfig.GetGrafanaReceiverMap() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|