1dcf9d1ae1
* new expr debugger --------- Co-authored-by: mmetc <92726601+mmetc@users.noreply.github.com>
104 lines
3.2 KiB
Go
104 lines
3.2 KiB
Go
package leakybucket
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/antonmedv/expr"
|
|
"github.com/antonmedv/expr/vm"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
|
)
|
|
|
|
// ResetFilter allows to kill the bucket (without overflowing), if a particular condition is met.
|
|
// An example would be a scenario to detect aggressive crawlers that *do not* fetch any static resources :
|
|
// type : leaky
|
|
// filter: "evt.Meta.log_type == 'http_access-log'
|
|
// reset_filter: evt.Parsed.request endswith '.css'
|
|
// ....
|
|
// Thus, if the bucket receives a request that matches fetching a static resource (here css), it cancels itself
|
|
|
|
type CancelOnFilter struct {
|
|
CancelOnFilter *vm.Program
|
|
Debug bool
|
|
}
|
|
|
|
var cancelExprCacheLock sync.Mutex
|
|
var cancelExprCache map[string]struct {
|
|
CancelOnFilter *vm.Program
|
|
}
|
|
|
|
func (u *CancelOnFilter) OnBucketPour(bucketFactory *BucketFactory) func(types.Event, *Leaky) *types.Event {
|
|
return func(msg types.Event, leaky *Leaky) *types.Event {
|
|
var condition, ok bool
|
|
if u.CancelOnFilter != nil {
|
|
leaky.logger.Tracef("running cancel_on filter")
|
|
output, err := exprhelpers.Run(u.CancelOnFilter, map[string]interface{}{"evt": &msg}, leaky.logger, u.Debug)
|
|
if err != nil {
|
|
leaky.logger.Warningf("cancel_on error : %s", err)
|
|
return &msg
|
|
}
|
|
if condition, ok = output.(bool); !ok {
|
|
leaky.logger.Warningf("cancel_on, unexpected non-bool return : %T", output)
|
|
return &msg
|
|
}
|
|
if condition {
|
|
leaky.logger.Debugf("reset_filter matched, kill bucket")
|
|
leaky.Suicide <- true
|
|
return nil //counter intuitively, we need to keep the message so that it doesn't trigger an endless loop
|
|
}
|
|
leaky.logger.Debugf("reset_filter didn't match")
|
|
}
|
|
return &msg
|
|
}
|
|
}
|
|
|
|
func (u *CancelOnFilter) OnBucketOverflow(bucketFactory *BucketFactory) func(*Leaky, types.RuntimeAlert, *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
|
return func(leaky *Leaky, alert types.RuntimeAlert, queue *types.Queue) (types.RuntimeAlert, *types.Queue) {
|
|
return alert, queue
|
|
}
|
|
}
|
|
|
|
func (u *CancelOnFilter) AfterBucketPour(bucketFactory *BucketFactory) func(types.Event, *Leaky) *types.Event {
|
|
return func(msg types.Event, leaky *Leaky) *types.Event {
|
|
return &msg
|
|
}
|
|
}
|
|
|
|
func (u *CancelOnFilter) OnBucketInit(bucketFactory *BucketFactory) error {
|
|
var err error
|
|
var compiledExpr struct {
|
|
CancelOnFilter *vm.Program
|
|
}
|
|
|
|
if cancelExprCache == nil {
|
|
cancelExprCache = make(map[string]struct {
|
|
CancelOnFilter *vm.Program
|
|
})
|
|
}
|
|
|
|
cancelExprCacheLock.Lock()
|
|
if compiled, ok := cancelExprCache[bucketFactory.CancelOnFilter]; ok {
|
|
cancelExprCacheLock.Unlock()
|
|
u.CancelOnFilter = compiled.CancelOnFilter
|
|
return nil
|
|
} else {
|
|
cancelExprCacheLock.Unlock()
|
|
//release the lock during compile
|
|
|
|
compiledExpr.CancelOnFilter, err = expr.Compile(bucketFactory.CancelOnFilter, exprhelpers.GetExprOptions(map[string]interface{}{"evt": &types.Event{}})...)
|
|
if err != nil {
|
|
bucketFactory.logger.Errorf("reset_filter compile error : %s", err)
|
|
return err
|
|
}
|
|
u.CancelOnFilter = compiledExpr.CancelOnFilter
|
|
if bucketFactory.Debug {
|
|
u.Debug = true
|
|
}
|
|
cancelExprCacheLock.Lock()
|
|
cancelExprCache[bucketFactory.CancelOnFilter] = compiledExpr
|
|
cancelExprCacheLock.Unlock()
|
|
}
|
|
return err
|
|
}
|