Security: apply patch 428 (#106710)

* declare dingding url as secret

patch raw settings before parsing because DingDing's config parser does not know about secrets

* fix integration test

---------

Co-authored-by: Yuri Tseretyan <yuriy.tseretyan@grafana.com>
pull/106402/head^2
Kevin Minehart 1 month ago committed by GitHub
parent 5f3c04f537
commit 910eb1dd9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 56
      pkg/services/ngalert/notifier/alertmanager.go
  2. 1
      pkg/services/ngalert/notifier/channels_config/available_channels.go
  3. 2
      pkg/services/ngalert/notifier/channels_config/available_channels_test.go
  4. 9
      pkg/services/ngalert/notifier/testreceivers.go
  5. 6
      pkg/tests/api/alerting/api_notification_channel_test.go

@ -53,6 +53,7 @@ type alertmanager struct {
Store AlertingStore
stateStore stateStore
DefaultConfiguration string
decryptFn alertingNotify.GetDecryptedValueFn
}
// maintenanceOptions represent the options for components that need maintenance on a frequency within the Alertmanager.
@ -149,6 +150,7 @@ func NewAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
Store: store,
stateStore: stateStore,
logger: l.New("component", "alertmanager", opts.TenantKey, opts.TenantID), // similar to what the base does
decryptFn: decryptFn,
}
return am, nil
@ -335,6 +337,14 @@ func (am *alertmanager) applyConfig(ctx context.Context, cfg *apimodels.Postable
return false, nil
}
receivers := PostableApiAlertingConfigToApiReceivers(cfg.AlertmanagerConfig)
for _, recv := range receivers {
err = patchNewSecureFields(ctx, recv, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
if err != nil {
return false, err
}
}
am.logger.Info("Applying new configuration to Alertmanager", "configHash", fmt.Sprintf("%x", configHash))
err = am.Base.ApplyConfig(alertingNotify.NotificationsConfiguration{
RoutingTree: cfg.AlertmanagerConfig.Route.AsAMRoute(),
@ -342,7 +352,7 @@ func (am *alertmanager) applyConfig(ctx context.Context, cfg *apimodels.Postable
MuteTimeIntervals: cfg.AlertmanagerConfig.MuteTimeIntervals,
TimeIntervals: cfg.AlertmanagerConfig.TimeIntervals,
Templates: ToTemplateDefinitions(cfg),
Receivers: PostableApiAlertingConfigToApiReceivers(cfg.AlertmanagerConfig),
Receivers: receivers,
DispatcherLimits: &nilLimits{},
Raw: rawConfig,
Hash: configHash,
@ -355,6 +365,50 @@ func (am *alertmanager) applyConfig(ctx context.Context, cfg *apimodels.Postable
return true, nil
}
func patchNewSecureFields(ctx context.Context, api *alertingNotify.APIReceiver, decode alertingNotify.DecodeSecretsFn, decrypt alertingNotify.GetDecryptedValueFn) error {
for _, integration := range api.Integrations {
switch integration.Type {
case "dingding":
err := patchSettingsFromSecureSettings(ctx, integration, "url", decode, decrypt)
if err != nil {
return err
}
}
}
return nil
}
func patchSettingsFromSecureSettings(ctx context.Context, integration *alertingNotify.GrafanaIntegrationConfig, key string, decode alertingNotify.DecodeSecretsFn, decrypt alertingNotify.GetDecryptedValueFn) error {
if _, ok := integration.SecureSettings[key]; !ok {
return nil
}
decoded, err := decode(integration.SecureSettings)
if err != nil {
return err
}
settings := map[string]any{}
err = json.Unmarshal(integration.Settings, &settings)
if err != nil {
return err
}
currentValue, ok := settings[key]
currentString := ""
if ok {
currentString, _ = currentValue.(string)
}
secretValue := decrypt(ctx, decoded, key, currentString)
if secretValue == currentString {
return nil
}
settings[key] = secretValue
data, err := json.Marshal(settings)
if err != nil {
return err
}
integration.Settings = data
return nil
}
// PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
func (am *alertmanager) PutAlerts(_ context.Context, postableAlerts apimodels.PostableAlerts) error {
alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))

@ -284,6 +284,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
Placeholder: "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx",
PropertyName: "url",
Required: true,
Secure: true,
},
{
Label: "Message Type",

@ -11,7 +11,7 @@ func TestGetSecretKeysForContactPointType(t *testing.T) {
receiverType string
expectedSecretFields []string
}{
{receiverType: "dingding", expectedSecretFields: []string{}},
{receiverType: "dingding", expectedSecretFields: []string{"url"}},
{receiverType: "kafka", expectedSecretFields: []string{"password"}},
{receiverType: "email", expectedSecretFields: []string{}},
{receiverType: "pagerduty", expectedSecretFields: []string{"integrationKey"}},

@ -24,12 +24,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
SecureSettings: gr.SecureSettings,
})
}
receivers = append(receivers, &alertingNotify.APIReceiver{
recv := &alertingNotify.APIReceiver{
ConfigReceiver: r.Receiver,
GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
Integrations: integrations,
},
})
}
err := patchNewSecureFields(ctx, recv, alertingNotify.DecodeSecretsFromBase64, am.decryptFn)
if err != nil {
return nil, 0, err
}
receivers = append(receivers, recv)
}
a := &alertingNotify.PostableAlert{}
if c.Alert != nil {

@ -2088,10 +2088,8 @@ var expAlertmanagerConfigFromAPI = `
"name": "dingding_test",
"type": "dingding",
"disableResolveMessage": false,
"settings": {
"url": "http://CHANNEL_ADDR/dingding_recv/dingding_test"
},
"secureFields": {}
"settings": {},
"secureFields": {"url": true}
}
]
},

Loading…
Cancel
Save