diff --git a/README.MD b/README.MD index f0bb2c20..09fe5abe 100644 --- a/README.MD +++ b/README.MD @@ -53,7 +53,7 @@ make docker/docker-push/docker-test # 本地测试运行 make clean && make build copy conf/app-example.conf conf/app.conf -build/bin/PrometheusAlert +./PrometheusAlert # 测试应用健康 curl http://localhost:8080/health @@ -136,6 +136,7 @@ feiyu563/prometheus-alert:latest - 增加支持飞书机器人应用。 - 增加告警组,可以将通知媒介写到告警组里面,便于配置和修改。 - 增加热加载配置接口 + - 增加钉钉加签 ------------------------------------- diff --git a/conf/app-example.conf b/conf/app-example.conf index fe8307a3..a7265ef5 100644 --- a/conf/app-example.conf +++ b/conf/app-example.conf @@ -69,6 +69,9 @@ open-dingding=1 ddurl=https://oapi.dingtalk.com/robot/send?access_token=xxxxx #是否开启 @所有人(0为关闭,1为开启) dd_isatall=1 +#是否开启钉钉机器人加签,0为关闭,1为开启 +# 使用方法:https://oapi.dingtalk.com/robot/send?access_token=XXXXXX&secret=mysecret +open-dingding-secret=0 #是否开启微信告警通道,可同时开始多个通道0为关闭,1为开启 open-weixin=1 diff --git a/controllers/dingding.go b/controllers/dingding.go index ebb00239..04db30c8 100644 --- a/controllers/dingding.go +++ b/controllers/dingding.go @@ -3,14 +3,20 @@ package controllers import ( "PrometheusAlert/models" "bytes" + "crypto/hmac" + "crypto/sha256" "crypto/tls" + "encoding/base64" "encoding/json" - "github.com/astaxie/beego" - "github.com/astaxie/beego/logs" - "io/ioutil" + "io" "net/http" "net/url" + "strconv" "strings" + "time" + + "github.com/astaxie/beego" + "github.com/astaxie/beego/logs" ) type DDMessage struct { @@ -31,6 +37,11 @@ func PostToDingDing(title, text, Ddurl, AtSomeOne, logsign string) string { logs.Info(logsign, "[dingding]", "钉钉接口未配置未开启状态,请先配置open-dingding为1") return "钉钉接口未配置未开启状态,请先配置open-dingding为1" } + // dingding sign + if openSecret := beego.AppConfig.String("open-dingding-secret"); openSecret == "1" { + Ddurl = dingdingSign(Ddurl) + } + Isatall, _ := beego.AppConfig.Int("dd_isatall") Atall := true if Isatall == 0 { @@ -82,7 +93,7 @@ func PostToDingDing(title, text, Ddurl, AtSomeOne, logsign string) string { logs.Error(logsign, "[dingding]", err.Error()) } defer res.Body.Close() - result, err := ioutil.ReadAll(res.Body) + result, err := io.ReadAll(res.Body) if err != nil { logs.Error(logsign, "[dingding]", err.Error()) } @@ -91,3 +102,41 @@ func PostToDingDing(title, text, Ddurl, AtSomeOne, logsign string) string { logs.Info(logsign, "[dingding]", string(result)) return string(result) } + +// dingdingSign adds sign and timestamp parms to dingding webhook url +// docs: https://open.dingtalk.com/document/orgapp/custom-bot-creation-and-installation +func dingdingSign(ddurl string) string { + timestamp := time.Now() + timestampMs := timestamp.UnixNano() / int64(time.Millisecond) + tsMsStr := strconv.FormatInt(timestampMs, 10) + // parse ddurl parms + u, err := url.Parse(ddurl) + if err != nil { + logs.Info("[dingdingSign]", "配置文件已开启钉钉加签,钉钉机器人地址解析加签参数 secret 失败,将使用不加签的地址!") + return ddurl + } + // get parm secret + queryParams := u.Query() + secret := queryParams.Get("secret") + if len(secret) == 0 { + logs.Info("[dingdingSign]", "配置文件已开启钉钉加签,钉钉机器人地址解析加签参数 secret 为空,将使用不加签的地址!") + return ddurl + } + // sign string + signStr := tsMsStr + "\n" + secret + // HmacSHA256 + key := []byte(secret) + h := hmac.New(sha256.New, key) + h.Write([]byte(signStr)) + signature := h.Sum(nil) + // Base64 + sign := base64.StdEncoding.EncodeToString(signature) + // splice url + delete(queryParams, "secret") + queryParams.Add("timestamp", tsMsStr) + queryParams.Add("sign", sign) + u.RawQuery = queryParams.Encode() + signURL := u.String() + + return signURL +} diff --git a/controllers/dingding_test.go b/controllers/dingding_test.go new file mode 100644 index 00000000..f07f297b --- /dev/null +++ b/controllers/dingding_test.go @@ -0,0 +1,42 @@ +package controllers + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDDMesage(t *testing.T) { + // A sample dingding message json payload + payload := ` + { + "msgtype": "markdown", + "markdown": { + "title": "AlertPrometheus告警测试", + "text": "**测试告警信息**\n" + }, + "at": { + "atMobiles": ["139xxxxxxxx", "159xxxxxxxx"], + "isAtAll": false + } + }` + message := DDMessage{} + err := json.Unmarshal([]byte(payload), &message) + assert.Nil(t, err) +} + +func TestDingdingSign(t *testing.T) { + var want, result string + + // url without parm secret + withoutURL := "https://oapi.dingtalk.com/robot/send?access_token=XXX" + want = withoutURL + result = dingdingSign(withoutURL) + assert.Equal(t, want, result) + + // url with parm secret + withURL := "https://oapi.dingtalk.com/robot/send?access_token=XXX&secret=mysecret" + result = dingdingSign(withURL) + t.Logf("一个示例的加签的地址:%s", result) +} diff --git a/doc/readme/conf-dingding.md b/doc/readme/conf-dingding.md index 72cb2f11..b8870143 100644 --- a/doc/readme/conf-dingding.md +++ b/doc/readme/conf-dingding.md @@ -1,8 +1,6 @@ # PrometheusAlert全家桶钉钉配置说明 ------------------ - - **开启钉钉机器人** +## 开启钉钉机器人 打开钉钉,进入钉钉群中,选择群设置-->智能群助手-->添加机器人-->自定义,可参下图: @@ -10,6 +8,8 @@ ![ding2](../images/dingding2.png) +
+ 新版本的钉钉加了安全设置,只需选择安全设置中的 自定义关键词 即可,并将关键词设置为 Prometheus或者app.conf中设置的title值均可,参考下图 ![ding3](../images/dingding3.png) @@ -18,12 +18,15 @@ 复制图中的Webhook地址,并填入PrometheusAlert配置文件app.conf中对应配置项即可。 +
+ **PS: 钉钉机器人目前已经支持 `@某人` ,使用该功能需要取得对应用户的钉钉关联手机号码,如下图:** + ![ding4](../images/dingding5.png) 钉钉目前支持只支持markdown语法的子集,具体支持的元素如下: -``` +```md 标题 # 一级标题 ## 二级标题 @@ -54,9 +57,38 @@ 2. item2 ``` -钉钉相关配置: +
+
+ +## 钉钉加签配置 + +增加钉钉加签的配置。创建自定义机器人的时候,选择加签,并复制加签内容组装到到机器人地址参数(webhook url parm secret)。程序会获取这个 `secret` 参数,进行加签处理之后生成对应的加签地址。 + +如果配置文件中启用了钉钉加签认证,但 webhook url 并没有传递 parm secret,那么程序将返回不加签的地址。也就是说,PrometheusAlert 配置中启用钉钉加签并不会影响不加签的机器人。 + +示例如下: +```conf +# 默认的钉钉机器人地址 webhook url 格式 +https://oapi.dingtalk.com/robot/send?access_token=XXX + +# 假如机器人加签内容为:mysecret +# 带上加签参数: secret=mysecret +https://oapi.dingtalk.com/robot/send?access_token=XXX&secret=mysecret + +# 程序加签处理后生成的加签地址 +https://oapi.dingtalk.com/robot/send?access_token=XXXXXX×tamp=XXX&sign=XXX + +# 如果配置文件中启用了钉钉加签认证,但 webhook url 并没有传递 parm secret,那么程序将返回不加签的地址 +https://oapi.dingtalk.com/robot/send?access_token=XXX ``` + +
+
+ +## 钉钉相关配置 + +```ini #---------------------↓全局配置----------------------- #告警消息标题 title=PrometheusAlert @@ -72,15 +104,21 @@ open-dingding=1 ddurl=https://oapi.dingtalk.com/robot/send?access_token=xxxxx #是否开启 @所有人(0为关闭,1为开启) dd_isatall=1 +#是否开启钉钉机器人加签,0为关闭,1为开启 +# 使用方法:https://oapi.dingtalk.com/robot/send?access_token=XXXXXX&secret=mysecret +open-dingding-secret=0 ``` -**如何使用** +
+
+ +## 如何使用 以Prometheus配合自定义模板为例: Prometheus配置参考: -``` +```yml global: resolve_timeout: 5m route: @@ -93,4 +131,4 @@ receivers: - name: 'web.hook.prometheusalert' webhook_configs: - url: 'http://[prometheusalert_url]:8080/prometheusalert?type=dd&tpl=prometheus-dd&ddurl=钉钉机器人地址,钉钉机器人地址2&at=18888888888,18888888889' -``` \ No newline at end of file +```