91 lines
3.2 KiB
Go
91 lines
3.2 KiB
Go
package csconfig
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/antonmedv/expr"
|
|
"github.com/antonmedv/expr/vm"
|
|
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
|
"github.com/crowdsecurity/crowdsec/pkg/yamlpatch"
|
|
"github.com/pkg/errors"
|
|
log "github.com/sirupsen/logrus"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
//Profile structure(s) are used by the local API to "decide" what kind of decision should be applied when a scenario with an active remediation has been triggered
|
|
type ProfileCfg struct {
|
|
Name string `yaml:"name,omitempty"`
|
|
Debug *bool `yaml:"debug,omitempty"`
|
|
Filters []string `yaml:"filters,omitempty"` //A list of OR'ed expressions. the models.Alert object
|
|
RuntimeFilters []*vm.Program `json:"-" yaml:"-"`
|
|
DebugFilters []*exprhelpers.ExprDebugger `json:"-" yaml:"-"`
|
|
Decisions []models.Decision `yaml:"decisions,omitempty"`
|
|
OnSuccess string `yaml:"on_success,omitempty"` //continue or break
|
|
OnFailure string `yaml:"on_failure,omitempty"` //continue or break
|
|
Notifications []string `yaml:"notifications,omitempty"`
|
|
}
|
|
|
|
func (c *LocalApiServerCfg) LoadProfiles() error {
|
|
if c.ProfilesPath == "" {
|
|
return fmt.Errorf("empty profiles path")
|
|
}
|
|
|
|
patcher := yamlpatch.NewPatcher(c.ProfilesPath, ".local")
|
|
fcontent, err := patcher.PrependedPatchContent()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
reader := bytes.NewReader(fcontent)
|
|
|
|
//process the yaml
|
|
dec := yaml.NewDecoder(reader)
|
|
dec.SetStrict(true)
|
|
for {
|
|
t := ProfileCfg{}
|
|
err = dec.Decode(&t)
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
return errors.Wrapf(err, "while decoding %s", c.ProfilesPath)
|
|
}
|
|
c.Profiles = append(c.Profiles, &t)
|
|
}
|
|
|
|
for pIdx, profile := range c.Profiles {
|
|
var runtimeFilter *vm.Program
|
|
var debugFilter *exprhelpers.ExprDebugger
|
|
|
|
c.Profiles[pIdx].RuntimeFilters = make([]*vm.Program, len(profile.Filters))
|
|
c.Profiles[pIdx].DebugFilters = make([]*exprhelpers.ExprDebugger, len(profile.Filters))
|
|
|
|
for fIdx, filter := range profile.Filters {
|
|
if runtimeFilter, err = expr.Compile(filter, expr.Env(exprhelpers.GetExprEnv(map[string]interface{}{"Alert": &models.Alert{}}))); err != nil {
|
|
return errors.Wrapf(err, "Error compiling filter of %s", profile.Name)
|
|
}
|
|
c.Profiles[pIdx].RuntimeFilters[fIdx] = runtimeFilter
|
|
if debugFilter, err = exprhelpers.NewDebugger(filter, expr.Env(exprhelpers.GetExprEnv(map[string]interface{}{"Alert": &models.Alert{}}))); err != nil {
|
|
log.Debugf("Error compiling debug filter of %s : %s", profile.Name, err)
|
|
// Don't fail if we can't compile the filter - for now
|
|
// return errors.Wrapf(err, "Error compiling debug filter of %s", profile.Name)
|
|
}
|
|
c.Profiles[pIdx].DebugFilters[fIdx] = debugFilter
|
|
}
|
|
|
|
for _, decision := range profile.Decisions {
|
|
if _, err := time.ParseDuration(*decision.Duration); err != nil {
|
|
return errors.Wrapf(err, "Error parsing duration '%s' of %s", *decision.Duration, profile.Name)
|
|
}
|
|
}
|
|
|
|
}
|
|
if len(c.Profiles) == 0 {
|
|
return fmt.Errorf("zero profiles loaded for LAPI")
|
|
}
|
|
return nil
|
|
}
|