From 2e76097d35e638b76d9e5428104e7fa87337b5b6 Mon Sep 17 00:00:00 2001 From: "Thibault \"bui\" Koechlin" Date: Wed, 2 Dec 2020 17:15:48 +0100 Subject: [PATCH] Fix overflows of overflows requesting for different decision scope (#499) --- pkg/csprofiles/csprofiles.go | 13 +++++++++-- pkg/database/alerts.go | 12 ++++++---- pkg/exprhelpers/visitor.go | 2 +- pkg/leakybucket/overflows.go | 44 ++++++++++++++++++++++++++++++++---- pkg/types/event.go | 13 +++++++++++ 5 files changed, 71 insertions(+), 13 deletions(-) diff --git a/pkg/csprofiles/csprofiles.go b/pkg/csprofiles/csprofiles.go index 5a8340ed2..7d816c74f 100644 --- a/pkg/csprofiles/csprofiles.go +++ b/pkg/csprofiles/csprofiles.go @@ -3,6 +3,7 @@ package csprofiles import ( "fmt" "net" + "strings" "github.com/antonmedv/expr" "github.com/crowdsecurity/crowdsec/pkg/csconfig" @@ -45,14 +46,15 @@ func GenerateDecisionFromProfile(Profile *csconfig.ProfileCfg, Alert *models.Ale decision.Value = new(string) *decision.Value = *Alert.Source.Value - if *decision.Scope == types.Ip { + if strings.EqualFold(*decision.Scope, types.Ip) { srcAddr := net.ParseIP(Alert.Source.IP) if srcAddr == nil { return nil, fmt.Errorf("can't parse ip %s", Alert.Source.IP) } decision.StartIP = int64(types.IP2Int(srcAddr)) decision.EndIP = decision.StartIP - } else if *decision.Scope == types.Range { + } else if strings.EqualFold(*decision.Scope, types.Range) { + /*here we're asked to ban a full range. let's keep in mind that it's not always possible : - the alert is about an IP, but the geolite enrichment failed - the alert is about an IP, but the geolite enrichment isn't present @@ -108,6 +110,7 @@ func EvaluateProfiles(Profiles []*csconfig.ProfileCfg, Alert *models.Alert) ([]* } PROFILE_LOOP: for _, profile := range Profiles { + matched := false for eIdx, expression := range profile.RuntimeFilters { output, err := expr.Run(expression, exprhelpers.GetExprEnv(map[string]interface{}{"Alert": Alert})) if err != nil { @@ -117,11 +120,13 @@ PROFILE_LOOP: switch out := output.(type) { case bool: if out { + matched = true /*the expression matched, create the associated decision*/ subdecisions, err := GenerateDecisionFromProfile(profile, Alert) if err != nil { return nil, errors.Wrapf(err, "while generating decision from profile %s", profile.Name) } + decisions = append(decisions, subdecisions...) } else { if profile.Debug != nil && *profile.Debug { @@ -132,10 +137,14 @@ PROFILE_LOOP: break PROFILE_LOOP } } + default: return nil, fmt.Errorf("unexpected type %t (%v) while running '%s'", output, output, profile.Filters[eIdx]) } + + } + if matched { if profile.OnSuccess == "break" { break PROFILE_LOOP } diff --git a/pkg/database/alerts.go b/pkg/database/alerts.go index 13f8d8a20..04c4d61ab 100644 --- a/pkg/database/alerts.go +++ b/pkg/database/alerts.go @@ -115,9 +115,6 @@ func (c *Client) CreateAlert(machineID string, alertList []*models.Alert) ([]str } func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([]string, error) { - var decisions []*ent.Decision - var metas []*ent.Meta - var events []*ent.Event ret := []string{} bulkSize := 20 @@ -125,6 +122,10 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([ c.Log.Debugf("writting %d items", len(alertList)) bulk := make([]*ent.AlertCreate, 0, bulkSize) for i, alertItem := range alertList { + var decisions []*ent.Decision + var metas []*ent.Meta + var events []*ent.Event + owner, err := c.QueryMachineByID(machineId) if err != nil { if errors.Cause(err) != UserNotExists { @@ -212,6 +213,7 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([ } } + alertB := c.Ent.Alert. Create(). SetScenario(*alertItem.Scenario). @@ -245,7 +247,7 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([ if len(bulk) == bulkSize { alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX) if err != nil { - return []string{}, errors.Wrapf(BulkError, "creating alert : %s", err) + return []string{}, errors.Wrapf(BulkError, "bulk creating alert : %s", err) } for _, alert := range alerts { ret = append(ret, strconv.Itoa(alert.ID)) @@ -261,7 +263,7 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([ alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX) if err != nil { - return []string{}, errors.Wrapf(BulkError, "creating alert : %s", err) + return []string{}, errors.Wrapf(BulkError, "leftovers creating alert : %s", err) } for _, alert := range alerts { diff --git a/pkg/exprhelpers/visitor.go b/pkg/exprhelpers/visitor.go index cc0abe379..86bea7986 100644 --- a/pkg/exprhelpers/visitor.go +++ b/pkg/exprhelpers/visitor.go @@ -114,7 +114,7 @@ again the expr environment given in parameter */ func (e *ExprDebugger) Run(logger *logrus.Entry, filterResult bool, exprEnv map[string]interface{}) { if len(e.expression) == 0 { - logger.Debugf("no variable to eval for filter '%s'", e.filter) + logger.Tracef("no variable to eval for filter '%s'", e.filter) return } logger.Debugf("eval(%s) = %s", e.filter, strings.ToUpper(strconv.FormatBool(filterResult))) diff --git a/pkg/leakybucket/overflows.go b/pkg/leakybucket/overflows.go index 866c42e3f..cb74f8810 100644 --- a/pkg/leakybucket/overflows.go +++ b/pkg/leakybucket/overflows.go @@ -18,12 +18,47 @@ import ( //SourceFromEvent extracts and formats a valid models.Source object from an Event func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, error) { - src := models.Source{} srcs := make(map[string]models.Source) + /*if it's already an overflow, we have properly formatted sources. + we can just twitch them to reflect the requested scope*/ if evt.Type == types.OVFLW { - return evt.Overflow.Sources, nil + + for k, v := range evt.Overflow.Sources { + + /*the scopes are already similar, nothing to do*/ + if leaky.scopeType.Scope == *v.Scope { + srcs[k] = v + continue + } + + /*The bucket requires a decision on scope Range */ + if leaky.scopeType.Scope == types.Range { + /*the original bucket was target IPs, check that we do have range*/ + if *v.Scope == types.Ip { + if v.Range != "" { + src := models.Source{} + src.AsName = v.AsName + src.AsNumber = v.AsNumber + src.Cn = v.Cn + src.Latitude = v.Latitude + src.Longitude = v.Longitude + src.Range = v.Range + src.Value = new(string) + src.Scope = new(string) + *src.Value = v.Range + *src.Scope = leaky.scopeType.Scope + srcs[*src.Value] = src + } + } else { + log.Warningf("bucket %s requires scope Range, but can't extrapolate from %s (%s)", + leaky.Name, *v.Scope, *v.Value) + } + } + } + return srcs, nil } + src := models.Source{} switch leaky.scopeType.Scope { case types.Range, types.Ip: if v, ok := evt.Meta["source_ip"]; ok { @@ -74,7 +109,7 @@ func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, e } else if leaky.scopeType.Scope == types.Range { src.Value = &src.Range } - srcs[src.IP] = src + srcs[*src.Value] = src default: if leaky.scopeType.RunTimeFilter != nil { retValue, err := expr.Run(leaky.scopeType.RunTimeFilter, exprhelpers.GetExprEnv(map[string]interface{}{"evt": &evt})) @@ -90,7 +125,6 @@ func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, e src.Scope = new(string) *src.Scope = leaky.scopeType.Scope srcs[*src.Value] = src - log.Debugf("source[%s] - %s = %s", leaky.Name, leaky.scopeType.Scope, *src.Value) } else { return srcs, fmt.Errorf("empty scope information") } @@ -213,7 +247,7 @@ func NewAlert(leaky *Leaky, queue *Queue) (types.RuntimeAlert, error) { //Include source info in format string sourceStr := "" if len(sources) > 1 { - sourceStr = fmt.Sprintf("%d Sources on scope.", len(sources)) + sourceStr = fmt.Sprintf("%d sources", len(sources)) } else if len(sources) == 1 { for k, _ := range sources { sourceStr = k diff --git a/pkg/types/event.go b/pkg/types/event.go index 8ecfae18a..14809d6bd 100644 --- a/pkg/types/event.go +++ b/pkg/types/event.go @@ -3,6 +3,8 @@ package types import ( "time" + log "github.com/sirupsen/logrus" + "github.com/antonmedv/expr/vm" "github.com/crowdsecurity/crowdsec/pkg/models" ) @@ -38,6 +40,17 @@ type Event struct { Meta map[string]string `yaml:"Meta,omitempty" json:"Meta,omitempty"` } +func (e *Event) GetType() string { + if e.Type == OVFLW { + return "overflow" + } else if e.Type == LOG { + return "log" + } else { + log.Warningf("unknown event type for %+v", e) + return "unknown" + } +} + //Move in leakybuckets const ( Undefined = ""