github.com/TeaOSLab/EdgeNode@v1.3.8/internal/waf/rule_set.go (about) 1 package waf 2 3 import ( 4 "fmt" 5 "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" 6 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 7 "github.com/TeaOSLab/EdgeNode/internal/utils" 8 "github.com/TeaOSLab/EdgeNode/internal/waf/requests" 9 "github.com/iwind/TeaGo/lists" 10 "github.com/iwind/TeaGo/maps" 11 "github.com/iwind/TeaGo/types" 12 "net/http" 13 "sort" 14 ) 15 16 type RuleConnector = string 17 18 const ( 19 RuleConnectorAnd = "and" 20 RuleConnectorOr = "or" 21 ) 22 23 type RuleSet struct { 24 Id int64 `yaml:"id" json:"id"` 25 Code string `yaml:"code" json:"code"` 26 IsOn bool `yaml:"isOn" json:"isOn"` 27 Name string `yaml:"name" json:"name"` 28 Description string `yaml:"description" json:"description"` 29 Rules []*Rule `yaml:"rules" json:"rules"` 30 Connector RuleConnector `yaml:"connector" json:"connector"` // rules connector 31 Actions []*ActionConfig `yaml:"actions" json:"actions"` 32 IgnoreLocal bool `yaml:"ignoreLocal" json:"ignoreLocal"` 33 34 actionCodes []string 35 actionInstances []ActionInterface 36 37 hasAllowActions bool 38 allowScope string 39 40 hasRules bool 41 } 42 43 func NewRuleSet() *RuleSet { 44 return &RuleSet{ 45 IsOn: true, 46 } 47 } 48 49 func (this *RuleSet) Init(waf *WAF) error { 50 this.hasRules = len(this.Rules) > 0 51 if this.hasRules { 52 for _, rule := range this.Rules { 53 err := rule.Init() 54 if err != nil { 55 return fmt.Errorf("init rule '%s %s %s' failed: %w", rule.Param, rule.Operator, types.String(rule.Value), err) 56 } 57 } 58 59 // sort by priority 60 sort.Slice(this.Rules, func(i, j int) bool { 61 return this.Rules[i].Priority > this.Rules[j].Priority 62 }) 63 } 64 65 // action codes 66 var actionCodes = []string{} 67 for _, action := range this.Actions { 68 if action.Code == ActionAllow { 69 this.hasAllowActions = true 70 if action.Options != nil { 71 this.allowScope = action.Options.GetString("scope") 72 } 73 } 74 if !lists.ContainsString(actionCodes, action.Code) { 75 actionCodes = append(actionCodes, action.Code) 76 } 77 } 78 this.actionCodes = actionCodes 79 80 // action instances 81 this.actionInstances = []ActionInterface{} 82 for _, action := range this.Actions { 83 var instance = FindActionInstance(action.Code, action.Options) 84 if instance == nil { 85 remotelogs.Error("WAF_RULE_SET", "can not find instance for action '"+action.Code+"'") 86 continue 87 } 88 89 err := instance.Init(waf) 90 if err != nil { 91 remotelogs.Error("WAF_RULE_SET", "init action '"+action.Code+"' failed: "+err.Error()) 92 continue 93 } 94 95 this.actionInstances = append(this.actionInstances, instance) 96 waf.AddAction(instance) 97 } 98 99 // sort actions 100 sort.Slice(this.actionInstances, func(i, j int) bool { 101 var instance1 = this.actionInstances[i] 102 if !instance1.WillChange() { 103 return true 104 } 105 if instance1.Code() == ActionRecordIP { 106 return true 107 } 108 return false 109 }) 110 111 return nil 112 } 113 114 func (this *RuleSet) AddRule(rule ...*Rule) { 115 this.Rules = append(this.Rules, rule...) 116 } 117 118 // AddAction 添加动作 119 func (this *RuleSet) AddAction(code string, options maps.Map) { 120 if options == nil { 121 options = maps.Map{} 122 } 123 this.Actions = append(this.Actions, &ActionConfig{ 124 Code: code, 125 Options: options, 126 }) 127 } 128 129 // HasSpecialActions 除了Allow之外是否还有别的动作 130 func (this *RuleSet) HasSpecialActions() bool { 131 for _, action := range this.Actions { 132 if action.Code != ActionAllow { 133 return true 134 } 135 } 136 return false 137 } 138 139 // HasAttackActions 检查是否含有攻击防御动作 140 func (this *RuleSet) HasAttackActions() bool { 141 for _, action := range this.actionInstances { 142 if action.IsAttack() { 143 return true 144 } 145 } 146 return false 147 } 148 149 func (this *RuleSet) ActionCodes() []string { 150 return this.actionCodes 151 } 152 153 func (this *RuleSet) PerformActions(waf *WAF, group *RuleGroup, req requests.Request, writer http.ResponseWriter) PerformResult { 154 if len(waf.Mode) != 0 && waf.Mode != firewallconfigs.FirewallModeDefend { 155 return PerformResult{ 156 ContinueRequest: true, 157 } 158 } 159 160 var isAllowed = this.hasAllowActions 161 var allowScope = this.allowScope 162 var continueRequest bool 163 var goNextGroup bool 164 var goNextSet bool 165 166 // 先执行allow 167 for _, instance := range this.actionInstances { 168 if !instance.WillChange() { 169 continueRequest = req.WAFOnAction(instance) 170 if !continueRequest { 171 return PerformResult{ 172 IsAllowed: isAllowed, 173 AllowScope: allowScope, 174 } 175 } 176 var performResult = instance.Perform(waf, group, this, req, writer) 177 continueRequest = performResult.ContinueRequest 178 goNextSet = performResult.GoNextSet 179 if performResult.IsAllowed { 180 isAllowed = true 181 allowScope = performResult.AllowScope 182 goNextGroup = performResult.GoNextGroup 183 } 184 } 185 } 186 187 // 再执行block|verify 188 for _, instance := range this.actionInstances { 189 // 只执行第一个可能改变请求的动作,其余的都会被忽略 190 if instance.WillChange() { 191 continueRequest = req.WAFOnAction(instance) 192 if !continueRequest { 193 return PerformResult{ 194 IsAllowed: isAllowed, 195 AllowScope: allowScope, 196 } 197 } 198 var performResult = instance.Perform(waf, group, this, req, writer) 199 continueRequest = performResult.ContinueRequest 200 goNextSet = performResult.GoNextSet 201 if performResult.IsAllowed { 202 isAllowed = true 203 allowScope = performResult.AllowScope 204 goNextGroup = performResult.GoNextGroup 205 } 206 return PerformResult{ 207 ContinueRequest: performResult.ContinueRequest, 208 GoNextGroup: goNextGroup, 209 GoNextSet: performResult.GoNextSet, 210 IsAllowed: isAllowed, 211 AllowScope: allowScope, 212 } 213 } 214 } 215 216 return PerformResult{ 217 ContinueRequest: true, 218 GoNextGroup: goNextGroup, 219 GoNextSet: goNextSet, 220 IsAllowed: isAllowed, 221 AllowScope: allowScope, 222 } 223 } 224 225 func (this *RuleSet) MatchRequest(req requests.Request) (b bool, hasRequestBody bool, err error) { 226 // 是否忽略局域网IP 227 if this.IgnoreLocal && utils.IsLocalIP(req.WAFRemoteIP()) { 228 return false, hasRequestBody, nil 229 } 230 231 if !this.hasRules { 232 return false, hasRequestBody, nil 233 } 234 switch this.Connector { 235 case RuleConnectorAnd: 236 for _, rule := range this.Rules { 237 b1, hasCheckRequestBody, err1 := rule.MatchRequest(req) 238 if hasCheckRequestBody { 239 hasRequestBody = true 240 } 241 if err1 != nil { 242 return false, hasRequestBody, err1 243 } 244 if !b1 { 245 return false, hasRequestBody, nil 246 } 247 } 248 return true, hasRequestBody, nil 249 case RuleConnectorOr: 250 for _, rule := range this.Rules { 251 b1, hasCheckRequestBody, err1 := rule.MatchRequest(req) 252 if hasCheckRequestBody { 253 hasRequestBody = true 254 } 255 if err1 != nil { 256 return false, hasRequestBody, err1 257 } 258 if b1 { 259 return true, hasRequestBody, nil 260 } 261 } 262 default: // same as And 263 for _, rule := range this.Rules { 264 b1, hasCheckRequestBody, err1 := rule.MatchRequest(req) 265 if hasCheckRequestBody { 266 hasRequestBody = true 267 } 268 if err1 != nil { 269 return false, hasRequestBody, err1 270 } 271 if !b1 { 272 return false, hasRequestBody, nil 273 } 274 } 275 return true, hasRequestBody, nil 276 } 277 return 278 } 279 280 func (this *RuleSet) MatchResponse(req requests.Request, resp *requests.Response) (b bool, hasRequestBody bool, err error) { 281 if !this.hasRules { 282 return false, hasRequestBody, nil 283 } 284 switch this.Connector { 285 case RuleConnectorAnd: 286 for _, rule := range this.Rules { 287 b1, hasCheckRequestBody, err1 := rule.MatchResponse(req, resp) 288 if hasCheckRequestBody { 289 hasRequestBody = true 290 } 291 if err1 != nil { 292 return false, hasRequestBody, err1 293 } 294 if !b1 { 295 return false, hasRequestBody, nil 296 } 297 } 298 return true, hasRequestBody, nil 299 case RuleConnectorOr: 300 for _, rule := range this.Rules { 301 // 对于OR连接符,只需要判断最先匹配的一条规则中的hasRequestBody即可 302 b1, hasCheckRequestBody, err1 := rule.MatchResponse(req, resp) 303 if err1 != nil { 304 return false, hasCheckRequestBody, err1 305 } 306 if b1 { 307 return true, hasCheckRequestBody, nil 308 } 309 } 310 default: // same as And 311 for _, rule := range this.Rules { 312 b1, hasCheckRequestBody, err1 := rule.MatchResponse(req, resp) 313 if hasCheckRequestBody { 314 hasRequestBody = true 315 } 316 if err1 != nil { 317 return false, hasRequestBody, err1 318 } 319 if !b1 { 320 return false, hasRequestBody, nil 321 } 322 } 323 return true, hasRequestBody, nil 324 } 325 return 326 }