From 66e28b35e116fcfcd1e506ee8152a8b725ebb7a5 Mon Sep 17 00:00:00 2001 From: samwaf Date: Tue, 11 Oct 2022 16:55:11 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=A7=84=E5=88=99=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- localserver.go | 8 +-- main.go | 23 +++++-- model/rule_info_json.go | 4 +- test/Grule_test.go | 123 +++++++++++++++++++++++++++++++++++ test/TutorialExample_test.go | 1 + test/ruleinfotest_test.go | 2 +- utils/rulehelper.go | 37 +++++++++++ wafengine.go | 69 ++++++++++++-------- 8 files changed, 226 insertions(+), 41 deletions(-) create mode 100644 test/Grule_test.go diff --git a/localserver.go b/localserver.go index 1e1ff1d..00b66d7 100644 --- a/localserver.go +++ b/localserver.go @@ -362,13 +362,13 @@ func StartLocalServer() { HostCode: rule_info.RuleBase.RuleDomainCode, //网站CODE RuleCode: rule_code, RuleName: rulename, - RuleContent: rule_tool.GenRuleInfo(rule_info), + RuleContent: rule_tool.GenRuleInfo(rule_info, rulename), RuleContentJSON: waf_rule_add_req.RuleJson, //TODO 后续考虑是否应该再从结构转一次 RuleVersionName: "初版", - RuleVersion: 0, + RuleVersion: 1, UserCode: global.GWAF_USER_CODE, IsPublicRule: 0, - RuleStatus: "1", + RuleStatus: 1, } //waf_host_add_req.USER_CODE = global.GWAF_LOCAL_DB.Debug().Create(waf_rule) @@ -425,7 +425,7 @@ func StartLocalServer() { ruleMap := map[string]interface{}{ "HostCode": rule_info.RuleBase.RuleDomainCode, //TODO 注意字典名称 "RuleName": rule_name, - "RuleContent": rule_tool.GenRuleInfo(rule_info), + "RuleContent": rule_tool.GenRuleInfo(rule_info, rule_name), "RuleContentJSON": waf_rule_edit_req.RuleJson, //TODO 后续考虑是否应该再从结构转一次 "RuleVersionName": "初版", "RuleVersion": rule.RuleVersion + 1, diff --git a/main.go b/main.go index 216d29f..46ea1b4 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "SamWaf/model" "log" "net/http" + "strconv" "time" ) @@ -43,13 +44,21 @@ func main() { log.Println("引擎已关闭,放弃提取规则") continue } - var ruleconfig model.Rules - global.GWAF_LOCAL_DB.Debug().Where("host_code = ? and user_code=? and rule_status= 1 ", code, global.GWAF_USER_CODE).Find(&ruleconfig) - if ruleconfig.RuleVersion > hostTarget[host].RuleData.RuleVersion { - //说明该code有更新 - hostRuleChan <- ruleconfig + var vcnt int + global.GWAF_LOCAL_DB.Debug().Model(&model.Rules{}).Where("host_code = ? and user_code=? ", + code, global.GWAF_USER_CODE).Select("sum(rule_version) as vcnt").Row().Scan(&vcnt) + log.Println("主机host" + code + " 版本" + strconv.Itoa(vcnt)) + var ruleconfig []model.Rules + if vcnt > 0 { + global.GWAF_LOCAL_DB.Debug().Where("host_code = ? and user_code=? ", code, global.GWAF_USER_CODE).Find(&ruleconfig) + if vcnt > hostTarget[host].RuleVersionSum { + hostTarget[host].RuleVersionSum = vcnt + //说明该code有更新 + hostRuleChan <- ruleconfig + } } + } time.Sleep(10 * time.Second) // 10s重新读取一次 @@ -60,8 +69,8 @@ func main() { select { case remoteConfig := <-hostRuleChan: //TODO 需要把删除的那部分数据从数据口里面去掉 - hostTarget[hostCode[remoteConfig.HostCode]].RuleData = remoteConfig - hostTarget[hostCode[remoteConfig.HostCode]].Rule.LoadRule(remoteConfig) + hostTarget[hostCode[remoteConfig[0].HostCode]].RuleData = remoteConfig + hostTarget[hostCode[remoteConfig[0].HostCode]].Rule.LoadRules(remoteConfig) log.Println(remoteConfig) break diff --git a/model/rule_info_json.go b/model/rule_info_json.go index f22d526..196ecf0 100644 --- a/model/rule_info_json.go +++ b/model/rule_info_json.go @@ -70,7 +70,7 @@ func (receiver *RuleTool) GenRuleToFront(rule RuleInfo) { }*/ //生成实际可使用的规则字符串 -func (receiver *RuleTool) GenRuleInfo(rule RuleInfo) string { +func (receiver *RuleTool) GenRuleInfo(rule RuleInfo, remark string) string { var conditionTpl = "" for _, condition := range rule.RuleConditionDetail.RelationDetail { @@ -120,7 +120,7 @@ func (receiver *RuleTool) GenRuleInfo(rule RuleInfo) string { } var dev = map[string]string{ "rule_name": rule.RuleBase.RuleName, - "rule_remark": "RuleRemark", + "rule_remark": remark, "rule_salience": strconv.Itoa(rule.RuleBase.Salience), "rule_condition": conditionTpl, "rule_action": do_assignmentTpl + do_methodTpl, diff --git a/test/Grule_test.go b/test/Grule_test.go new file mode 100644 index 0000000..343d93b --- /dev/null +++ b/test/Grule_test.go @@ -0,0 +1,123 @@ +package test + +import ( + "github.com/hyperjumptech/grule-rule-engine/ast" + "github.com/hyperjumptech/grule-rule-engine/builder" + "github.com/hyperjumptech/grule-rule-engine/engine" + "github.com/hyperjumptech/grule-rule-engine/pkg" + "github.com/stretchr/testify/assert" + "log" + "testing" + "time" +) + +func TestGrule(t *testing.T) { + myFact := &MyFact{ + IntAttribute: 123, + StringAttribute: "Some string value", + BooleanAttribute: true, + FloatAttribute: 1.234, + TimeAttribute: time.Now(), + } + dataCtx := ast.NewDataContext() + err := dataCtx.Add("MF", myFact) + assert.NoError(t, err) + + // Prepare knowledgebase library and load it with our rule. + knowledgeLibrary := ast.NewKnowledgeLibrary() + ruleBuilder := builder.NewRuleBuilder(knowledgeLibrary) + + drls := ` +rule CheckValues1 "Check the default values1牛啊" salience 10 { + when + MF.IntAttribute == 1231 && MF.StringAttribute == "Some string value" + then + MF.WhatToSay = MF.GetWhatToSay("你好"); +} +rule CheckValues2 "Check the default values2绝壁" salience 10 { + when + MF.IntAttribute > 120 + then + MF.WhatToSay = MF.GetWhatToSay("你好1"); +} +` + + byteArr := pkg.NewBytesResource([]byte(drls)) + err = ruleBuilder.BuildRuleFromResource("Tutorial", "0.0.1", byteArr) + assert.NoError(t, err) + + knowledgeBase := knowledgeLibrary.NewKnowledgeBaseInstance("Tutorial", "0.0.1") + + my_engine := engine.NewGruleEngine() + processType := "match" + if processType == "match" { + matchrules, err := my_engine.FetchMatchingRules(dataCtx, knowledgeBase) + if err != nil { + log.Fatal(err) + } else { + log.Println(myFact.WhatToSay) + log.Println(len(matchrules)) + for _, v := range matchrules { + log.Println(v.RuleName) + + log.Println(v.RuleDescription) + } + } + } else { + err := my_engine.Execute(dataCtx, knowledgeBase) + if err != nil { + log.Fatal(err) + } + } + + //模拟动态新加的 + knowledgeLibrary = ast.NewKnowledgeLibrary() + ruleBuilder = builder.NewRuleBuilder(knowledgeLibrary) + drls = ` +rule CheckValues1 "Check the default values1" salience 10 { + when + MF.IntAttribute == 1231 && MF.StringAttribute == "Some string value" + then + MF.WhatToSay = MF.GetWhatToSay("你好"); +} +rule CheckValues2 "Check the default values2" salience 10 { + when + MF.IntAttribute > 120 + then + MF.WhatToSay = MF.GetWhatToSay("你好1"); +} +rule CheckValues3 "nihao" salience 10 { + when + MF.IntAttribute > 0 + then + MF.WhatToSay = MF.GetWhatToSay("你好1"); +} +` + + byteArr = pkg.NewBytesResource([]byte(drls)) + err = ruleBuilder.BuildRuleFromResource("Tutorial", "0.0.1", byteArr) + assert.NoError(t, err) + + knowledgeBase = knowledgeLibrary.NewKnowledgeBaseInstance("Tutorial", "0.0.1") + + my_engine = engine.NewGruleEngine() + processType = "match" + if processType == "match" { + matchrules, err := my_engine.FetchMatchingRules(dataCtx, knowledgeBase) + if err != nil { + log.Fatal(err) + } else { + log.Println(myFact.WhatToSay) + log.Println(len(matchrules)) + for _, v := range matchrules { + log.Println(v.RuleName) + } + } + } else { + err := my_engine.Execute(dataCtx, knowledgeBase) + if err != nil { + log.Fatal(err) + } + } + +} diff --git a/test/TutorialExample_test.go b/test/TutorialExample_test.go index 69e08e8..1a3f890 100644 --- a/test/TutorialExample_test.go +++ b/test/TutorialExample_test.go @@ -22,6 +22,7 @@ type MyFact struct { } func (mf *MyFact) GetWhatToSay(sentence string) string { + log.Println("Let say " + sentence) return fmt.Sprintf("Let say \"%s\"", sentence) } diff --git a/test/ruleinfotest_test.go b/test/ruleinfotest_test.go index 3557557..76a26a1 100644 --- a/test/ruleinfotest_test.go +++ b/test/ruleinfotest_test.go @@ -19,5 +19,5 @@ func TestJsonTest(t *testing.T) { } fmt.Printf("反序列化后 RuleName=%v RuleDomainCode=%v \n", ruleInfo.RuleBase.RuleName, ruleInfo.RuleBase.RuleDomainCode) - fmt.Println(ruleTool.GenRuleInfo(ruleInfo)) + fmt.Println(ruleTool.GenRuleInfo(ruleInfo, "规则触发了")) } diff --git a/utils/rulehelper.go b/utils/rulehelper.go index 0abda02..74d4aa8 100644 --- a/utils/rulehelper.go +++ b/utils/rulehelper.go @@ -41,6 +41,36 @@ func (rulehelper *RuleHelper) LoadRule(ruleconfig model.Rules) { rulehelper.engine = engine.NewGruleEngine() } + +func (rulehelper *RuleHelper) LoadRules(ruleconfig []model.Rules) string { + + knowledgeLibrary := ast.NewKnowledgeLibrary() + ruleBuilder := builder.NewRuleBuilder(knowledgeLibrary) + + /*drls = ` + rule CheckRegionNotChina "CheckRegionNotChina" salience 10 { + when + fact.SRC_INFO.CONTENT_LENGTH == 0 && fact.SRC_INFO.HOST == "mybaidu1.com:8081" + then + fact.ExecResult = 1; + Retract("CheckRegionNotChina"); + } + `*/ + rulestr := "" + for _, v := range ruleconfig { + rulestr = rulestr + v.RuleContent + " \n" + } + byteArr := pkg.NewBytesResource([]byte(rulestr)) + err := ruleBuilder.BuildRuleFromResource("Region", "0.0.1", byteArr) + if err != nil { + log.Fatal(err) + } + + rulehelper.knowledgeBase = knowledgeLibrary.NewKnowledgeBaseInstance("Region", "0.0.1") + + rulehelper.engine = engine.NewGruleEngine() + return rulestr +} func (rulehelper *RuleHelper) Exec(key string, ruleinfo *innerbean.WAF_REQUEST_FULL) error { //rulehelper.dataCtx = ast.NewDataContext() @@ -54,3 +84,10 @@ func (rulehelper *RuleHelper) Exec(key string, ruleinfo *innerbean.WAF_REQUEST_F } return err } + +func (rulehelper *RuleHelper) Match(key string, ruleinfo *innerbean.WAF_REQUEST_FULL) ([]*ast.RuleEntry, error) { + + dataCtx := ast.NewDataContext() + dataCtx.Add(key, ruleinfo) + return rulehelper.engine.FetchMatchingRules(dataCtx, rulehelper.knowledgeBase) +} diff --git a/wafengine.go b/wafengine.go index 8bf8da4..3763154 100644 --- a/wafengine.go +++ b/wafengine.go @@ -26,10 +26,11 @@ import ( // 主机安全配置 type HostSafe struct { - RevProxy *httputil.ReverseProxy - Rule utils.RuleHelper - TargetHost string - RuleData model.Rules + RevProxy *httputil.ReverseProxy + Rule utils.RuleHelper + TargetHost string + RuleData []model.Rules + RuleVersionSum int //规则版本的汇总 通过这个来进行版本动态加载 } var ( @@ -46,9 +47,9 @@ var ( esHelper utils.EsHelper phttphandler *baseHandle - hostRuleChan = make(chan model.Rules, 10) //规则链 - engineChan = make(chan int, 10) //引擎链 - engineCurrentStatus int = 0 // 当前waf引擎状态 + hostRuleChan = make(chan []model.Rules, 10) //规则链 + engineChan = make(chan int, 10) //引擎链 + engineCurrentStatus int = 0 // 当前waf引擎状态 ) type baseHandle struct{} @@ -108,7 +109,7 @@ func (h *baseHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) { } }() // 获取请求报文的内容长度 - len := r.ContentLength + contentLength := r.ContentLength //server_online[8081].Svr.Close() var bodyByte []byte @@ -134,7 +135,7 @@ func (h *baseHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) { SRC_IP: ip_and_port[0], SRC_PORT: ip_and_port[1], CREATE_TIME: time.Now().Format("2006-01-02 15:04:05"), - CONTENT_LENGTH: len, + CONTENT_LENGTH: contentLength, COOKIES: string(cookies), BODY: string(bodyByte), REQ_UUID: uuid.NewV4().String(), @@ -146,20 +147,27 @@ func (h *baseHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) { SRC_INFO: weblogbean, ExecResult: 0, } - hostTarget[host].Rule.Exec("fact", rule) - if rule.ExecResult == 1 { + rule_matchs, err := hostTarget[host].Rule.Match("fact", rule) + if err == nil { + if len(rule_matchs) > 0 { - waflogbean := innerbean.WAFLog{ - CREATE_TIME: time.Now().Format("2006-01-02 15:04:05"), - RULE: hostTarget[host].RuleData.RuleName, - REQ_UUID: uuid.NewV4().String(), + rulestr := "" + for _, v := range rule_matchs { + rulestr = rulestr + v.RuleDescription + "," + } + waflogbean := innerbean.WAFLog{ + CREATE_TIME: time.Now().Format("2006-01-02 15:04:05"), + RULE: rulestr, + REQ_UUID: uuid.NewV4().String(), + } + esHelper.BatchInsertWAF("web_log", waflogbean) + log.Println("no china") + w.Header().Set("WAF", "SAMWAF DROP") + w.Write([]byte(" no china " + host)) + return } - esHelper.BatchInsertWAF("web_log", waflogbean) - log.Println("no china") - w.Header().Set("WAF", "SAMWAF DROP") - w.Write([]byte(" no china " + host)) - return } + // 取出代理ip // 直接从缓存取出 @@ -273,15 +281,22 @@ func Start_WAF() { ruleHelper := utils.RuleHelper{} //查询规则 - var ruleconfig model.Rules - global.GWAF_LOCAL_DB.Debug().Where("code = ? and user_code=? ", hosts[i].Code, global.GWAF_USER_CODE).Find(&ruleconfig) - ruleHelper.LoadRule(ruleconfig) + var vcnt int + global.GWAF_LOCAL_DB.Debug().Model(&model.Rules{}).Where("host_code = ? and user_code=? ", + hosts[i].Code, global.GWAF_USER_CODE).Select("sum(rule_version) as vcnt").Row().Scan(&vcnt) + log.Println("主机host" + hosts[i].Code + " 版本" + strconv.Itoa(vcnt)) + var ruleconfigs []model.Rules + if vcnt > 0 { + global.GWAF_LOCAL_DB.Debug().Where("code = ? and user_code=? ", hosts[i].Code, global.GWAF_USER_CODE).Find(&ruleconfigs) + ruleHelper.LoadRules(ruleconfigs) + } hostsafe := &HostSafe{ - RevProxy: nil, - Rule: ruleHelper, - TargetHost: hosts[i].Remote_host + ":" + strconv.Itoa(hosts[i].Remote_port), - RuleData: ruleconfig, + RevProxy: nil, + Rule: ruleHelper, + TargetHost: hosts[i].Remote_host + ":" + strconv.Itoa(hosts[i].Remote_port), + RuleData: ruleconfigs, + RuleVersionSum: vcnt, } //赋值到白名单里面 hostTarget[hosts[i].Host+":"+strconv.Itoa(hosts[i].Port)] = hostsafe