From 4fe2a11223185b2c76da243b236f8ddc8dcc1704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Mon, 2 Feb 2026 12:54:01 +0800 Subject: [PATCH] fix(notify): support webhook sign (#24183) --- pkg/apis/notify/config.go | 1 + pkg/apis/notify/robot.go | 14 +++++--- pkg/mcclient/options/notify/robot.go | 2 ++ pkg/notify/models/notification.go | 1 + pkg/notify/models/robot.go | 38 ++++++++++++--------- pkg/notify/sender/webhook.go | 17 +++++++++ pkg/notify/tasks/notifications_send_task.go | 1 + 7 files changed, 53 insertions(+), 21 deletions(-) diff --git a/pkg/apis/notify/config.go b/pkg/apis/notify/config.go index 6114178cd7..04f3e2ae7a 100644 --- a/pkg/apis/notify/config.go +++ b/pkg/apis/notify/config.go @@ -162,6 +162,7 @@ type SendParams struct { Header jsonutils.JSONObject Body jsonutils.JSONObject MsgKey string + SecretKey string `json:"secret_key"` DomainId string RemoteTemplateParam SRemoteTemplateParam GroupKey string diff --git a/pkg/apis/notify/robot.go b/pkg/apis/notify/robot.go index 3e42d9cbdc..8b2b4a401a 100644 --- a/pkg/apis/notify/robot.go +++ b/pkg/apis/notify/robot.go @@ -21,6 +21,10 @@ import ( "yunion.io/x/onecloud/pkg/apis" ) +const ( + WEBHOOK_SIGNATURE_HEADER = "X-Auth-Token" +) + type RobotCreateInput struct { apis.SharableVirtualResourceCreateInput apis.EnabledBaseResourceCreateInput @@ -37,6 +41,7 @@ type RobotCreateInput struct { Header jsonutils.JSONObject `json:"header"` Body jsonutils.JSONObject `json:"body"` MsgKey string `json:"msg_key"` + SecretKey string `json:"secret_key"` UseTemplate tristate.TriState `json:"use_template"` } @@ -63,8 +68,9 @@ type RobotUpdateInput struct { Address string `json:"address"` // description: Language preference // example: en - Lang string `json:"lang"` - Header jsonutils.JSONObject `json:"header"` - Body jsonutils.JSONObject `json:"body"` - MsgKey string `json:"msg_key"` + Lang string `json:"lang"` + Header jsonutils.JSONObject `json:"header"` + Body jsonutils.JSONObject `json:"body"` + MsgKey string `json:"msg_key"` + SecretKey string `json:"secret_key"` } diff --git a/pkg/mcclient/options/notify/robot.go b/pkg/mcclient/options/notify/robot.go index 83c1979d2c..970fbcae3d 100644 --- a/pkg/mcclient/options/notify/robot.go +++ b/pkg/mcclient/options/notify/robot.go @@ -41,6 +41,7 @@ type RobotCreateOptions struct { Header string Body string MsgKey string + SecretKey string UseTemplate bool `help:"just for webhook"` } @@ -90,6 +91,7 @@ type SrobotUpdateOptions struct { Header *string Body *string MsgKey string + SecretKey string UseTemplate tristate.TriState } diff --git a/pkg/notify/models/notification.go b/pkg/notify/models/notification.go index 47cb5c7f2c..e1e32e15dc 100644 --- a/pkg/notify/models/notification.go +++ b/pkg/notify/models/notification.go @@ -365,6 +365,7 @@ func (nm *SNotificationManager) PerformContactNotify(ctx context.Context, userCr params.Header = robot.Header params.Body = robot.Body params.MsgKey = robot.MsgKey + params.SecretKey = robot.SecretKey params.Receivers = api.SNotifyReceiver{ Contact: robot.Address, } diff --git a/pkg/notify/models/robot.go b/pkg/notify/models/robot.go index 86c7bee793..eaae0fc786 100644 --- a/pkg/notify/models/robot.go +++ b/pkg/notify/models/robot.go @@ -60,13 +60,15 @@ type SRobot struct { db.SSharableVirtualResourceBase db.SEnabledResourceBase - Type string `width:"16" nullable:"false" create:"required" get:"user" list:"user" index:"true"` - Address string `nullable:"false" create:"required" update:"user" get:"user" list:"user"` - Lang string `width:"16" nullable:"false" create:"required" update:"user" get:"user" list:"user"` - Header jsonutils.JSONObject `length:"long" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"` - Body jsonutils.JSONObject `length:"long" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"` - MsgKey string `width:"16" nullable:"true" update:"user" get:"user" list:"user"` - UseTemplate tristate.TriState `default:"false" list:"domain" update:"user" create:"admin_optional"` + Type string `width:"16" nullable:"false" create:"required" get:"user" list:"user" index:"true"` + Address string `nullable:"false" create:"required" update:"user" get:"user" list:"user"` + Lang string `width:"16" nullable:"false" create:"required" update:"user" get:"user" list:"user"` + Header jsonutils.JSONObject `length:"long" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"` + Body jsonutils.JSONObject `length:"long" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"` + MsgKey string `width:"16" nullable:"true" update:"user" get:"user" list:"user"` + // webhook 签名加密 + SecretKey string `width:"128" nullable:"true" update:"user"` + UseTemplate tristate.TriState `default:"false" list:"domain" update:"user" create:"admin_optional"` } var RobotList = []string{api.FEISHU_ROBOT, api.DINGTALK_ROBOT, api.WORKWX_ROBOT, api.WEBHOOK, api.WEBHOOK_ROBOT} @@ -98,11 +100,12 @@ func (rm *SRobotManager) ValidateCreateData(ctx context.Context, userCred mcclie Contact: input.Address, DomainId: input.ProjectDomainId, }, - Header: input.Header, - Body: input.Body, - MsgKey: input.MsgKey, - Title: "Validate", - Message: "This is a verification message, please ignore.", + Header: input.Header, + Body: input.Body, + MsgKey: input.MsgKey, + SecretKey: input.SecretKey, + Title: "Validate", + Message: "This is a verification message, please ignore.", }) if err != nil { if errors.ErrConnectRefused == errors.Cause(err) { @@ -158,11 +161,12 @@ func (r *SRobot) ValidateUpdateData(ctx context.Context, userCred mcclient.Token // check Address dirver := GetDriver(fmt.Sprintf("%s-robot", r.Type)) err := dirver.Send(ctx, api.SendParams{ - Header: input.Header, - Body: input.Body, - MsgKey: input.MsgKey, - Title: "Validate", - Message: "This is a verification message, please ignore.", + Header: input.Header, + Body: input.Body, + MsgKey: input.MsgKey, + SecretKey: input.SecretKey, + Title: "Validate", + Message: "This is a verification message, please ignore.", Receivers: api.SNotifyReceiver{ Contact: input.Address, }, diff --git a/pkg/notify/sender/webhook.go b/pkg/notify/sender/webhook.go index 1dccb42e83..2d97d2c3b6 100644 --- a/pkg/notify/sender/webhook.go +++ b/pkg/notify/sender/webhook.go @@ -15,6 +15,9 @@ package sender import ( "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" "net/http" "strings" @@ -35,6 +38,15 @@ func (self *SWebhookSender) GetSenderType() string { return api.WEBHOOK_ROBOT } +func GenerateHMACSignature(payload []byte, secret string) string { + // 创建HMAC哈希器 + h := hmac.New(sha256.New, []byte(secret)) + // 写入要签名的数据 + h.Write(payload) + // 计算哈希值并转为16进制字符串 + return hex.EncodeToString(h.Sum(nil)) +} + func (self *SWebhookSender) Send(ctx context.Context, args api.SendParams) error { dict := jsonutils.NewDict() header := http.Header{} @@ -75,6 +87,11 @@ func (self *SWebhookSender) Send(ctx context.Context, args api.SendParams) error } } + if len(args.SecretKey) > 0 { + signature := GenerateHMACSignature([]byte(jsonutils.Marshal(dict).String()), args.SecretKey) + header.Set(api.WEBHOOK_SIGNATURE_HEADER, signature) + } + _, _, err := httputils.JSONRequest(cli, ctx, httputils.POST, args.Receivers.Contact, header, dict, false) return errors.Wrap(err, "webhook send") } diff --git a/pkg/notify/tasks/notifications_send_task.go b/pkg/notify/tasks/notifications_send_task.go index 41ad5a6eab..bcd493ac19 100644 --- a/pkg/notify/tasks/notifications_send_task.go +++ b/pkg/notify/tasks/notifications_send_task.go @@ -265,6 +265,7 @@ func (notificationSendTask *NotificationSendTask) batchSend(ctx context.Context, params.Header = robot.Header params.Body = robot.Body params.MsgKey = robot.MsgKey + params.SecretKey = robot.SecretKey params.GroupTimes = uint(receivers[i].rNotificaion.GroupTimes) err = driver.Send(ctx, params) if err != nil {