mirror of
https://github.com/apernet/OpenGFW.git
synced 2024-12-23 01:19:21 +08:00
feat: logging support in ruleset
This commit is contained in:
parent
465373eaf1
commit
7353a16358
29
cmd/root.go
29
cmd/root.go
@ -254,6 +254,7 @@ func runMain(cmd *cobra.Command, args []string) {
|
|||||||
logger.Fatal("failed to load rules", zap.Error(err))
|
logger.Fatal("failed to load rules", zap.Error(err))
|
||||||
}
|
}
|
||||||
rsConfig := &ruleset.BuiltinConfig{
|
rsConfig := &ruleset.BuiltinConfig{
|
||||||
|
Logger: &rulesetLogger{},
|
||||||
GeoSiteFilename: config.Ruleset.GeoSite,
|
GeoSiteFilename: config.Ruleset.GeoSite,
|
||||||
GeoIpFilename: config.Ruleset.GeoIp,
|
GeoIpFilename: config.Ruleset.GeoIp,
|
||||||
}
|
}
|
||||||
@ -371,14 +372,6 @@ func (l *engineLogger) UDPStreamAction(info ruleset.StreamInfo, action ruleset.A
|
|||||||
zap.Bool("noMatch", noMatch))
|
zap.Bool("noMatch", noMatch))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *engineLogger) MatchError(info ruleset.StreamInfo, err error) {
|
|
||||||
logger.Error("match error",
|
|
||||||
zap.Int64("id", info.ID),
|
|
||||||
zap.String("src", info.SrcString()),
|
|
||||||
zap.String("dst", info.DstString()),
|
|
||||||
zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *engineLogger) ModifyError(info ruleset.StreamInfo, err error) {
|
func (l *engineLogger) ModifyError(info ruleset.StreamInfo, err error) {
|
||||||
logger.Error("modify error",
|
logger.Error("modify error",
|
||||||
zap.Int64("id", info.ID),
|
zap.Int64("id", info.ID),
|
||||||
@ -408,6 +401,26 @@ func (l *engineLogger) AnalyzerErrorf(streamID int64, name string, format string
|
|||||||
zap.String("msg", fmt.Sprintf(format, args...)))
|
zap.String("msg", fmt.Sprintf(format, args...)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type rulesetLogger struct{}
|
||||||
|
|
||||||
|
func (l *rulesetLogger) Log(info ruleset.StreamInfo, name string) {
|
||||||
|
logger.Info("ruleset log",
|
||||||
|
zap.String("name", name),
|
||||||
|
zap.Int64("id", info.ID),
|
||||||
|
zap.String("src", info.SrcString()),
|
||||||
|
zap.String("dst", info.DstString()),
|
||||||
|
zap.Any("props", info.Props))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *rulesetLogger) MatchError(info ruleset.StreamInfo, name string, err error) {
|
||||||
|
logger.Error("ruleset match error",
|
||||||
|
zap.String("name", name),
|
||||||
|
zap.Int64("id", info.ID),
|
||||||
|
zap.String("src", info.SrcString()),
|
||||||
|
zap.String("dst", info.DstString()),
|
||||||
|
zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
func envOrDefaultString(key, def string) string {
|
func envOrDefaultString(key, def string) string {
|
||||||
if v := os.Getenv(key); v != "" {
|
if v := os.Getenv(key); v != "" {
|
||||||
return v
|
return v
|
||||||
|
@ -41,7 +41,6 @@ type Logger interface {
|
|||||||
UDPStreamPropUpdate(info ruleset.StreamInfo, close bool)
|
UDPStreamPropUpdate(info ruleset.StreamInfo, close bool)
|
||||||
UDPStreamAction(info ruleset.StreamInfo, action ruleset.Action, noMatch bool)
|
UDPStreamAction(info ruleset.StreamInfo, action ruleset.Action, noMatch bool)
|
||||||
|
|
||||||
MatchError(info ruleset.StreamInfo, err error)
|
|
||||||
ModifyError(info ruleset.StreamInfo, err error)
|
ModifyError(info ruleset.StreamInfo, err error)
|
||||||
|
|
||||||
AnalyzerDebugf(streamID int64, name string, format string, args ...interface{})
|
AnalyzerDebugf(streamID int64, name string, format string, args ...interface{})
|
||||||
|
@ -148,10 +148,7 @@ func (s *tcpStream) ReassembledSG(sg reassembly.ScatterGather, ac reassembly.Ass
|
|||||||
s.virgin = false
|
s.virgin = false
|
||||||
s.logger.TCPStreamPropUpdate(s.info, false)
|
s.logger.TCPStreamPropUpdate(s.info, false)
|
||||||
// Match properties against ruleset
|
// Match properties against ruleset
|
||||||
result, err := s.ruleset.Match(s.info)
|
result := s.ruleset.Match(s.info)
|
||||||
if err != nil {
|
|
||||||
s.logger.MatchError(s.info, err)
|
|
||||||
}
|
|
||||||
action := result.Action
|
action := result.Action
|
||||||
if action != ruleset.ActionMaybe && action != ruleset.ActionModify {
|
if action != ruleset.ActionMaybe && action != ruleset.ActionModify {
|
||||||
verdict := actionToTCPVerdict(action)
|
verdict := actionToTCPVerdict(action)
|
||||||
|
@ -201,10 +201,7 @@ func (s *udpStream) Feed(udp *layers.UDP, rev bool, uc *udpContext) {
|
|||||||
s.virgin = false
|
s.virgin = false
|
||||||
s.logger.UDPStreamPropUpdate(s.info, false)
|
s.logger.UDPStreamPropUpdate(s.info, false)
|
||||||
// Match properties against ruleset
|
// Match properties against ruleset
|
||||||
result, err := s.ruleset.Match(s.info)
|
result := s.ruleset.Match(s.info)
|
||||||
if err != nil {
|
|
||||||
s.logger.MatchError(s.info, err)
|
|
||||||
}
|
|
||||||
action := result.Action
|
action := result.Action
|
||||||
if action == ruleset.ActionModify {
|
if action == ruleset.ActionModify {
|
||||||
// Call the modifier instance
|
// Call the modifier instance
|
||||||
@ -214,6 +211,7 @@ func (s *udpStream) Feed(udp *layers.UDP, rev bool, uc *udpContext) {
|
|||||||
s.logger.ModifyError(s.info, errInvalidModifier)
|
s.logger.ModifyError(s.info, errInvalidModifier)
|
||||||
action = ruleset.ActionMaybe
|
action = ruleset.ActionMaybe
|
||||||
} else {
|
} else {
|
||||||
|
var err error
|
||||||
uc.Packet, err = udpMI.Process(udp.Payload)
|
uc.Packet, err = udpMI.Process(udp.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Modifier error, fallback to maybe
|
// Modifier error, fallback to maybe
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
type ExprRule struct {
|
type ExprRule struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Action string `yaml:"action"`
|
Action string `yaml:"action"`
|
||||||
|
Log bool `yaml:"log"`
|
||||||
Modifier ModifierEntry `yaml:"modifier"`
|
Modifier ModifierEntry `yaml:"modifier"`
|
||||||
Expr string `yaml:"expr"`
|
Expr string `yaml:"expr"`
|
||||||
}
|
}
|
||||||
@ -45,7 +46,8 @@ func ExprRulesFromYAML(file string) ([]ExprRule, error) {
|
|||||||
// compiledExprRule is the internal, compiled representation of an expression rule.
|
// compiledExprRule is the internal, compiled representation of an expression rule.
|
||||||
type compiledExprRule struct {
|
type compiledExprRule struct {
|
||||||
Name string
|
Name string
|
||||||
Action Action
|
Action *Action // fallthrough if nil
|
||||||
|
Log bool
|
||||||
ModInstance modifier.Instance
|
ModInstance modifier.Instance
|
||||||
Program *vm.Program
|
Program *vm.Program
|
||||||
}
|
}
|
||||||
@ -55,6 +57,7 @@ var _ Ruleset = (*exprRuleset)(nil)
|
|||||||
type exprRuleset struct {
|
type exprRuleset struct {
|
||||||
Rules []compiledExprRule
|
Rules []compiledExprRule
|
||||||
Ans []analyzer.Analyzer
|
Ans []analyzer.Analyzer
|
||||||
|
Logger Logger
|
||||||
GeoMatcher *geo.GeoMatcher
|
GeoMatcher *geo.GeoMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,25 +65,31 @@ func (r *exprRuleset) Analyzers(info StreamInfo) []analyzer.Analyzer {
|
|||||||
return r.Ans
|
return r.Ans
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *exprRuleset) Match(info StreamInfo) (MatchResult, error) {
|
func (r *exprRuleset) Match(info StreamInfo) MatchResult {
|
||||||
env := streamInfoToExprEnv(info)
|
env := streamInfoToExprEnv(info)
|
||||||
for _, rule := range r.Rules {
|
for _, rule := range r.Rules {
|
||||||
v, err := vm.Run(rule.Program, env)
|
v, err := vm.Run(rule.Program, env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return MatchResult{
|
// Log the error and continue to the next rule.
|
||||||
Action: ActionMaybe,
|
r.Logger.MatchError(info, rule.Name, err)
|
||||||
}, fmt.Errorf("rule %q failed to run: %w", rule.Name, err)
|
continue
|
||||||
}
|
}
|
||||||
if vBool, ok := v.(bool); ok && vBool {
|
if vBool, ok := v.(bool); ok && vBool {
|
||||||
|
if rule.Log {
|
||||||
|
r.Logger.Log(info, rule.Name)
|
||||||
|
}
|
||||||
|
if rule.Action != nil {
|
||||||
return MatchResult{
|
return MatchResult{
|
||||||
Action: rule.Action,
|
Action: *rule.Action,
|
||||||
ModInstance: rule.ModInstance,
|
ModInstance: rule.ModInstance,
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No match
|
||||||
return MatchResult{
|
return MatchResult{
|
||||||
Action: ActionMaybe,
|
Action: ActionMaybe,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompileExprRules compiles a list of expression rules into a ruleset.
|
// CompileExprRules compiles a list of expression rules into a ruleset.
|
||||||
@ -97,10 +106,17 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
|
|||||||
}
|
}
|
||||||
// Compile all rules and build a map of analyzers that are used by the rules.
|
// Compile all rules and build a map of analyzers that are used by the rules.
|
||||||
for _, rule := range rules {
|
for _, rule := range rules {
|
||||||
action, ok := actionStringToAction(rule.Action)
|
if rule.Action == "" && !rule.Log {
|
||||||
|
return nil, fmt.Errorf("rule %q must have at least one of action or log", rule.Name)
|
||||||
|
}
|
||||||
|
var action *Action
|
||||||
|
if rule.Action != "" {
|
||||||
|
a, ok := actionStringToAction(rule.Action)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("rule %q has invalid action %q", rule.Name, rule.Action)
|
return nil, fmt.Errorf("rule %q has invalid action %q", rule.Name, rule.Action)
|
||||||
}
|
}
|
||||||
|
action = &a
|
||||||
|
}
|
||||||
visitor := &idVisitor{Identifiers: make(map[string]bool)}
|
visitor := &idVisitor{Identifiers: make(map[string]bool)}
|
||||||
patcher := &idPatcher{}
|
patcher := &idPatcher{}
|
||||||
program, err := expr.Compile(rule.Expr,
|
program, err := expr.Compile(rule.Expr,
|
||||||
@ -145,9 +161,10 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
|
|||||||
cr := compiledExprRule{
|
cr := compiledExprRule{
|
||||||
Name: rule.Name,
|
Name: rule.Name,
|
||||||
Action: action,
|
Action: action,
|
||||||
|
Log: rule.Log,
|
||||||
Program: program,
|
Program: program,
|
||||||
}
|
}
|
||||||
if action == ActionModify {
|
if action != nil && *action == ActionModify {
|
||||||
mod, ok := fullModMap[rule.Modifier.Name]
|
mod, ok := fullModMap[rule.Modifier.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("rule %q uses unknown modifier %q", rule.Name, rule.Modifier.Name)
|
return nil, fmt.Errorf("rule %q uses unknown modifier %q", rule.Name, rule.Modifier.Name)
|
||||||
@ -168,6 +185,7 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
|
|||||||
return &exprRuleset{
|
return &exprRuleset{
|
||||||
Rules: compiledRules,
|
Rules: compiledRules,
|
||||||
Ans: depAns,
|
Ans: depAns,
|
||||||
|
Logger: config.Logger,
|
||||||
GeoMatcher: geoMatcher,
|
GeoMatcher: geoMatcher,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -284,6 +302,10 @@ func (p *idPatcher) Visit(node *ast.Node) {
|
|||||||
switch (*node).(type) {
|
switch (*node).(type) {
|
||||||
case *ast.CallNode:
|
case *ast.CallNode:
|
||||||
callNode := (*node).(*ast.CallNode)
|
callNode := (*node).(*ast.CallNode)
|
||||||
|
if callNode.Func == nil {
|
||||||
|
// Ignore invalid call nodes
|
||||||
|
return
|
||||||
|
}
|
||||||
switch callNode.Func.Name {
|
switch callNode.Func.Name {
|
||||||
case "cidr":
|
case "cidr":
|
||||||
cidrStringNode, ok := callNode.Arguments[1].(*ast.StringNode)
|
cidrStringNode, ok := callNode.Arguments[1].(*ast.StringNode)
|
||||||
|
@ -90,10 +90,17 @@ type Ruleset interface {
|
|||||||
Analyzers(StreamInfo) []analyzer.Analyzer
|
Analyzers(StreamInfo) []analyzer.Analyzer
|
||||||
// Match matches a stream against the ruleset and returns the result.
|
// Match matches a stream against the ruleset and returns the result.
|
||||||
// It must be safe for concurrent use by multiple workers.
|
// It must be safe for concurrent use by multiple workers.
|
||||||
Match(StreamInfo) (MatchResult, error)
|
Match(StreamInfo) MatchResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger is the logging interface for the ruleset.
|
||||||
|
type Logger interface {
|
||||||
|
Log(info StreamInfo, name string)
|
||||||
|
MatchError(info StreamInfo, name string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BuiltinConfig struct {
|
type BuiltinConfig struct {
|
||||||
|
Logger Logger
|
||||||
GeoSiteFilename string
|
GeoSiteFilename string
|
||||||
GeoIpFilename string
|
GeoIpFilename string
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user