github.com/TeaOSLab/EdgeNode@v1.3.8/internal/nodes/http_request_waf.go (about) 1 package nodes 2 3 import ( 4 "bytes" 5 iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary" 6 "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" 7 "github.com/TeaOSLab/EdgeNode/internal/iplibrary" 8 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 9 "github.com/TeaOSLab/EdgeNode/internal/stats" 10 "github.com/TeaOSLab/EdgeNode/internal/waf" 11 "github.com/iwind/TeaGo/Tea" 12 "github.com/iwind/TeaGo/types" 13 "io" 14 "net/http" 15 "time" 16 ) 17 18 // 调用WAF 19 func (this *HTTPRequest) doWAFRequest() (blocked bool) { 20 if this.web.FirewallRef == nil || !this.web.FirewallRef.IsOn { 21 return 22 } 23 24 var remoteAddr = this.requestRemoteAddr(true) 25 26 // 检查是否为白名单直连 27 if !Tea.IsTesting() && this.nodeConfig.IPIsAutoAllowed(remoteAddr) { 28 return 29 } 30 31 // 当前连接是否已关闭 32 if this.isConnClosed() { 33 this.disableLog = true 34 return true 35 } 36 37 // 是否在全局名单中 38 canGoNext, isInAllowedList, _ := iplibrary.AllowIP(remoteAddr, this.ReqServer.Id) 39 if !canGoNext { 40 this.disableLog = true 41 this.Close() 42 return true 43 } 44 if isInAllowedList { 45 return false 46 } 47 48 // 检查是否在临时黑名单中 49 if waf.SharedIPBlackList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeServer, this.ReqServer.Id, remoteAddr) || waf.SharedIPBlackList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, remoteAddr) { 50 this.disableLog = true 51 this.Close() 52 53 return true 54 } 55 56 var forceLog = false 57 var forceLogRequestBody = false 58 var forceLogRegionDenying = false 59 if this.ReqServer.HTTPFirewallPolicy != nil && 60 this.ReqServer.HTTPFirewallPolicy.IsOn && 61 this.ReqServer.HTTPFirewallPolicy.Log != nil && 62 this.ReqServer.HTTPFirewallPolicy.Log.IsOn { 63 forceLog = true 64 forceLogRequestBody = this.ReqServer.HTTPFirewallPolicy.Log.RequestBody 65 forceLogRegionDenying = this.ReqServer.HTTPFirewallPolicy.Log.RegionDenying 66 } 67 68 // 检查IP名单 69 { 70 // 当前服务的独立设置 71 if this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn { 72 blockedRequest, breakChecking := this.checkWAFRemoteAddr(this.web.FirewallPolicy) 73 if blockedRequest { 74 return true 75 } 76 if breakChecking { 77 return false 78 } 79 } 80 81 // 公用的防火墙设置 82 if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn { 83 blockedRequest, breakChecking := this.checkWAFRemoteAddr(this.ReqServer.HTTPFirewallPolicy) 84 if blockedRequest { 85 return true 86 } 87 if breakChecking { 88 return false 89 } 90 } 91 } 92 93 // 检查WAF规则 94 { 95 // 当前服务的独立设置 96 if this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn { 97 blockedRequest, breakChecking := this.checkWAFRequest(this.web.FirewallPolicy, forceLog, forceLogRequestBody, forceLogRegionDenying, false) 98 if blockedRequest { 99 return true 100 } 101 if breakChecking { 102 return false 103 } 104 } 105 106 // 公用的防火墙设置 107 if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn { 108 blockedRequest, breakChecking := this.checkWAFRequest(this.ReqServer.HTTPFirewallPolicy, forceLog, forceLogRequestBody, forceLogRegionDenying, this.web.FirewallRef.IgnoreGlobalRules) 109 if blockedRequest { 110 return true 111 } 112 if breakChecking { 113 return false 114 } 115 } 116 } 117 118 return 119 } 120 121 // check client remote address 122 func (this *HTTPRequest) checkWAFRemoteAddr(firewallPolicy *firewallconfigs.HTTPFirewallPolicy) (blocked bool, breakChecking bool) { 123 if firewallPolicy == nil { 124 return 125 } 126 127 var isDefendMode = firewallPolicy.Mode == firewallconfigs.FirewallModeDefend 128 129 // 检查IP白名单 130 var remoteAddrs []string 131 if len(this.remoteAddr) > 0 { 132 remoteAddrs = []string{this.remoteAddr} 133 } else { 134 remoteAddrs = this.requestRemoteAddrs() 135 } 136 137 var inbound = firewallPolicy.Inbound 138 if inbound == nil { 139 return 140 } 141 for _, ref := range inbound.AllAllowListRefs() { 142 if ref.IsOn && ref.ListId > 0 { 143 var list = iplibrary.SharedIPListManager.FindList(ref.ListId) 144 if list != nil { 145 _, found := list.ContainsIPStrings(remoteAddrs) 146 if found { 147 breakChecking = true 148 return 149 } 150 } 151 } 152 } 153 154 // 检查IP黑名单 155 if isDefendMode { 156 for _, ref := range inbound.AllDenyListRefs() { 157 if ref.IsOn && ref.ListId > 0 { 158 var list = iplibrary.SharedIPListManager.FindList(ref.ListId) 159 if list != nil { 160 item, found := list.ContainsIPStrings(remoteAddrs) 161 if found { 162 // 触发事件 163 if item != nil && len(item.EventLevel) > 0 { 164 actions := iplibrary.SharedActionManager.FindEventActions(item.EventLevel) 165 for _, action := range actions { 166 goNext, err := action.DoHTTP(this.RawReq, this.RawWriter) 167 if err != nil { 168 remotelogs.Error("HTTP_REQUEST_WAF", "do action '"+err.Error()+"' failed: "+err.Error()) 169 return true, false 170 } 171 if !goNext { 172 this.disableLog = true 173 return true, false 174 } 175 } 176 } 177 178 // TODO 考虑是否需要记录日志信息吗,可能数据量非常庞大,所以暂时不记录 179 180 this.writer.WriteHeader(http.StatusForbidden) 181 this.writer.Close() 182 183 // 停止日志 184 this.disableLog = true 185 186 return true, false 187 } 188 } 189 } 190 } 191 } 192 193 return 194 } 195 196 // check waf inbound rules 197 func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFirewallPolicy, forceLog bool, logRequestBody bool, logDenying bool, ignoreRules bool) (blocked bool, breakChecking bool) { 198 // 检查配置是否为空 199 if firewallPolicy == nil || !firewallPolicy.IsOn || firewallPolicy.Inbound == nil || !firewallPolicy.Inbound.IsOn || firewallPolicy.Mode == firewallconfigs.FirewallModeBypass { 200 return 201 } 202 203 var isDefendMode = firewallPolicy.Mode == firewallconfigs.FirewallModeDefend 204 205 // 检查IP白名单 206 var remoteAddrs []string 207 if len(this.remoteAddr) > 0 { 208 remoteAddrs = []string{this.remoteAddr} 209 } else { 210 remoteAddrs = this.requestRemoteAddrs() 211 } 212 213 var inbound = firewallPolicy.Inbound 214 if inbound == nil { 215 return 216 } 217 218 // 检查地区封禁 219 if firewallPolicy.Inbound.Region != nil && firewallPolicy.Inbound.Region.IsOn { 220 var regionConfig = firewallPolicy.Inbound.Region 221 if regionConfig.IsNotEmpty() { 222 for _, remoteAddr := range remoteAddrs { 223 var result = iplib.LookupIP(remoteAddr) 224 if result != nil && result.IsOk() { 225 var currentURL = this.URL() 226 if regionConfig.MatchCountryURL(currentURL) { 227 // 检查国家/地区级别封禁 228 if !regionConfig.IsAllowedCountry(result.CountryId(), result.ProvinceId()) { 229 this.firewallPolicyId = firewallPolicy.Id 230 231 if isDefendMode { 232 var promptHTML string 233 if len(regionConfig.CountryHTML) > 0 { 234 promptHTML = regionConfig.CountryHTML 235 } else if this.ReqServer != nil && this.ReqServer.HTTPFirewallPolicy != nil && len(this.ReqServer.HTTPFirewallPolicy.DenyCountryHTML) > 0 { 236 promptHTML = this.ReqServer.HTTPFirewallPolicy.DenyCountryHTML 237 } 238 239 if len(promptHTML) > 0 { 240 var formattedHTML = this.Format(promptHTML) 241 this.writer.Header().Set("Content-Type", "text/html; charset=utf-8") 242 this.writer.Header().Set("Content-Length", types.String(len(formattedHTML))) 243 this.writer.WriteHeader(http.StatusForbidden) 244 _, _ = this.writer.Write([]byte(formattedHTML)) 245 } else { 246 this.writeCode(http.StatusForbidden, "The region has been denied.", "当前区域禁止访问") 247 } 248 249 // 延时返回,避免攻击 250 time.Sleep(1 * time.Second) 251 } 252 253 // 停止日志 254 if !logDenying { 255 this.disableLog = true 256 } else { 257 this.tags = append(this.tags, "denyCountry") 258 } 259 260 if isDefendMode { 261 return true, false 262 } 263 } 264 } 265 266 if regionConfig.MatchProvinceURL(currentURL) { 267 // 检查省份封禁 268 if !regionConfig.IsAllowedProvince(result.CountryId(), result.ProvinceId()) { 269 this.firewallPolicyId = firewallPolicy.Id 270 271 if isDefendMode { 272 var promptHTML string 273 if len(regionConfig.ProvinceHTML) > 0 { 274 promptHTML = regionConfig.ProvinceHTML 275 } else if this.ReqServer != nil && this.ReqServer.HTTPFirewallPolicy != nil && len(this.ReqServer.HTTPFirewallPolicy.DenyProvinceHTML) > 0 { 276 promptHTML = this.ReqServer.HTTPFirewallPolicy.DenyProvinceHTML 277 } 278 279 if len(promptHTML) > 0 { 280 var formattedHTML = this.Format(promptHTML) 281 this.writer.Header().Set("Content-Type", "text/html; charset=utf-8") 282 this.writer.Header().Set("Content-Length", types.String(len(formattedHTML))) 283 this.writer.WriteHeader(http.StatusForbidden) 284 _, _ = this.writer.Write([]byte(formattedHTML)) 285 } else { 286 this.writeCode(http.StatusForbidden, "The region has been denied.", "当前区域禁止访问") 287 } 288 289 // 延时返回,避免攻击 290 time.Sleep(1 * time.Second) 291 } 292 293 // 停止日志 294 if !logDenying { 295 this.disableLog = true 296 } else { 297 this.tags = append(this.tags, "denyProvince") 298 } 299 300 if isDefendMode { 301 return true, false 302 } 303 } 304 } 305 } 306 } 307 } 308 } 309 310 // 是否执行规则 311 if ignoreRules { 312 return 313 } 314 315 // 规则测试 316 var w = waf.SharedWAFManager.FindWAF(firewallPolicy.Id) 317 if w == nil { 318 return 319 } 320 321 result, err := w.MatchRequest(this, this.writer, this.web.FirewallRef.DefaultCaptchaType) 322 if err != nil { 323 if !this.canIgnore(err) { 324 remotelogs.Warn("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error()) 325 } 326 return 327 } 328 if result.IsAllowed && (len(result.AllowScope) == 0 || result.AllowScope == waf.AllowScopeGlobal) { 329 breakChecking = true 330 } 331 if forceLog && logRequestBody && result.HasRequestBody && result.Set != nil && result.Set.HasAttackActions() { 332 this.wafHasRequestBody = true 333 } 334 335 if result.Set != nil { 336 if forceLog { 337 this.forceLog = true 338 } 339 340 if result.Set.HasSpecialActions() { 341 this.firewallPolicyId = firewallPolicy.Id 342 this.firewallRuleGroupId = types.Int64(result.Group.Id) 343 this.firewallRuleSetId = types.Int64(result.Set.Id) 344 345 if result.Set.HasAttackActions() { 346 this.isAttack = true 347 } 348 349 // 添加统计 350 stats.SharedHTTPRequestStatManager.AddFirewallRuleGroupId(this.ReqServer.Id, this.firewallRuleGroupId, result.Set.Actions) 351 } 352 353 this.firewallActions = append(result.Set.ActionCodes(), firewallPolicy.Mode) 354 } 355 356 return !result.GoNext, breakChecking 357 } 358 359 // call response waf 360 func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) { 361 if this.web.FirewallRef == nil || !this.web.FirewallRef.IsOn { 362 return 363 } 364 365 // 当前服务的独立设置 366 var forceLog = false 367 var forceLogRequestBody = false 368 if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn && this.ReqServer.HTTPFirewallPolicy.Log != nil && this.ReqServer.HTTPFirewallPolicy.Log.IsOn { 369 forceLog = true 370 forceLogRequestBody = this.ReqServer.HTTPFirewallPolicy.Log.RequestBody 371 } 372 373 if this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn { 374 blockedRequest, breakChecking := this.checkWAFResponse(this.web.FirewallPolicy, resp, forceLog, forceLogRequestBody, false) 375 if blockedRequest { 376 return true 377 } 378 if breakChecking { 379 return 380 } 381 } 382 383 // 公用的防火墙设置 384 if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn { 385 blockedRequest, _ := this.checkWAFResponse(this.ReqServer.HTTPFirewallPolicy, resp, forceLog, forceLogRequestBody, this.web.FirewallRef.IgnoreGlobalRules) 386 if blockedRequest { 387 return true 388 } 389 } 390 return 391 } 392 393 // check waf outbound rules 394 func (this *HTTPRequest) checkWAFResponse(firewallPolicy *firewallconfigs.HTTPFirewallPolicy, resp *http.Response, forceLog bool, logRequestBody bool, ignoreRules bool) (blocked bool, breakChecking bool) { 395 if firewallPolicy == nil || !firewallPolicy.IsOn || !firewallPolicy.Outbound.IsOn || firewallPolicy.Mode == firewallconfigs.FirewallModeBypass { 396 return 397 } 398 399 // 是否执行规则 400 if ignoreRules { 401 return 402 } 403 404 var w = waf.SharedWAFManager.FindWAF(firewallPolicy.Id) 405 if w == nil { 406 return 407 } 408 409 result, err := w.MatchResponse(this, resp, this.writer) 410 if err != nil { 411 if !this.canIgnore(err) { 412 remotelogs.Warn("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error()) 413 } 414 return 415 } 416 if result.IsAllowed && (len(result.AllowScope) == 0 || result.AllowScope == waf.AllowScopeGlobal) { 417 breakChecking = true 418 } 419 if forceLog && logRequestBody && result.HasRequestBody && result.Set != nil && result.Set.HasAttackActions() { 420 this.wafHasRequestBody = true 421 } 422 423 if result.Set != nil { 424 if forceLog { 425 this.forceLog = true 426 } 427 428 if result.Set.HasSpecialActions() { 429 this.firewallPolicyId = firewallPolicy.Id 430 this.firewallRuleGroupId = types.Int64(result.Group.Id) 431 this.firewallRuleSetId = types.Int64(result.Set.Id) 432 433 if result.Set.HasAttackActions() { 434 this.isAttack = true 435 } 436 437 // 添加统计 438 stats.SharedHTTPRequestStatManager.AddFirewallRuleGroupId(this.ReqServer.Id, this.firewallRuleGroupId, result.Set.Actions) 439 } 440 441 this.firewallActions = append(result.Set.ActionCodes(), firewallPolicy.Mode) 442 } 443 444 return !result.GoNext, breakChecking 445 } 446 447 // WAFRaw 原始请求 448 func (this *HTTPRequest) WAFRaw() *http.Request { 449 return this.RawReq 450 } 451 452 // WAFRemoteIP 客户端IP 453 func (this *HTTPRequest) WAFRemoteIP() string { 454 return this.requestRemoteAddr(true) 455 } 456 457 // WAFGetCacheBody 获取缓存中的Body 458 func (this *HTTPRequest) WAFGetCacheBody() []byte { 459 return this.requestBodyData 460 } 461 462 // WAFSetCacheBody 设置Body 463 func (this *HTTPRequest) WAFSetCacheBody(body []byte) { 464 this.requestBodyData = body 465 } 466 467 // WAFReadBody 读取Body 468 func (this *HTTPRequest) WAFReadBody(max int64) (data []byte, err error) { 469 if this.RawReq.ContentLength > 0 { 470 data, err = io.ReadAll(io.LimitReader(this.RawReq.Body, max)) 471 } 472 473 return 474 } 475 476 // WAFRestoreBody 恢复Body 477 func (this *HTTPRequest) WAFRestoreBody(data []byte) { 478 if len(data) > 0 { 479 this.RawReq.Body = io.NopCloser(io.MultiReader(bytes.NewBuffer(data), this.RawReq.Body)) 480 } 481 } 482 483 // WAFServerId 服务ID 484 func (this *HTTPRequest) WAFServerId() int64 { 485 return this.ReqServer.Id 486 } 487 488 // WAFClose 关闭连接 489 func (this *HTTPRequest) WAFClose() { 490 this.Close() 491 492 // 这里不要强关IP所有连接,避免因为单个服务而影响所有 493 } 494 495 func (this *HTTPRequest) WAFOnAction(action interface{}) (goNext bool) { 496 if action == nil { 497 return true 498 } 499 500 instance, ok := action.(waf.ActionInterface) 501 if !ok { 502 return true 503 } 504 505 switch instance.Code() { 506 case waf.ActionTag: 507 this.tags = append(this.tags, action.(*waf.TagAction).Tags...) 508 } 509 return true 510 } 511 512 func (this *HTTPRequest) WAFFingerprint() []byte { 513 // 目前只有HTTPS请求才有指纹 514 if !this.IsHTTPS { 515 return nil 516 } 517 518 var requestConn = this.RawReq.Context().Value(HTTPConnContextKey) 519 if requestConn == nil { 520 return nil 521 } 522 523 clientConn, ok := requestConn.(ClientConnInterface) 524 if ok { 525 return clientConn.Fingerprint() 526 } 527 528 return nil 529 } 530 531 func (this *HTTPRequest) WAFMaxRequestSize() int64 { 532 var maxRequestSize = firewallconfigs.DefaultMaxRequestBodySize 533 if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.MaxRequestBodySize > 0 { 534 maxRequestSize = this.ReqServer.HTTPFirewallPolicy.MaxRequestBodySize 535 } 536 return maxRequestSize 537 } 538 539 // DisableAccessLog 在当前请求中不使用访问日志 540 func (this *HTTPRequest) DisableAccessLog() { 541 this.disableLog = true 542 } 543 544 // DisableStat 停用统计 545 func (this *HTTPRequest) DisableStat() { 546 if this.web != nil { 547 this.web.StatRef = nil 548 } 549 550 this.disableMetrics = true 551 }