This commit is contained in:
Sebastien Blot 2023-06-09 13:01:58 +02:00
commit a2e6359880
No known key found for this signature in database
GPG key ID: DFC2902F40449F6A
11 changed files with 541 additions and 32 deletions

View file

@ -256,6 +256,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
rootCmd.AddCommand(NewHubTestCmd())
rootCmd.AddCommand(NewNotificationsCmd())
rootCmd.AddCommand(NewSupportCmd())
rootCmd.AddCommand(NewWafRulesCmd())
if fflag.CscliSetup.IsEnabled() {
rootCmd.AddCommand(NewSetupCmd())

View file

@ -131,6 +131,8 @@ func compInstalledItems(itemType string, args []string, toComplete string) ([]st
items, err = cwhub.GetInstalledPostOverflowsAsString()
case cwhub.COLLECTIONS:
items, err = cwhub.GetInstalledCollectionsAsString()
case cwhub.WAF_RULES:
items, err = cwhub.GetInstalledWafRulesAsString()
default:
return nil, cobra.ShellCompDirectiveDefault
}
@ -324,6 +326,8 @@ func ShowMetrics(hubItem *cwhub.Item) {
}
ShowMetrics(hubItem)
}
case cwhub.WAF_RULES:
log.Fatalf("FIXME: not implemented yet")
default:
log.Errorf("item of type '%s' is unknown", hubItem.Type)
}

View file

