|
@@ -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 {
|