浏览代码

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

Thibault "bui" Koechlin 4 年之前
父节点
当前提交
2e76097d35
共有 5 个文件被更改,包括 71 次插入13 次删除
  1. 11 2
      pkg/csprofiles/csprofiles.go
  2. 7 5
      pkg/database/alerts.go
  3. 1 1
      pkg/exprhelpers/visitor.go
  4. 39 5
      pkg/leakybucket/overflows.go
  5. 13 0
      pkg/types/event.go

+ 11 - 2
pkg/csprofiles/csprofiles.go

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

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

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

+ 39 - 5
pkg/leakybucket/overflows.go

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

+ 13 - 0
pkg/types/event.go

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