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  }