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  }