Sebastien Blot 2 年之前
父節點
當前提交
c46e2ccdad
共有 5 個文件被更改,包括 170 次插入33 次删除
  1. 11 5
      pkg/acquisition/modules/waf/waf.go
  2. 4 0
      pkg/csconfig/crowdsec_service.go
  3. 88 28
      pkg/waf/waf.go
  4. 26 0
      pkg/waf/waf_expr_lib.go
  5. 41 0
      pkg/waf/waf_helpers.go

+ 11 - 5
pkg/acquisition/modules/waf/waf.go

@@ -7,6 +7,7 @@ import (
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"net/http"
 	"net/http"
+	"os"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
@@ -16,7 +17,6 @@ import (
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	"github.com/crowdsecurity/crowdsec/pkg/types"
 	"github.com/crowdsecurity/crowdsec/pkg/waf"
 	"github.com/crowdsecurity/crowdsec/pkg/waf"
 	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
 	"github.com/crowdsecurity/go-cs-lib/pkg/trace"
-	"github.com/davecgh/go-spew/spew"
 	"github.com/google/uuid"
 	"github.com/google/uuid"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus"
@@ -80,6 +80,7 @@ func (w *WafSource) UnmarshalConfig(yamlConfig []byte) error {
 	if w.config.Mode == "" {
 	if w.config.Mode == "" {
 		w.config.Mode = configuration.TAIL_MODE
 		w.config.Mode = configuration.TAIL_MODE
 	}
 	}
+
 	return nil
 	return nil
 }
 }
 
 
@@ -118,16 +119,21 @@ func (w *WafSource) Configure(yamlConfig []byte, logger *log.Entry) error {
 	var inBandRules string
 	var inBandRules string
 
 
 	for _, rule := range crowdsecWafConfig.InbandRules {
 	for _, rule := range crowdsecWafConfig.InbandRules {
+
 		inBandRules += rule.String() + "\n"
 		inBandRules += rule.String() + "\n"
 	}
 	}
 
 
-	w.logger.Infof("Loading rules %+v", inBandRules)
+	w.logger.Infof("Loading %d in-band rules", len(strings.Split(inBandRules, "\n")))
+
+	//w.logger.Infof("Loading rules %+v", inBandRules)
+
+	fs := os.DirFS(crowdsecWafConfig.Datadir)
 
 
 	//in-band waf : kill on sight
 	//in-band waf : kill on sight
 	inbandwaf, err := coraza.NewWAF(
 	inbandwaf, err := coraza.NewWAF(
 		coraza.NewWAFConfig().
 		coraza.NewWAFConfig().
 			WithErrorCallback(logError).
 			WithErrorCallback(logError).
-			WithDirectives(inBandRules),
+			WithDirectives(inBandRules).WithRootFS(fs),
 	)
 	)
 
 
 	if err != nil {
 	if err != nil {
@@ -145,8 +151,8 @@ func (w *WafSource) Configure(yamlConfig []byte, logger *log.Entry) error {
 		return errors.Wrap(err, "Cannot create WAF")
 		return errors.Wrap(err, "Cannot create WAF")
 	}
 	}
 	w.outOfBandWaf = outofbandwaf
 	w.outOfBandWaf = outofbandwaf
-	log.Printf("OOB -> %s", spew.Sdump(w.outOfBandWaf))
-	log.Printf("IB -> %s", spew.Sdump(w.inBandWaf))
+	//log.Printf("OOB -> %s", spew.Sdump(w.outOfBandWaf))
+	//log.Printf("IB -> %s", spew.Sdump(w.inBandWaf))
 
 
 	//We don´t use the wrapper provided by coraza because we want to fully control what happens when a rule match to send the information in crowdsec
 	//We don´t use the wrapper provided by coraza because we want to fully control what happens when a rule match to send the information in crowdsec
 	w.mux.HandleFunc(w.config.Path, w.wafHandler)
 	w.mux.HandleFunc(w.config.Path, w.wafHandler)

+ 4 - 0
pkg/csconfig/crowdsec_service.go

@@ -11,6 +11,8 @@ import (
 	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
 	"github.com/crowdsecurity/go-cs-lib/pkg/ptr"
 )
 )
 
 
+var DataDir string // FIXME: find a better way to pass this to the waf
+
 // CrowdsecServiceCfg contains the location of parsers/scenarios/... and acquisition files
 // CrowdsecServiceCfg contains the location of parsers/scenarios/... and acquisition files
 type CrowdsecServiceCfg struct {
 type CrowdsecServiceCfg struct {
 	Enable                    *bool             `yaml:"enable"`
 	Enable                    *bool             `yaml:"enable"`
@@ -106,6 +108,8 @@ func (c *Config) LoadCrowdsec() error {
 	c.Crowdsec.HubDir = c.ConfigPaths.HubDir
 	c.Crowdsec.HubDir = c.ConfigPaths.HubDir
 	c.Crowdsec.HubIndexFile = c.ConfigPaths.HubIndexFile
 	c.Crowdsec.HubIndexFile = c.ConfigPaths.HubIndexFile
 
 
+	DataDir = c.Crowdsec.DataDir // FIXME: find a better way to give it to the waf
+
 	if c.Crowdsec.ParserRoutinesCount <= 0 {
 	if c.Crowdsec.ParserRoutinesCount <= 0 {
 		c.Crowdsec.ParserRoutinesCount = 1
 		c.Crowdsec.ParserRoutinesCount = 1
 	}
 	}

+ 88 - 28
pkg/waf/waf.go

@@ -2,21 +2,29 @@ package waf
 
 
 import (
 import (
 	"os"
 	"os"
+	"path/filepath"
 	"strings"
 	"strings"
 
 
 	"github.com/antonmedv/expr"
 	"github.com/antonmedv/expr"
 	"github.com/antonmedv/expr/vm"
 	"github.com/antonmedv/expr/vm"
+	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
 	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
+	"github.com/crowdsecurity/crowdsec/pkg/types"
 	log "github.com/sirupsen/logrus"
 	log "github.com/sirupsen/logrus"
 	"gopkg.in/yaml.v3"
 	"gopkg.in/yaml.v3"
 )
 )
 
 
 type Hook struct {
 type Hook struct {
-	Filter     string      `yaml:"filter"`
-	FilterExpr *vm.Program `yaml:"-"`
-	OnSuccess  string      `yaml:"on_success"`
-	Apply      []string    `yaml:"apply"`
-	ApplyExpr  []*vm.Program
+	Filter     string        `yaml:"filter"`
+	FilterExpr *vm.Program   `yaml:"-"`
+	OnSuccess  string        `yaml:"on_success"`
+	Apply      []string      `yaml:"apply"`
+	ApplyExpr  []*vm.Program `yaml:"-"`
+}
+
+type CompiledHook struct {
+	Filter *vm.Program   `yaml:"-"`
+	Apply  []*vm.Program `yaml:"-"`
 }
 }
 
 
 type WafRule struct {
 type WafRule struct {
@@ -25,33 +33,43 @@ type WafRule struct {
 	OnLoad            []Hook   `yaml:"on_load"`
 	OnLoad            []Hook   `yaml:"on_load"`
 	PreEval           []Hook   `yaml:"pre_eval"`
 	PreEval           []Hook   `yaml:"pre_eval"`
 	OnMatch           []Hook   `yaml:"on_match"`
 	OnMatch           []Hook   `yaml:"on_match"`
-	MergedRules       []string `yaml:"-"`
-	OutOfBand         bool     `yaml:"-"`
+
+	CompiledOnLoad  []CompiledHook `yaml:"-"`
+	CompiledPreEval []CompiledHook `yaml:"-"`
+	CompiledOnMatch []CompiledHook `yaml:"-"`
+
+	MergedRules []string `yaml:"-"`
+	OutOfBand   bool     `yaml:"-"`
 }
 }
 
 
 type WafConfig struct {
 type WafConfig struct {
 	InbandRules    []WafRule
 	InbandRules    []WafRule
 	OutOfBandRules []WafRule
 	OutOfBandRules []WafRule
+	Datadir        string
+	logger         *log.Entry
 }
 }
 
 
-func buildHook(hook Hook) (Hook, error) {
+func buildHook(hook Hook) (CompiledHook, error) {
+	compiledHook := CompiledHook{}
 	if hook.Filter != "" {
 	if hook.Filter != "" {
 		program, err := expr.Compile(hook.Filter) //FIXME: opts
 		program, err := expr.Compile(hook.Filter) //FIXME: opts
 		if err != nil {
 		if err != nil {
 			log.Errorf("unable to compile filter %s : %s", hook.Filter, err)
 			log.Errorf("unable to compile filter %s : %s", hook.Filter, err)
-			return Hook{}, err
+			return CompiledHook{}, err
 		}
 		}
-		hook.FilterExpr = program
+		compiledHook.Filter = program
 	}
 	}
 	for _, apply := range hook.Apply {
 	for _, apply := range hook.Apply {
-		program, err := expr.Compile(apply) //FIXME: opts
+		program, err := expr.Compile(apply, GetExprWAFOptions(map[string]interface{}{
+			"WafRules": []WafRule{},
+		})...)
 		if err != nil {
 		if err != nil {
 			log.Errorf("unable to compile apply %s : %s", apply, err)
 			log.Errorf("unable to compile apply %s : %s", apply, err)
-			return Hook{}, err
+			return CompiledHook{}, err
 		}
 		}
-		hook.ApplyExpr = append(hook.ApplyExpr, program)
+		compiledHook.Apply = append(compiledHook.Apply, program)
 	}
 	}
-	return hook, nil
+	return compiledHook, nil
 }
 }
 
 
 func (w *WafConfig) LoadWafRules() error {
 func (w *WafConfig) LoadWafRules() error {
@@ -61,28 +79,37 @@ func (w *WafConfig) LoadWafRules() error {
 			files = append(files, hubWafRuleItem.LocalPath)
 			files = append(files, hubWafRuleItem.LocalPath)
 		}
 		}
 	}
 	}
-	log.Infof("Loading %d waf files", len(files))
+	w.logger.Infof("Loading %d waf files", len(files))
 	for _, file := range files {
 	for _, file := range files {
 
 
 		fileContent, err := os.ReadFile(file) //FIXME: actually read from datadir
 		fileContent, err := os.ReadFile(file) //FIXME: actually read from datadir
 		if err != nil {
 		if err != nil {
-			log.Errorf("unable to read file %s : %s", file, err)
+			w.logger.Errorf("unable to read file %s : %s", file, err)
 			continue
 			continue
 		}
 		}
 		wafRule := WafRule{}
 		wafRule := WafRule{}
 		err = yaml.Unmarshal(fileContent, &wafRule)
 		err = yaml.Unmarshal(fileContent, &wafRule)
 		if err != nil {
 		if err != nil {
-			log.Errorf("unable to unmarshal file %s : %s", file, err)
+			w.logger.Errorf("unable to unmarshal file %s : %s", file, err)
 			continue
 			continue
 		}
 		}
 		if wafRule.SecLangFilesRules != nil {
 		if wafRule.SecLangFilesRules != nil {
 			for _, rulesFile := range wafRule.SecLangFilesRules {
 			for _, rulesFile := range wafRule.SecLangFilesRules {
-				c, err := os.ReadFile(rulesFile)
+				fullPath := filepath.Join(w.Datadir, rulesFile)
+				c, err := os.ReadFile(fullPath)
 				if err != nil {
 				if err != nil {
-					log.Errorf("unable to read file %s : %s", rulesFile, err)
+					w.logger.Errorf("unable to read file %s : %s", rulesFile, err)
 					continue
 					continue
 				}
 				}
-				wafRule.MergedRules = append(wafRule.MergedRules, string(c))
+				for _, line := range strings.Split(string(c), "\n") {
+					if strings.HasPrefix(line, "#") {
+						continue
+					}
+					if strings.TrimSpace(line) == "" {
+						continue
+					}
+					wafRule.MergedRules = append(wafRule.MergedRules, line)
+				}
 			}
 			}
 		}
 		}
 		if wafRule.SecLangRules != nil {
 		if wafRule.SecLangRules != nil {
@@ -91,27 +118,48 @@ func (w *WafConfig) LoadWafRules() error {
 
 
 		//compile hooks
 		//compile hooks
 		for _, hook := range wafRule.OnLoad {
 		for _, hook := range wafRule.OnLoad {
-			hook, err = buildHook(hook)
+			compiledHook, err := buildHook(hook)
 			if err != nil {
 			if err != nil {
-				log.Errorf("unable to build hook %s : %s", hook.Filter, err)
+				w.logger.Errorf("unable to build hook %s : %s", hook.Filter, err)
 				continue
 				continue
 			}
 			}
+			wafRule.CompiledOnLoad = append(wafRule.CompiledOnLoad, compiledHook)
 		}
 		}
 
 
 		for _, hook := range wafRule.PreEval {
 		for _, hook := range wafRule.PreEval {
-			hook, err = buildHook(hook)
+			compiledHook, err := buildHook(hook)
 			if err != nil {
 			if err != nil {
-				log.Errorf("unable to build hook %s : %s", hook.Filter, err)
+				w.logger.Errorf("unable to build hook %s : %s", hook.Filter, err)
 				continue
 				continue
 			}
 			}
+			wafRule.CompiledPreEval = append(wafRule.CompiledPreEval, compiledHook)
 		}
 		}
 
 
 		for _, hook := range wafRule.OnMatch {
 		for _, hook := range wafRule.OnMatch {
-			hook, err = buildHook(hook)
+			compiledHook, err := buildHook(hook)
 			if err != nil {
 			if err != nil {
-				log.Errorf("unable to build hook %s : %s", hook.Filter, err)
+				w.logger.Errorf("unable to build hook %s : %s", hook.Filter, err)
 				continue
 				continue
 			}
 			}
+			wafRule.CompiledOnMatch = append(wafRule.CompiledOnMatch, compiledHook)
+		}
+
+		//Run the on_load hooks
+
+		if len(wafRule.CompiledOnLoad) > 0 {
+			w.logger.Infof("Running %d on_load hooks", len(wafRule.CompiledOnLoad))
+			for hookIdx, onLoadHook := range wafRule.CompiledOnLoad {
+				//Ignore filter for on load ?
+				if onLoadHook.Apply != nil {
+					for exprIdx, applyExpr := range onLoadHook.Apply {
+						_, err := expr.Run(applyExpr, nil) //FIXME: give proper env
+						if err != nil {
+							w.logger.Errorf("unable to run apply for on_load rule %s : %s", wafRule.OnLoad[hookIdx].Apply[exprIdx], err)
+							continue
+						}
+					}
+				}
+			}
 		}
 		}
 
 
 		if wafRule.MergedRules != nil {
 		if wafRule.MergedRules != nil {
@@ -121,14 +169,26 @@ func (w *WafConfig) LoadWafRules() error {
 				w.InbandRules = append(w.InbandRules, wafRule)
 				w.InbandRules = append(w.InbandRules, wafRule)
 			}
 			}
 		} else {
 		} else {
-			log.Warnf("no rules found in file %s ??", file)
+			w.logger.Warnf("no rules found in file %s ??", file)
 		}
 		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
 func NewWafConfig() *WafConfig {
 func NewWafConfig() *WafConfig {
-	return &WafConfig{}
+	//FIXME: find a better way to get the datadir
+	clog := log.New()
+	if err := types.ConfigureLogger(clog); err != nil {
+		//return nil, fmt.Errorf("while configuring datasource logger: %w", err)
+		return nil
+	}
+	logger := clog.WithFields(log.Fields{
+		"type": "waf-config",
+	})
+
+	initWafHelpers()
+
+	return &WafConfig{Datadir: csconfig.DataDir, logger: logger}
 }
 }
 
 
 func (w *WafRule) String() string {
 func (w *WafRule) String() string {

+ 26 - 0
pkg/waf/waf_expr_lib.go

@@ -0,0 +1,26 @@
+package waf
+
+//This is a copy paste from expr_lib.go, we probably want to only have one ?
+
+type exprCustomFunc struct {
+	name      string
+	function  func(params ...any) (any, error)
+	signature []interface{}
+}
+
+var exprFuncs = []exprCustomFunc{
+	{
+		name:     "SetRulesToInband",
+		function: SetRulesToInband,
+		signature: []interface{}{
+			new(func() error),
+		},
+	},
+	{
+		name:     "SetRulesToOutOfBand",
+		function: SetRulesToOutOfBand,
+		signature: []interface{}{
+			new(func() error),
+		},
+	},
+}

+ 41 - 0
pkg/waf/waf_helpers.go

@@ -0,0 +1,41 @@
+package waf
+
+import (
+	"github.com/antonmedv/expr"
+	"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
+)
+
+var exprFunctionOptions []expr.Option
+
+func initWafHelpers() {
+	exprFunctionOptions = []expr.Option{}
+	for _, function := range exprFuncs {
+		exprFunctionOptions = append(exprFunctionOptions,
+			expr.Function(function.name,
+				function.function,
+				function.signature...,
+			))
+	}
+}
+
+func GetExprWAFOptions(ctx map[string]interface{}) []expr.Option {
+	baseHelpers := exprhelpers.GetExprOptions(ctx)
+
+	for _, function := range exprFuncs {
+		baseHelpers = append(baseHelpers,
+			expr.Function(function.name,
+				function.function,
+				function.signature...,
+			))
+	}
+	return baseHelpers
+}
+
+func SetRulesToInband(params ...any) (any, error) {
+
+	return nil, nil
+}
+
+func SetRulesToOutOfBand(params ...any) (any, error) {
+	return nil, nil
+}