diff --git a/api/waf_login.go b/api/waf_login.go index 3781711..ef9548d 100644 --- a/api/waf_login.go +++ b/api/waf_login.go @@ -41,10 +41,9 @@ func (w *WafLoginApi) LoginApi(c *gin.Context) { //通知信息 noticeStr := fmt.Sprintf("登录IP:%s 归属地区:%s", c.ClientIP(), utils.GetCountry(c.ClientIP())) - global.GQEQUE_MESSAGE_DB.PushBack(innerbean.MessageInfo{ - Title: "登录信息", - Content: noticeStr, - Remarks: "无", + global.GQEQUE_MESSAGE_DB.PushBack(innerbean.OperatorMessageInfo{ + BaseMessageInfo: innerbean.BaseMessageInfo{OperaType: "登录信息"}, + OperaCnt: noticeStr, }) response.OkWithDetailed(response2.LoginRep{ diff --git a/innerbean/message_info.go b/innerbean/message_info.go index cc71aab..702e3b6 100644 --- a/innerbean/message_info.go +++ b/innerbean/message_info.go @@ -1,7 +1,85 @@ package innerbean -type MessageInfo struct { - Title string `json:"title"` - Content string `json:"content"` - Remarks string `json:"remarks"` +import ( + "SamWaf/wechat" +) + +type BaseInfo interface { + ToFormat() string +} + +/*** + +信息类型: +服务器: +IP地址: + +*/ + +type BaseMessageInfo struct { + OperaType string `json:"operatype"` + Server string `json:"server"` +} + +/* +** +域名信息: +触发规则: +*/ +type RuleMessageInfo struct { + BaseMessageInfo + Domain string `json:"domain"` + RuleInfo string `json:"ruleinfo"` + Ip string `json:"ip"` +} + +func (r RuleMessageInfo) ToFormat() map[string]*wechat.DataItem { + Data := map[string]*wechat.DataItem{} + Data["domain"] = &wechat.DataItem{ + Value: r.Domain, + Color: "#808080", + } + Data["ruleinfo"] = &wechat.DataItem{ + Value: r.RuleInfo, + Color: "#808080", + } + Data["ip"] = &wechat.DataItem{ + Value: r.Ip, + Color: "#808080", + } + Data["server"] = &wechat.DataItem{ + Value: r.Server, + Color: "#808080", + } + Data["operatype"] = &wechat.DataItem{ + Value: r.OperaType, + Color: "#808080", + } + return Data +} + +func (r OperatorMessageInfo) ToFormat() map[string]*wechat.DataItem { + Data := map[string]*wechat.DataItem{} + Data["operacnt"] = &wechat.DataItem{ + Value: r.OperaCnt, + Color: "#808080", + } + Data["server"] = &wechat.DataItem{ + Value: r.Server, + Color: "#808080", + } + Data["operatype"] = &wechat.DataItem{ + Value: r.OperaType, + Color: "#808080", + } + return Data +} + +/* +** +信息内容: +*/ +type OperatorMessageInfo struct { + BaseMessageInfo + OperaCnt string `json:"operacnt"` } diff --git a/utils/notifyhelper.go b/utils/notifyhelper.go index e0fb193..816fc0a 100644 --- a/utils/notifyhelper.go +++ b/utils/notifyhelper.go @@ -2,9 +2,9 @@ package utils import ( "SamWaf/global" + "SamWaf/innerbean" "SamWaf/utils/zlog" "SamWaf/wechat" - "fmt" ) type NotifyHelper struct { @@ -12,14 +12,39 @@ type NotifyHelper struct { var NotifyHelperApp = new(NotifyHelper) -func (receiver *NotifyHelper) SendInfo(title string, content string, remarks string) { +func (receiver *NotifyHelper) SendRuleInfo(ruleMessageInfo innerbean.RuleMessageInfo) { if global.GCACHE_WECHAT_ACCESS == "" { zlog.Error("未初始化wechat") return } - content = fmt.Sprintf("服务器:%s ", global.GWAF_CUSTOM_SERVER_NAME) + content + ruleMessageInfo.Server = global.GWAF_CUSTOM_SERVER_NAME + //content = fmt.Sprintf("服务器:%s ", global.GWAF_CUSTOM_SERVER_NAME) + content tm, err := wechat.BuildTemplateMessage("oUBYM65NIBB2_QhApG7DgOl6A0FU", - "E0nquOCrrTcMQr6BsyhBraEq6-KukkjD5ZblpPbCcsg", title, content, remarks) + "RbQR70NpwkVHMLiN_jBr4Swccxu_FuzODuTBNK2DX1w", ruleMessageInfo.ToFormat()) + if err != nil { + zlog.Error("构造失败", err) + } else { + ptm, ptmerr := wechat.PushTemplateMessage(global.GCACHE_WECHAT_ACCESS, tm) + if ptmerr != nil { + zlog.Error("推送失败", ptmerr) + } else if ptm.ErrCode != 0 { + // 微信服务器返回错误 + zlog.Info("Error occurred when pushing message: " + ptm.ErrMsg) + } else { + zlog.Info("推送成功") + } + } +} + +func (receiver *NotifyHelper) SendNoticeInfo(operatorMessageInfo innerbean.OperatorMessageInfo) { + if global.GCACHE_WECHAT_ACCESS == "" { + zlog.Error("未初始化wechat") + return + } + operatorMessageInfo.Server = global.GWAF_CUSTOM_SERVER_NAME + //content = fmt.Sprintf("服务器:%s ", global.GWAF_CUSTOM_SERVER_NAME) + content + tm, err := wechat.BuildTemplateMessage("oUBYM65NIBB2_QhApG7DgOl6A0FU", + "I4-QACokwNr-v1tM64_E2UwUFtIkSW3v_xa9PocP21I", operatorMessageInfo.ToFormat()) if err != nil { zlog.Error("构造失败", err) } else { @@ -33,5 +58,4 @@ func (receiver *NotifyHelper) SendInfo(title string, content string, remarks str zlog.Info("推送成功") } } - } diff --git a/wafenginecore/dequeengine.go b/wafenginecore/dequeengine.go index 40ed767..f1a307d 100644 --- a/wafenginecore/dequeengine.go +++ b/wafenginecore/dequeengine.go @@ -31,7 +31,7 @@ func ProcessDequeEngine() { defer func() { e := recover() if e != nil { - zlog.Error("ProcessErrorException", e) + zlog.Info("ProcessErrorException", e) } }() for !global.GQEQUE_DB.Empty() { @@ -49,28 +49,38 @@ func ProcessDequeEngine() { } for !global.GQEQUE_MESSAGE_DB.Empty() { - messageinfo := global.GQEQUE_MESSAGE_DB.PopFront().(innerbean.MessageInfo) - utils.NotifyHelperApp.SendInfo(messageinfo.Title, messageinfo.Content, messageinfo.Remarks) - if messageinfo.Title == "命中保护规则" { - //发送websocket - for _, ws := range global.GWebSocket { - if ws != nil { - //写入ws数据 - msgBytes, err := json.Marshal(model.MsgPacket{ - MessageId: uuid.NewV4().String(), - MessageType: "命中保护规则", - MessageData: messageinfo.Content, - MessageAttach: nil, - MessageDateTime: time.Now().Format("2006-01-02 15:04:05"), - MessageUnReadStatus: true, - }) - err = ws.WriteMessage(1, msgBytes) - if err != nil { - continue + messageinfo := global.GQEQUE_MESSAGE_DB.PopFront().(interface{}) + switch messageinfo.(type) { + case innerbean.RuleMessageInfo: + rulemessage := messageinfo.(innerbean.RuleMessageInfo) + utils.NotifyHelperApp.SendRuleInfo(rulemessage) + if rulemessage.BaseMessageInfo.OperaType == "命中保护规则" { + //发送websocket + for _, ws := range global.GWebSocket { + if ws != nil { + //写入ws数据 + msgBytes, err := json.Marshal(model.MsgPacket{ + MessageId: uuid.NewV4().String(), + MessageType: "命中保护规则", + MessageData: rulemessage.RuleInfo + rulemessage.Ip, + MessageAttach: nil, + MessageDateTime: time.Now().Format("2006-01-02 15:04:05"), + MessageUnReadStatus: true, + }) + err = ws.WriteMessage(1, msgBytes) + if err != nil { + continue + } } } } + break + case innerbean.OperatorMessageInfo: + operatorMessage := messageinfo.(innerbean.OperatorMessageInfo) + utils.NotifyHelperApp.SendNoticeInfo(operatorMessage) + break } + //zlog.Info("MESSAGE", messageinfo) } time.Sleep((100 * time.Millisecond)) diff --git a/wafenginecore/wafengine.go b/wafenginecore/wafengine.go index 523b18b..a2f0fd8 100644 --- a/wafenginecore/wafengine.go +++ b/wafenginecore/wafengine.go @@ -289,10 +289,11 @@ func EchoErrorInfo(w http.ResponseWriter, r *http.Request, weblogbean innerbean. zlog.Debug(noticeStr) //发送微信推送消息 - global.GQEQUE_MESSAGE_DB.PushBack(innerbean.MessageInfo{ - Title: "命中保护规则", - Content: noticeStr, - Remarks: "无", + global.GQEQUE_MESSAGE_DB.PushBack(innerbean.RuleMessageInfo{ + BaseMessageInfo: innerbean.BaseMessageInfo{OperaType: "命中保护规则", Server: global.GWAF_CUSTOM_SERVER_NAME}, + Domain: weblogbean.HOST, + RuleInfo: ruleName, + Ip: fmt.Sprintf("%s (%s)", weblogbean.SRC_IP, utils.GetCountry(weblogbean.SRC_IP)), }) weblogbean.RULE = ruleName weblogbean.ACTION = "阻止" diff --git a/waftask/localtaskcounter.go b/waftask/localtaskcounter.go index 4956ccf..216a125 100644 --- a/waftask/localtaskcounter.go +++ b/waftask/localtaskcounter.go @@ -145,10 +145,9 @@ func TaskStatusNotify() { if err == nil { noticeStr := fmt.Sprintf("今日访问量:%d 今天恶意访问量:%d 昨日恶意访问量:%d", statHomeInfo.VisitCountOfToday, statHomeInfo.AttackCountOfToday, statHomeInfo.AttackCountOfYesterday) - global.GQEQUE_MESSAGE_DB.PushBack(innerbean.MessageInfo{ - Title: "汇总通知", - Content: noticeStr, - Remarks: "无", + global.GQEQUE_MESSAGE_DB.PushBack(innerbean.OperatorMessageInfo{ + BaseMessageInfo: innerbean.BaseMessageInfo{OperaType: "汇总通知"}, + OperaCnt: noticeStr, }) } else { zlog.Error("TaskStatusNotifyerror", err) diff --git a/wechat/template_message.go b/wechat/template_message.go index 0ec5af0..d3f6529 100644 --- a/wechat/template_message.go +++ b/wechat/template_message.go @@ -9,20 +9,21 @@ import ( ) type TemplateMessage struct { - ToUser string `json:"touser"` - TemplateId string `json:"template_id"` - Data Content `json:"data"` + ToUser string `json:"touser"` // 必须, 接受者OpenID + TemplateID string `json:"template_id"` // 必须, 模版ID + URL string `json:"url,omitempty"` // 可选, 用户点击后跳转的URL, 该URL必须处于开发者在公众平台网站中设置的域中 + Color string `json:"color,omitempty"` // 可选, 整个消息的颜色, 可以不设置 + Data map[string]*DataItem `json:"data"` // 必须, 模板数据 + MiniProgram struct { + AppID string `json:"appid"` //所需跳转到的小程序appid(该小程序appid必须与发模板消息的公众号是绑定关联关系) + PagePath string `json:"pagepath"` //所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar) + } `json:"miniprogram"` //可选,跳转至小程序地址 } -type Content struct { - From Item `json:"from"` - Description Item `json:"description"` - Remark Item `json:"remark"` -} - -type Item struct { +// DataItem 模版内某个 .DATA 的值 +type DataItem struct { Value string `json:"value"` - Color string `json:"color"` + Color string `json:"color,omitempty"` } type TemplateResponse struct { @@ -31,26 +32,12 @@ type TemplateResponse struct { MsgId int `json:"msgid"` } -func BuildTemplateMessage(receiverId, templateId, from, description, remark string) (templateMessage []byte, err error) { +func BuildTemplateMessage(receiverId, templateId string, dataItems map[string]*DataItem) (templateMessage []byte, err error) { t := TemplateMessage{ ToUser: receiverId, - TemplateId: templateId, - Data: Content{ - From: Item{ - Value: from, - Color: "#808080", - }, - Description: Item{ - Value: description, - Color: "#000000", - }, - Remark: Item{ - Value: remark, - Color: "#808080", - }, - }, + TemplateID: templateId, + Data: dataItems, } - // serialize struct to json string templateMessage, err = json.Marshal(t) if err != nil { diff --git a/编译说明.md b/编译说明.md index d4d1a9b..c2aaf79 100644 --- a/编译说明.md +++ b/编译说明.md @@ -8,11 +8,11 @@ go-bindata-assetfs.exe -o=vue/vue.go -pkg=vue vue/dist/... # linux //生成普通发行版(未upx压缩的) -docker run --rm -v "$PWD":/media/sf_SamWaf -w /media/sf_SamWaf -e CGO_ENABLED=1 -e GOPROXY=https://goproxy.cn,direct golang:1.19 go build -v -ldflags="-X SamWaf/global.GWAF_RELEASE=true -X SamWaf/global.GWAF_RELEASE_VERSION_NAME=202300601 -X SamWaf/global.GWAF_RELEASE_VERSION=100 -s -w -extldflags "-static"" -o /media/sf_SamWaf/release/SamWafLinux64 main.go +docker run --rm -v "$PWD":/media/sf_SamWaf -w /media/sf_SamWaf -e CGO_ENABLED=1 -e GOPROXY=https://goproxy.cn,direct golang:1.19 go build -v -ldflags="-X SamWaf/global.GWAF_RELEASE=true -X SamWaf/global.GWAF_RELEASE_VERSION_NAME=20230604 -X SamWaf/global.GWAF_RELEASE_VERSION=100 -s -w -extldflags "-static"" -o /media/sf_SamWaf/release/SamWafLinux64 main.go //可调试的centos -docker run --rm -v "$PWD":/media/sf_SamWaf -w /media/sf_SamWaf -e CGO_ENABLED=1 -e GOPROXY=https://goproxy.cn,direct golang:1.19 go build -v -ldflags="-X SamWaf/global.GWAF_RELEASE=true -X SamWaf/global.GWAF_RELEASE_VERSION_NAME=20230530 -X SamWaf/global.GWAF_RELEASE_VERSION=100 -s -w -extldflags "-static"" -o /media/sf_SamWaf/release/SamWafLinux64 main.go +docker run --rm -v "$PWD":/media/sf_SamWaf -w /media/sf_SamWaf -e CGO_ENABLED=1 -e GOPROXY=https://goproxy.cn,direct golang:1.19 go build -v -ldflags="-X SamWaf/global.GWAF_RELEASE=true -X SamWaf/global.GWAF_RELEASE_VERSION_NAME=20230604 -X SamWaf/global.GWAF_RELEASE_VERSION=100 -s -w -extldflags "-static"" -o /media/sf_SamWaf/release/SamWafLinux64 main.go //可调试的local ubuntu docker run --rm -v "$PWD":/home/ubuntu/samwaf -w /home/ubuntu/samwaf -e CGO_ENABLED=1 -e GOPROXY=https://goproxy.cn,direct golang:1.19 go build -v -ldflags="-extldflags "-static"" -o /home/ubuntu/samwaf/release/SamWafLinux64.exe main.go