From 1745dbb85de23a30c0119fa12b2648e31fb924cb Mon Sep 17 00:00:00 2001 From: Rounak Datta Date: Fri, 3 Jun 2022 20:51:23 +0530 Subject: [PATCH] Pass HMAC in header for integrity --- pkg/config/config.go | 10 ++++++---- pkg/notifications/helpers.go | 1 - pkg/notifications/notification.go | 12 ++++++++---- pkg/notifications/webhook.go | 20 ++++++++++++++++++++ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 8491f1706b8..780d5854b3e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -160,8 +160,9 @@ const ( MetricsUsername Key = `metrics.username` MetricsPassword Key = `metrics.password` - WebhookEnabled Key = "webhook.enabled" - WebhookEndpointURL Key = "webhook.endpointurl" + WebhookNotificationsEnabled Key = "notifications.webhook.enabled" + WebhookNotificationEndpointURL Key = "notifications.webhook.endpointurl" + WebhookNotificationSignatureSecret Key = "notifications.webhook.signaturesecret" ) // GetString returns a string config value @@ -370,8 +371,9 @@ func InitDefaultConfig() { KeyvalueType.setDefault("memory") // Metrics MetricsEnabled.setDefault(false) - // Webhook Notifications - WebhookEnabled.setDefault(false) + // Webhook Notification + WebhookNotificationsEnabled.setDefault(false) + WebhookNotificationSignatureSecret.setDefault("") } // InitConfig initializes the config, sets defaults etc. diff --git a/pkg/notifications/helpers.go b/pkg/notifications/helpers.go index 65807d4e734..f642ffdabcd 100644 --- a/pkg/notifications/helpers.go +++ b/pkg/notifications/helpers.go @@ -16,7 +16,6 @@ func DoPost(url string, body string, headers map[string]string) (resp *http.Resp for k, v := range headers { req.Header.Add(k, v) } - req.Header.Add("Content-Type", "application/json") hc := http.Client{} return hc.Do(req) } diff --git a/pkg/notifications/notification.go b/pkg/notifications/notification.go index 91d31331941..104d9c6386e 100644 --- a/pkg/notifications/notification.go +++ b/pkg/notifications/notification.go @@ -59,7 +59,7 @@ func Notify(notifiable Notifiable, notification Notification) (err error) { return } - if config.WebhookEnabled.GetBool() { + if config.WebhookNotificationsEnabled.GetBool() { err = notifyWebhook(notifiable, notification) if err != nil { return @@ -90,10 +90,14 @@ func notifyWebhook(notifiable Notifiable, notification Notification) error { return err } + signature := GenerateHMAC(string(content)) webhookNotification := &WebhookNotification{ - endpointURL: config.DatabasePassword.GetString(), - headers: map[string]string{}, // TODO: allow passing user-configurable headers - body: string(content), + endpointURL: config.WebhookNotificationEndpointURL.GetString(), + headers: map[string]string{ + webhookNotificationSignatureHeader: signature, + webhookNotificationContentTypeHeader: "application/json", + }, + body: string(content), } return SendWebhook(webhookNotification) diff --git a/pkg/notifications/webhook.go b/pkg/notifications/webhook.go index b6c31aef8c9..24c9996ac6a 100644 --- a/pkg/notifications/webhook.go +++ b/pkg/notifications/webhook.go @@ -16,6 +16,19 @@ package notifications +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + + "code.vikunja.io/api/pkg/config" +) + +const ( + webhookNotificationSignatureHeader = "x-vikunja-signature" + webhookNotificationContentTypeHeader = "Content-Type" +) + // WebhookNotification represents a notification that published to a http endpoint type WebhookNotification struct { endpointURL string @@ -23,6 +36,13 @@ type WebhookNotification struct { body string } +// GenerateHMAC generates a HMAC code for the webhook body signed with the configured secret +func GenerateHMAC(requestBody string) string { + h := hmac.New(sha256.New, []byte(config.WebhookNotificationSignatureSecret.GetString())) + h.Write([]byte(requestBody)) + return hex.EncodeToString(h.Sum(nil)) +} + // SendWebhook sends the notification to the configured endpoint func SendWebhook(w *WebhookNotification) error { resp, err := DoPost(w.endpointURL, w.body, w.headers)