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 (
"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
}

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

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{}) {
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)))

View file

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

View file

@ -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 = ""