From ef5cc12b331e3cfe5a64f9e3ad2f5898b5ee3c71 Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Fri, 14 Mar 2025 07:22:41 +0100 Subject: [PATCH] Alerting: Add HMAC signature config to the webhook integration (#100960) Adds HMAC-SHA256 signature support to webhook notifications, providing a way to verify the authenticity and integrity of webhook requests. The implementation allows to specify the header in which the signature will be sent. The signature is calculated from the request body. An optional timestamp header name can be provided. If set, the HMAC signature will be generated by concatenating the timestamp, a ":" and the request body: {timestamp}:{body}. The timestamp will also be sent in the provided header name. --- .../administration/provisioning/index.md | 32 +++++++++++++---- .../integrations/webhook-notifier.md | 32 +++++++++++++++++ .../file-provisioning/index.md | 7 ++++ .../channels_config/available_channels.go | 36 +++++++++++++++++++ .../available_channels_test.go | 2 +- 5 files changed, 101 insertions(+), 8 deletions(-) diff --git a/docs/sources/administration/provisioning/index.md b/docs/sources/administration/provisioning/index.md index dcec3f915d7..6145d69a761 100644 --- a/docs/sources/administration/provisioning/index.md +++ b/docs/sources/administration/provisioning/index.md @@ -617,13 +617,31 @@ Grafana encrypts secure settings in the database. #### Alert notification `webhook` -| Name | Secure setting | -| ------------ | -------------- | -| `url` | | -| `httpMethod` | | -| `username` | | -| `password` | yes | -| `tls_config` | | +| Name | Secure setting | +| ------------- | -------------- | +| `url` | | +| `http_method` | | +| `username` | | +| `password` | yes | +| `tls_config` | | +| `hmac_config` | | + +##### TLS configuration + +| Name | Secure setting | +| -------------------- | -------------- | +| `insecureSkipVerify` | | +| `clientCertificate` | yes | +| `clientKey` | yes | +| `caCertificate` | yes | + +##### HMAC signature configuration + +| Name | Secure setting | +| ----------------- | -------------- | +| `secret` | yes | +| `header` | | +| `timestampHeader` | | #### Alert notification `googlechat` diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md index e7741ac3b56..bf4e0cc90fe 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md @@ -72,6 +72,7 @@ For more details on contact points, including how to test them and enable notifi | Authentication Header Credentials | Credentials for the `Authorization` Request header. | | Max Alerts | Maximum number of alerts to include in a notification. Any alerts exceeding this limit are ignored. `0` means no limit. | | TLS | TLS configuration options, including CA certificate, client certificate, and client key. | +| HMAC Signature | HMAC signature configuration options. | {{< admonition type="note" >}} @@ -79,6 +80,37 @@ You can configure either HTTP Basic Authentication or the Authorization request {{< /admonition >}} +#### HMAC signature + +You can secure your webhook notifications using HMAC signatures to verify the authenticity and integrity of the requests. When enabled, Grafana signs the webhook payload with a shared secret using HMAC-SHA256. + +| Option | Description | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Secret | The shared secret key used to generate the HMAC signature. | +| Header | The HTTP header where the signature will be set. Default is `X-Grafana-Alerting-Signature`. | +| Timestamp Header | Optional header to include a timestamp in the signature calculation. When specified, Grafana will set a Unix timestamp in this header and include it in the HMAC calculation. This provides protection against replay attacks. | + +When HMAC signing is configured, Grafana generates a signature using HMAC-SHA256 with your secret key. If a timestamp header is specified, a Unix timestamp is included in the signature calculation. The signature is calculated as: + +``` +HMAC(timestamp + ":" + body) +``` + +The timestamp is sent in the specified header. If no timestamp header is specified, the signature is calculated just from the request body. The signature is sent as a hex-encoded string in the specified signature header. + +##### Validate a request + +To validate incoming webhook requests from Grafana, follow these steps: + +1. Extract the signature from the header (default is `X-Grafana-Alerting-Signature`). +2. If you configured a timestamp header, extract the timestamp value and verify it's recent to prevent replay attacks. +3. Calculate the expected signature: + - Create an HMAC-SHA256 hash using your shared secret + - If using timestamps, include the timestamp followed by a colon (`:`) before the request body + - Hash the raw request body + - Convert the result to a hexadecimal string +4. Compare the calculated signature with the one in the request header. + #### Optional settings using templates Use the following settings to include custom data within the [JSON payload](#body). Both options support using [notification templates](ref:notification-templates). diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md index f7cb4a0e174..898fdf894bf 100644 --- a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md +++ b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md @@ -628,6 +628,13 @@ settings: clientKey: key in PEM format # caCertificate: CA certificate in PEM format + hmacConfig: + # + secret: secret-key + # + header: X-Grafana-Alerting-Signature + # + timestampHeader: X-Grafana-Alerting-Signature-Timestamp ``` {{< /collapse >}} diff --git a/pkg/services/ngalert/notifier/channels_config/available_channels.go b/pkg/services/ngalert/notifier/channels_config/available_channels.go index 2e2ce80d24d..7adfb84c095 100644 --- a/pkg/services/ngalert/notifier/channels_config/available_channels.go +++ b/pkg/services/ngalert/notifier/channels_config/available_channels.go @@ -1016,6 +1016,42 @@ func GetAvailableNotifiers() []*NotifierPlugin { }, }, }, + { + Label: "HMAC Signature", + PropertyName: "hmacConfig", + Description: "HMAC signature configuration options", + Element: ElementTypeSubform, + SubformOptions: []NotifierOption{ + { + Label: "Secret", + Element: ElementTypeInput, + Description: "", + InputType: InputTypeText, + PropertyName: "secret", + Required: true, + Secure: true, + }, + { + Label: "Header", + Element: ElementTypeInput, + Description: "The header in which the HMAC signature will be included.", + InputType: InputTypeText, + PropertyName: "header", + Placeholder: "X-Grafana-Alerting-Signature", + Required: false, + Secure: false, + }, + { + Label: "Timestamp header", + Element: ElementTypeInput, + Description: "If set, the timestamp will be included in the HMAC signature. The value should be the name of the header to use.", + InputType: InputTypeText, + PropertyName: "timestampHeader", + Required: false, + Secure: false, + }, + }, + }, }, }, { diff --git a/pkg/services/ngalert/notifier/channels_config/available_channels_test.go b/pkg/services/ngalert/notifier/channels_config/available_channels_test.go index cc2defa2c80..0fe4330faf6 100644 --- a/pkg/services/ngalert/notifier/channels_config/available_channels_test.go +++ b/pkg/services/ngalert/notifier/channels_config/available_channels_test.go @@ -22,7 +22,7 @@ func TestGetSecretKeysForContactPointType(t *testing.T) { {receiverType: "sensugo", expectedSecretFields: []string{"apikey"}}, {receiverType: "teams", expectedSecretFields: []string{}}, {receiverType: "telegram", expectedSecretFields: []string{"bottoken"}}, - {receiverType: "webhook", expectedSecretFields: []string{"password", "authorization_credentials", "tlsConfig.caCertificate", "tlsConfig.clientCertificate", "tlsConfig.clientKey"}}, + {receiverType: "webhook", expectedSecretFields: []string{"password", "authorization_credentials", "tlsConfig.caCertificate", "tlsConfig.clientCertificate", "tlsConfig.clientKey", "hmacConfig.secret"}}, {receiverType: "wecom", expectedSecretFields: []string{"url", "secret"}}, {receiverType: "prometheus-alertmanager", expectedSecretFields: []string{"basicAuthPassword"}}, {receiverType: "discord", expectedSecretFields: []string{"url"}},