@ -0,0 +1,196 @@
package main
import (
"fmt"
"github.com/fatih/color"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
)
func NewWafRulesCmd() *cobra.Command {
var cmdWafRules = &cobra.Command{
Use: "waf-rules [action] [config]",
Short: "Install/Remove/Upgrade/Inspect waf-rule(s) from hub",
Example: `cscli waf-rules install crowdsecurity/core-rule-set
cscli waf-rules inspect crowdsecurity/core-rule-set
cscli waf-rules upgrade crowdsecurity/core-rule-set
cscli waf-rules list
cscli waf-rules remove crowdsecurity/core-rule-set
`,
Args: cobra.MinimumNArgs(1),
Aliases: []string{"waf-rule"},
DisableAutoGenTag: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := csConfig.LoadHub(); err != nil {
log.Fatal(err)
}
if csConfig.Hub == nil {
return fmt.Errorf("you must configure cli before interacting with hub")
}
if err := cwhub.SetHubBranch(); err != nil {
return fmt.Errorf("error while setting hub branch: %s", err)
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Info("Run 'sudo cscli hub update' to get the hub index")
log.Fatalf("Failed to get Hub index : %v", err)
}
return nil
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
if cmd.Name() == "inspect" || cmd.Name() == "list" {
return
}
log.Infof(ReloadMessage())
},
}
cmdWafRules.AddCommand(NewWafRulesInstallCmd())
cmdWafRules.AddCommand(NewWafRulesRemoveCmd())
cmdWafRules.AddCommand(NewWafRulesUpgradeCmd())
cmdWafRules.AddCommand(NewWafRulesInspectCmd())
cmdWafRules.AddCommand(NewWafRulesListCmd())
return cmdWafRules
}
func NewWafRulesInstallCmd() *cobra.Command {
var ignoreError bool
var cmdWafRulesInstall = &cobra.Command{
Use: "install [config]",
Short: "Install given waf-rule(s)",
Long: `Fetch and install given waf-rule(s) from hub`,
Example: `cscli waf-rules install crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1),
DisableAutoGenTag: true,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compAllItems(cwhub.WAF_RULES, args, toComplete)
},
Run: func(cmd *cobra.Command, args []string) {
for _, name := range args {
t := cwhub.GetItem(cwhub.WAF_RULES, name)
if t == nil {
nearestItem, score := GetDistance(cwhub.WAF_RULES, name)
Suggest(cwhub.WAF_RULES, name, nearestItem.Name, score, ignoreError)
continue
}
if err := cwhub.InstallItem(csConfig, name, cwhub.WAF_RULES, forceAction, downloadOnly); err != nil {
if ignoreError {
log.Errorf("Error while installing '%s': %s", name, err)
} else {
log.Fatalf("Error while installing '%s': %s", name, err)
}
}
}
},
}
cmdWafRulesInstall.PersistentFlags().BoolVarP(&downloadOnly, "download-only", "d", false, "Only download packages, don't enable")
cmdWafRulesInstall.PersistentFlags().BoolVar(&forceAction, "force", false, "Force install : Overwrite tainted and outdated files")
cmdWafRulesInstall.PersistentFlags().BoolVar(&ignoreError, "ignore", false, "Ignore errors when installing multiple waf rules")
return cmdWafRulesInstall
}
func NewWafRulesRemoveCmd() *cobra.Command {
var cmdWafRulesRemove = &cobra.Command{
Use: "remove [config]",
Short: "Remove given waf-rule(s)",
Long: `Remove given waf-rule(s) from hub`,
Aliases: []string{"delete"},
Example: `cscli waf-rules remove crowdsec/xxx crowdsec/xyz`,
DisableAutoGenTag: true,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compInstalledItems(cwhub.WAF_RULES, args, toComplete)
},
Run: func(cmd *cobra.Command, args []string) {
if all {
cwhub.RemoveMany(csConfig, cwhub.WAF_RULES, "", all, purge, forceAction)
return
}
if len(args) == 0 {
log.Fatalf("Specify at least one waf rule to remove or '--all' flag.")
}
for _, name := range args {
cwhub.RemoveMany(csConfig, cwhub.WAF_RULES, name, all, purge, forceAction)
}
},
}
cmdWafRulesRemove.PersistentFlags().BoolVar(&purge, "purge", false, "Delete source file too")
cmdWafRulesRemove.PersistentFlags().BoolVar(&forceAction, "force", false, "Force remove : Remove tainted and outdated files")
cmdWafRulesRemove.PersistentFlags().BoolVar(&all, "all", false, "Delete all the waf rules")
return cmdWafRulesRemove
}
func NewWafRulesUpgradeCmd() *cobra.Command {
var cmdWafRulesUpgrade = &cobra.Command{
Use: "upgrade [config]",
Short: "Upgrade given waf-rule(s)",
Long: `Fetch and upgrade given waf-rule(s) from hub`,
Example: `cscli waf-rules upgrade crowdsec/xxx crowdsec/xyz`,
DisableAutoGenTag: true,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compInstalledItems(cwhub.WAF_RULES, args, toComplete)
},
Run: func(cmd *cobra.Command, args []string) {
if all {
cwhub.UpgradeConfig(csConfig, cwhub.WAF_RULES, "", forceAction)
} else {
if len(args) == 0 {
log.Fatalf("no target waf rule to upgrade")
}
for _, name := range args {
cwhub.UpgradeConfig(csConfig, cwhub.WAF_RULES, name, forceAction)
}
}
},
}
cmdWafRulesUpgrade.PersistentFlags().BoolVar(&all, "all", false, "Upgrade all the waf rules")
cmdWafRulesUpgrade.PersistentFlags().BoolVar(&forceAction, "force", false, "Force upgrade : Overwrite tainted and outdated files")
return cmdWafRulesUpgrade
}
func NewWafRulesInspectCmd() *cobra.Command {
var cmdWafRulesInspect = &cobra.Command{
Use: "inspect [name]",
Short: "Inspect given waf rule",
Long: `Inspect given waf rule`,
Example: `cscli waf-rules inspect crowdsec/xxx`,
DisableAutoGenTag: true,
Args: cobra.MinimumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compInstalledItems(cwhub.WAF_RULES, args, toComplete)
},
Run: func(cmd *cobra.Command, args []string) {
InspectItem(args[0], cwhub.WAF_RULES)
},
}
cmdWafRulesInspect.PersistentFlags().StringVarP(&prometheusURL, "url", "u", "", "Prometheus url")
return cmdWafRulesInspect
}
func NewWafRulesListCmd() *cobra.Command {
var cmdWafRulesList = &cobra.Command{
Use: "list [name]",
Short: "List all waf rules or given one",
Long: `List all waf rules or given one`,
Example: `cscli waf-rules list
cscli waf-rules list crowdsecurity/xxx`,
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
ListItems(color.Output, []string{cwhub.WAF_RULES}, args, false, true, all)
},
}
cmdWafRulesList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well")
return cmdWafRulesList
}

2
go.mod
View file

@ -70,7 +70,7 @@ require (
github.com/blackfireio/osinfo v1.0.3
github.com/bluele/gcache v0.0.2
github.com/cespare/xxhash/v2 v2.1.2
github.com/corazawaf/coraza/v3 v3.0.0-rc.2
github.com/corazawaf/coraza/v3 v3.0.0-00010101000000-000000000000
github.com/coreos/go-systemd/v22 v22.5.0
github.com/crowdsecurity/go-cs-lib v0.0.0-20230531105801-4c1535c2b3bd
github.com/goccy/go-yaml v1.9.7

View file

@ -7,6 +7,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"
@ -14,6 +15,7 @@ import (
corazatypes "github.com/corazawaf/coraza/v3/types"
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
"github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/crowdsecurity/crowdsec/pkg/waf"
"github.com/crowdsecurity/go-cs-lib/pkg/trace"
"github.com/google/uuid"
"github.com/pkg/errors"
@ -48,23 +50,6 @@ type WafSourceConfig struct {
configuration.DataSourceCommonCfg `yaml:",inline"`
}
/*
type DataSource interface {
GetMetrics() []prometheus.Collector // Returns pointers to metrics that are managed by the module
GetAggregMetrics() []prometheus.Collector // Returns pointers to metrics that are managed by the module (aggregated mode, limits cardinality)
UnmarshalConfig([]byte) error // Decode and pre-validate the YAML datasource - anything that can be checked before runtime
Configure([]byte, *log.Entry) error // Complete the YAML datasource configuration and perform runtime checks.
ConfigureByDSN(string, map[string]string, *log.Entry, string) error // Configure the datasource
GetMode() string // Get the mode (TAIL, CAT or SERVER)
GetName() string // Get the name of the module
OneShotAcquisition(chan types.Event, *tomb.Tomb) error // Start one shot acquisition(eg, cat a file)
StreamingAcquisition(chan types.Event, *tomb.Tomb) error // Start live acquisition (eg, tail a file)
CanRun() error // Whether the datasource can run or not (eg, journalctl on BSD is a non-sense)
GetUuid() string // Get the unique identifier of the datasource
Dump() interface{}
}
*/
func (w *WafSource) GetMetrics() []prometheus.Collector {
return nil
}
@ -102,6 +87,7 @@ func (w *WafSource) UnmarshalConfig(yamlConfig []byte) error {
if w.config.Mode == "" {
w.config.Mode = configuration.TAIL_MODE
}
return nil
}
@ -129,11 +115,32 @@ func (w *WafSource) Configure(yamlConfig []byte, logger *log.Entry) error {
Handler: w.mux,
}
crowdsecWafConfig := waf.NewWafConfig()
err = crowdsecWafConfig.LoadWafRules()
if err != nil {
return fmt.Errorf("cannot load WAF rules: %w", err)
}
var inBandRules string
for _, rule := range crowdsecWafConfig.InbandRules {
inBandRules += rule.String() + "\n"
}
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
inbandwaf, err := coraza.NewWAF(
coraza.NewWAFConfig().
WithErrorCallback(logError).
WithDirectivesFromFile("coraza_inband.conf"),
WithDirectives(inBandRules).WithRootFS(fs),
)
if err != nil {
@ -144,8 +151,8 @@ func (w *WafSource) Configure(yamlConfig []byte, logger *log.Entry) error {
//out-of-band waf : log only
outofbandwaf, err := coraza.NewWAF(
coraza.NewWAFConfig().
WithErrorCallback(logError).
WithDirectivesFromFile("coraza_outofband.conf"),
WithErrorCallback(logError), //.
//WithDirectivesFromFile("coraza_outofband.conf"),
)
if err != nil {
return errors.Wrap(err, "Cannot create WAF")
@ -269,6 +276,8 @@ func processReqWithEngine(waf coraza.WAF, r ParsedRequest, uuid string, wafType
//this method is not exported by coraza, so we have to do it ourselves.
//ideally, this would be dealt with by expr code, and we provide helpers to manipulate the transaction object?\
//var txx experimental.FullTransaction
//txx := experimental.ToFullInterface(tx)
//txx = tx.(experimental.FullTransaction)
//txx.RemoveRuleByID(1)

View file

@ -11,6 +11,8 @@ import (
"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
type CrowdsecServiceCfg struct {
Enable *bool `yaml:"enable"`
@ -106,6 +108,8 @@ func (c *Config) LoadCrowdsec() error {
c.Crowdsec.HubDir = c.ConfigPaths.HubDir
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 {
c.Crowdsec.ParserRoutinesCount = 1
}

View file

@ -20,7 +20,8 @@ var PARSERS = "parsers"
var PARSERS_OVFLW = "postoverflows"
var SCENARIOS = "scenarios"
var COLLECTIONS = "collections"
var ItemTypes = []string{PARSERS, PARSERS_OVFLW, SCENARIOS, COLLECTIONS}
var WAF_RULES = "waf-rules"
var ItemTypes = []string{PARSERS, PARSERS_OVFLW, SCENARIOS, COLLECTIONS, WAF_RULES}
var hubIdx map[string]map[string]Item
@ -42,7 +43,7 @@ type ItemHubStatus struct {
Status string `json:"status"`
}
//Item can be : parsed, scenario, collection
// Item can be : parsed, scenario, collection
type Item struct {
/*descriptive info*/
Type string `yaml:"type,omitempty" json:"type,omitempty"` //parser|postoverflows|scenario|collection(|enrich)
@ -77,6 +78,7 @@ type Item struct {
PostOverflows []string `yaml:"postoverflows,omitempty" json:"postoverflows,omitempty"`
Scenarios []string `yaml:"scenarios,omitempty" json:"scenarios,omitempty"`
Collections []string `yaml:"collections,omitempty" json:"collections,omitempty"`
WafRules []string `yaml:"waf_rules,omitempty" json:"waf_rules,omitempty"`
}
func (i *Item) toHubStatus() ItemHubStatus {
@ -107,7 +109,7 @@ var skippedTainted = 0
var ReferenceMissingError = errors.New("Reference(s) missing in collection")
var MissingHubIndex = errors.New("hub index can't be found")
//GetVersionStatus : semver requires 'v' prefix
// GetVersionStatus : semver requires 'v' prefix
func GetVersionStatus(v *Item) int {
return semver.Compare("v"+v.Version, "v"+v.LocalVersion)
}
@ -140,7 +142,7 @@ func GetItemMap(itemType string) map[string]Item {
return m
}
//GetItemByPath retrieves the item from hubIdx based on the path. To achieve this it will resolve symlink to find associated hub item.
// GetItemByPath retrieves the item from hubIdx based on the path. To achieve this it will resolve symlink to find associated hub item.
func GetItemByPath(itemType string, itemPath string) (*Item, error) {
/*try to resolve symlink*/
finalName := ""
@ -199,14 +201,14 @@ func AddItem(itemType string, item Item) error {
}
func DisplaySummary() {
log.Printf("Loaded %d collecs, %d parsers, %d scenarios, %d post-overflow parsers", len(hubIdx[COLLECTIONS]),
len(hubIdx[PARSERS]), len(hubIdx[SCENARIOS]), len(hubIdx[PARSERS_OVFLW]))
log.Printf("Loaded %d collecs, %d parsers, %d scenarios, %d post-overflow parsers, %d waf rules", len(hubIdx[COLLECTIONS]),
len(hubIdx[PARSERS]), len(hubIdx[SCENARIOS]), len(hubIdx[PARSERS_OVFLW]), len(hubIdx[WAF_RULES]))
if skippedLocal > 0 || skippedTainted > 0 {
log.Printf("unmanaged items : %d local, %d tainted", skippedLocal, skippedTainted)
}
}
//returns: human-text, Enabled, Warning, Unmanaged
// returns: human-text, Enabled, Warning, Unmanaged
func ItemStatus(v Item) (string, bool, bool, bool) {
strret := "disabled"
Ok := false
@ -341,7 +343,34 @@ func GetInstalledCollections() ([]Item, error) {
return retItems, nil
}
//Returns a list of entries for packages : name, status, local_path, local_version, utf8_status (fancy)
func GetInstalledWafRules() ([]Item, error) {
var retItems []Item
if _, ok := hubIdx[WAF_RULES]; !ok {
return nil, fmt.Errorf("no waf rules in hubIdx")
}
for _, item := range hubIdx[WAF_RULES] {
if item.Installed {
retItems = append(retItems, item)
}
}
return retItems, nil
}
func GetInstalledWafRulesAsString() ([]string, error) {
var retStr []string
items, err := GetInstalledWafRules()
if err != nil {
return nil, errors.Wrap(err, "while fetching waf rules")
}
for _, it := range items {
retStr = append(retStr, it.Name)
}
return retStr, nil
}
// Returns a list of entries for packages : name, status, local_path, local_version, utf8_status (fancy)
func GetHubStatusForItemType(itemType string, name string, all bool) []ItemHubStatus {
if _, ok := hubIdx[itemType]; !ok {
log.Errorf("type %s doesn't exist", itemType)

View file

@ -90,8 +90,11 @@ func parser_visit(path string, f os.DirEntry, err error) error {
} else if stage == COLLECTIONS {
ftype = COLLECTIONS
stage = ""
} else if stage == WAF_RULES {
ftype = WAF_RULES
stage = ""
} else if ftype != PARSERS && ftype != PARSERS_OVFLW /*its a PARSER / PARSER_OVFLW with a stage */ {
return fmt.Errorf("unknown configuration type for file '%s'", path)
return fmt.Errorf("unknown configuration type %s for file '%s'", ftype, path)
}
log.Tracef("CORRECTED [%s] by [%s] in stage [%s] of type [%s]", fname, fauthor, stage, ftype)
@ -102,7 +105,7 @@ func parser_visit(path string, f os.DirEntry, err error) error {
when the collection is installed, both files are created
*/
//non symlinks are local user files or hub files
if f.Type() & os.ModeSymlink == 0 {
if f.Type()&os.ModeSymlink == 0 {
local = true
log.Tracef("%s isn't a symlink", path)
} else {
@ -406,7 +409,7 @@ func LoadPkgIndex(buff []byte) (map[string]map[string]Item, error) {
/*if it's a collection, check its sub-items are present*/
//XX should be done later
if itemType == COLLECTIONS {
var tmp = [][]string{item.Parsers, item.PostOverflows, item.Scenarios, item.Collections}
var tmp = [][]string{item.Parsers, item.PostOverflows, item.Scenarios, item.Collections, item.WafRules}
for idx, ptr := range tmp {
ptrtype := ItemTypes[idx]
for _, p := range ptr {

196
pkg/waf/waf.go Normal file
View file

@ -0,0 +1,196 @@
package waf
import (
"os"
"path/filepath"
"strings"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
"github.com/crowdsecurity/crowdsec/pkg/types"
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 `yaml:"-"`
}
type CompiledHook struct {
Filter *vm.Program `yaml:"-"`
Apply []*vm.Program `yaml:"-"`
}
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"`
CompiledOnLoad []CompiledHook `yaml:"-"`
CompiledPreEval []CompiledHook `yaml:"-"`
CompiledOnMatch []CompiledHook `yaml:"-"`
MergedRules []string `yaml:"-"`
OutOfBand bool `yaml:"-"`
}
type WafConfig struct {
InbandRules []WafRule
OutOfBandRules []WafRule
Datadir string
logger *log.Entry
}
func buildHook(hook Hook) (CompiledHook, error) {
compiledHook := CompiledHook{}
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 CompiledHook{}, err
}
compiledHook.Filter = program
}
for _, apply := range hook.Apply {
program, err := expr.Compile(apply, GetExprWAFOptions(map[string]interface{}{
"WafRules": []WafRule{},
})...)
if err != nil {
log.Errorf("unable to compile apply %s : %s", apply, err)
return CompiledHook{}, err
}
compiledHook.Apply = append(compiledHook.Apply, program)
}
return compiledHook, 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)
}
}
w.logger.Infof("Loading %d waf files", len(files))
for _, file := range files {
fileContent, err := os.ReadFile(file) //FIXME: actually read from datadir
if err != nil {
w.logger.Errorf("unable to read file %s : %s", file, err)
continue
}
wafRule := WafRule{}
err = yaml.Unmarshal(fileContent, &wafRule)
if err != nil {
w.logger.Errorf("unable to unmarshal file %s : %s", file, err)
continue
}
if wafRule.SecLangFilesRules != nil {
for _, rulesFile := range wafRule.SecLangFilesRules {
fullPath := filepath.Join(w.Datadir, rulesFile)
c, err := os.ReadFile(fullPath)
if err != nil {
w.logger.Errorf("unable to read file %s : %s", rulesFile, err)
continue
}
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 {
wafRule.MergedRules = append(wafRule.MergedRules, wafRule.SecLangRules...)
}
//compile hooks
for _, hook := range wafRule.OnLoad {
compiledHook, err := buildHook(hook)
if err != nil {
w.logger.Errorf("unable to build hook %s : %s", hook.Filter, err)
continue
}
wafRule.CompiledOnLoad = append(wafRule.CompiledOnLoad, compiledHook)
}
for _, hook := range wafRule.PreEval {
compiledHook, err := buildHook(hook)
if err != nil {
w.logger.Errorf("unable to build hook %s : %s", hook.Filter, err)
continue
}
wafRule.CompiledPreEval = append(wafRule.CompiledPreEval, compiledHook)
}
for _, hook := range wafRule.OnMatch {
compiledHook, err := buildHook(hook)
if err != nil {
w.logger.Errorf("unable to build hook %s : %s", hook.Filter, err)
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.OutOfBand {
w.OutOfBandRules = append(w.OutOfBandRules, wafRule)
} else {
w.InbandRules = append(w.InbandRules, wafRule)
}
} else {
w.logger.Warnf("no rules found in file %s ??", file)
}
}
return nil
}
func NewWafConfig() *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 {
return strings.Join(w.MergedRules, "\n")
}

26
pkg/waf/waf_expr_lib.go Normal file
View file

@ -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
pkg/waf/waf_helpers.go Normal file
View file

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