diff --git a/pkg/acquisition/modules/waf/waf.go b/pkg/acquisition/modules/waf/waf.go index b1255247a..eb21d51bb 100644 --- a/pkg/acquisition/modules/waf/waf.go +++ b/pkg/acquisition/modules/waf/waf.go @@ -10,6 +10,7 @@ import ( "github.com/corazawaf/coraza/v3" "github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration" "github.com/crowdsecurity/crowdsec/pkg/types" + "github.com/crowdsecurity/crowdsec/pkg/waf" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" @@ -110,7 +111,23 @@ func (w *WafSource) Configure(yamlConfig []byte, logger *log.Entry) error { Handler: w.mux, } - waf, err := coraza.NewWAF(coraza.NewWAFConfig()) + crowdsecWafConfig := waf.NewWafConfig() + + err = crowdsecWafConfig.LoadWafRules() + + if err != nil { + return fmt.Errorf("Cannot load WAF rules: %w", err) + } + + var rules string + + for _, rule := range crowdsecWafConfig.InbandRules { + rules += rule.String() + "\n" + } + + w.logger.Infof("Loading rules %+v", rules) + + waf, err := coraza.NewWAF(coraza.NewWAFConfig().WithDirectives(rules)) if err != nil { return errors.Wrap(err, "Cannot create WAF") diff --git a/pkg/waf/waf.go b/pkg/waf/waf.go new file mode 100644 index 000000000..f86a74199 --- /dev/null +++ b/pkg/waf/waf.go @@ -0,0 +1,136 @@ +package waf + +import ( + "os" + "strings" + + "github.com/antonmedv/expr" + "github.com/antonmedv/expr/vm" + "github.com/crowdsecurity/crowdsec/pkg/cwhub" + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" +) + +type Hook struct { + Filter string `yaml:"filter"` + FilterExpr *vm.Program `yaml:"-"` + OnSuccess string `yaml:"on_success"` + Apply []string `yaml:"apply"` + ApplyExpr []*vm.Program +} + +type WafRule struct { + SecLangFilesRules []string `yaml:"seclang_files_rules"` + SecLangRules []string `yaml:"seclang_rules"` + OnLoad []Hook `yaml:"on_load"` + PreEval []Hook `yaml:"pre_eval"` + OnMatch []Hook `yaml:"on_match"` + MergedRules []string `yaml:"-"` + OutOfBand bool `yaml:"-"` +} + +type WafConfig struct { + InbandRules []WafRule + OutOfBandRules []WafRule +} + +func buildHook(hook Hook) (Hook, error) { + if hook.Filter != "" { + program, err := expr.Compile(hook.Filter) //FIXME: opts + if err != nil { + log.Errorf("unable to compile filter %s : %s", hook.Filter, err) + return Hook{}, err + } + hook.FilterExpr = program + } + for _, apply := range hook.Apply { + program, err := expr.Compile(apply) //FIXME: opts + if err != nil { + log.Errorf("unable to compile apply %s : %s", apply, err) + return Hook{}, err + } + hook.ApplyExpr = append(hook.ApplyExpr, program) + } + return hook, nil +} + +func (w *WafConfig) LoadWafRules() error { + var files []string + for _, hubWafRuleItem := range cwhub.GetItemMap(cwhub.WAF_RULES) { + if hubWafRuleItem.Installed { + files = append(files, hubWafRuleItem.LocalPath) + } + } + log.Infof("Loading %d waf files", len(files)) + for _, file := range files { + + fileContent, err := os.ReadFile(file) //FIXME: actually read from datadir + if err != nil { + log.Errorf("unable to read file %s : %s", file, err) + continue + } + wafRule := WafRule{} + err = yaml.Unmarshal(fileContent, &wafRule) + if err != nil { + log.Errorf("unable to unmarshal file %s : %s", file, err) + continue + } + if wafRule.SecLangFilesRules != nil { + for _, rulesFile := range wafRule.SecLangFilesRules { + c, err := os.ReadFile(rulesFile) + if err != nil { + log.Errorf("unable to read file %s : %s", rulesFile, err) + continue + } + wafRule.MergedRules = append(wafRule.MergedRules, string(c)) + } + } + if wafRule.SecLangRules != nil { + wafRule.MergedRules = append(wafRule.MergedRules, wafRule.SecLangRules...) + } + + //compile hooks + for _, hook := range wafRule.OnLoad { + hook, err = buildHook(hook) + if err != nil { + log.Errorf("unable to build hook %s : %s", hook.Filter, err) + continue + } + } + + for _, hook := range wafRule.PreEval { + hook, err = buildHook(hook) + if err != nil { + log.Errorf("unable to build hook %s : %s", hook.Filter, err) + continue + } + } + + for _, hook := range wafRule.OnMatch { + hook, err = buildHook(hook) + if err != nil { + log.Errorf("unable to build hook %s : %s", hook.Filter, err) + continue + } + } + + if wafRule.MergedRules != nil { + if wafRule.OutOfBand { + w.OutOfBandRules = append(w.OutOfBandRules, wafRule) + } else { + w.InbandRules = append(w.InbandRules, wafRule) + } + } else { + log.Warnf("no rules found in file %s ??", file) + } + } + return nil +} + +func NewWafConfig() *WafConfig { + return &WafConfig{} +} + +func (w *WafRule) String() string { + return strings.Join(w.MergedRules, "\n") +}