Fix overflows of overflows requesting for different decision scope (#499)

This commit is contained in:
Thibault "bui" Koechlin 2020-12-02 17:15:48 +01:00 committed by GitHub
parent 8707140fb2
commit 2e76097d35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 71 additions and 13 deletions

View file

@ -3,6 +3,7 @@ package csprofiles
import ( import (
"fmt" "fmt"
"net" "net"
"strings"
"github.com/antonmedv/expr" "github.com/antonmedv/expr"
"github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/csconfig"
@ -45,14 +46,15 @@ func GenerateDecisionFromProfile(Profile *csconfig.ProfileCfg, Alert *models.Ale
decision.Value = new(string) decision.Value = new(string)
*decision.Value = *Alert.Source.Value *decision.Value = *Alert.Source.Value
if *decision.Scope == types.Ip { if strings.EqualFold(*decision.Scope, types.Ip) {
srcAddr := net.ParseIP(Alert.Source.IP) srcAddr := net.ParseIP(Alert.Source.IP)
if srcAddr == nil { if srcAddr == nil {
return nil, fmt.Errorf("can't parse ip %s", Alert.Source.IP) return nil, fmt.Errorf("can't parse ip %s", Alert.Source.IP)
} }
decision.StartIP = int64(types.IP2Int(srcAddr)) decision.StartIP = int64(types.IP2Int(srcAddr))
decision.EndIP = decision.StartIP 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 : /*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 failed
- the alert is about an IP, but the geolite enrichment isn't present - 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: PROFILE_LOOP:
for _, profile := range Profiles { for _, profile := range Profiles {
matched := false
for eIdx, expression := range profile.RuntimeFilters { for eIdx, expression := range profile.RuntimeFilters {
output, err := expr.Run(expression, exprhelpers.GetExprEnv(map[string]interface{}{"Alert": Alert})) output, err := expr.Run(expression, exprhelpers.GetExprEnv(map[string]interface{}{"Alert": Alert}))
if err != nil { if err != nil {
@ -117,11 +120,13 @@ PROFILE_LOOP:
switch out := output.(type) { switch out := output.(type) {
case bool: case bool:
if out { if out {
matched = true
/*the expression matched, create the associated decision*/ /*the expression matched, create the associated decision*/
subdecisions, err := GenerateDecisionFromProfile(profile, Alert) subdecisions, err := GenerateDecisionFromProfile(profile, Alert)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "while generating decision from profile %s", profile.Name) return nil, errors.Wrapf(err, "while generating decision from profile %s", profile.Name)
} }
decisions = append(decisions, subdecisions...) decisions = append(decisions, subdecisions...)
} else { } else {
if profile.Debug != nil && *profile.Debug { if profile.Debug != nil && *profile.Debug {
@ -132,10 +137,14 @@ PROFILE_LOOP:
break PROFILE_LOOP break PROFILE_LOOP
} }
} }
default: default:
return nil, fmt.Errorf("unexpected type %t (%v) while running '%s'", output, output, profile.Filters[eIdx]) return nil, fmt.Errorf("unexpected type %t (%v) while running '%s'", output, output, profile.Filters[eIdx])
} }
}
if matched {
if profile.OnSuccess == "break" { if profile.OnSuccess == "break" {
break PROFILE_LOOP break PROFILE_LOOP
} }

View file

@ -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) { 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{} ret := []string{}
bulkSize := 20 bulkSize := 20
@ -125,6 +122,10 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
c.Log.Debugf("writting %d items", len(alertList)) c.Log.Debugf("writting %d items", len(alertList))
bulk := make([]*ent.AlertCreate, 0, bulkSize) bulk := make([]*ent.AlertCreate, 0, bulkSize)
for i, alertItem := range alertList { for i, alertItem := range alertList {
var decisions []*ent.Decision
var metas []*ent.Meta
var events []*ent.Event
owner, err := c.QueryMachineByID(machineId) owner, err := c.QueryMachineByID(machineId)
if err != nil { if err != nil {
if errors.Cause(err) != UserNotExists { if errors.Cause(err) != UserNotExists {
@ -212,6 +213,7 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
} }
} }
alertB := c.Ent.Alert. alertB := c.Ent.Alert.
Create(). Create().
SetScenario(*alertItem.Scenario). SetScenario(*alertItem.Scenario).
@ -245,7 +247,7 @@ func (c *Client) CreateAlertBulk(machineId string, alertList []*models.Alert) ([
if len(bulk) == bulkSize { if len(bulk) == bulkSize {
alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX) alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX)
if err != nil { 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 { for _, alert := range alerts {
ret = append(ret, strconv.Itoa(alert.ID)) 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) alerts, err := c.Ent.Alert.CreateBulk(bulk...).Save(c.CTX)
if err != nil { 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 { for _, alert := range alerts {

View file

@ -114,7 +114,7 @@ again the expr environment given in parameter
*/ */
func (e *ExprDebugger) Run(logger *logrus.Entry, filterResult bool, exprEnv map[string]interface{}) { func (e *ExprDebugger) Run(logger *logrus.Entry, filterResult bool, exprEnv map[string]interface{}) {
if len(e.expression) == 0 { 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 return
} }
logger.Debugf("eval(%s) = %s", e.filter, strings.ToUpper(strconv.FormatBool(filterResult))) logger.Debugf("eval(%s) = %s", e.filter, strings.ToUpper(strconv.FormatBool(filterResult)))

View file

@ -18,12 +18,47 @@ import (
//SourceFromEvent extracts and formats a valid models.Source object from an Event //SourceFromEvent extracts and formats a valid models.Source object from an Event
func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, error) { func SourceFromEvent(evt types.Event, leaky *Leaky) (map[string]models.Source, error) {
src := models.Source{}
srcs := make(map[string]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 { 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 { switch leaky.scopeType.Scope {
case types.Range, types.Ip: case types.Range, types.Ip:
if v, ok := evt.Meta["source_ip"]; ok { 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 { } else if leaky.scopeType.Scope == types.Range {
src.Value = &src.Range src.Value = &src.Range
} }
srcs[src.IP] = src srcs[*src.Value] = src
default: default:
if leaky.scopeType.RunTimeFilter != nil { if leaky.scopeType.RunTimeFilter != nil {
retValue, err := expr.Run(leaky.scopeType.RunTimeFilter, exprhelpers.GetExprEnv(map[string]interface{}{"evt": &evt})) 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 = new(string)
*src.Scope = leaky.scopeType.Scope *src.Scope = leaky.scopeType.Scope
srcs[*src.Value] = src srcs[*src.Value] = src
log.Debugf("source[%s] - %s = %s", leaky.Name, leaky.scopeType.Scope, *src.Value)
} else { } else {
return srcs, fmt.Errorf("empty scope information") 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 //Include source info in format string
sourceStr := "" sourceStr := ""
if len(sources) > 1 { 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 { } else if len(sources) == 1 {
for k, _ := range sources { for k, _ := range sources {
sourceStr = k sourceStr = k

View file

@ -3,6 +3,8 @@ package types
import ( import (
"time" "time"
log "github.com/sirupsen/logrus"
"github.com/antonmedv/expr/vm" "github.com/antonmedv/expr/vm"
"github.com/crowdsecurity/crowdsec/pkg/models" "github.com/crowdsecurity/crowdsec/pkg/models"
) )
@ -38,6 +40,17 @@ type Event struct {
Meta map[string]string `yaml:"Meta,omitempty" json:"Meta,omitempty"` 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 //Move in leakybuckets
const ( const (
Undefined = "" Undefined = ""