github.com/crowdsecurity/crowdsec@v1.6.1/pkg/leakybucket/reset_filter.go (about)

     1  package leakybucket
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/antonmedv/expr"
     7  	"github.com/antonmedv/expr/vm"
     8  
     9  	"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
    10  	"github.com/crowdsecurity/crowdsec/pkg/types"
    11  )
    12  
    13  // ResetFilter allows to kill the bucket (without overflowing), if a particular condition is met.
    14  // An example would be a scenario to detect aggressive crawlers that *do not* fetch any static resources :
    15  // type : leaky
    16  // filter: "evt.Meta.log_type == 'http_access-log'
    17  // reset_filter: evt.Parsed.request endswith '.css'
    18  // ....
    19  // Thus, if the bucket receives a request that matches fetching a static resource (here css), it cancels itself
    20  
    21  type CancelOnFilter struct {
    22  	CancelOnFilter *vm.Program
    23  	Debug          bool
    24  }
    25  
    26  var cancelExprCacheLock sync.Mutex
    27  var cancelExprCache map[string]struct {
    28  	CancelOnFilter *vm.Program
    29  }
    30  
    31  func (u *CancelOnFilter) OnBucketPour(bucketFactory *BucketFactory) func(types.Event, *Leaky) *types.Event {
    32  	return func(msg types.Event, leaky *Leaky) *types.Event {
    33  		var condition, ok bool
    34  		if u.CancelOnFilter != nil {
    35  			leaky.logger.Tracef("running cancel_on filter")
    36  			output, err := exprhelpers.Run(u.CancelOnFilter, map[string]interface{}{"evt": &msg}, leaky.logger, u.Debug)
    37  			if err != nil {
    38  				leaky.logger.Warningf("cancel_on error : %s", err)
    39  				return &msg
    40  			}
    41  			if condition, ok = output.(bool); !ok {
    42  				leaky.logger.Warningf("cancel_on, unexpected non-bool return : %T", output)
    43  				return &msg
    44  			}
    45  			if condition {
    46  				leaky.logger.Debugf("reset_filter matched, kill bucket")
    47  				leaky.Suicide <- true
    48  				return nil //counter intuitively, we need to keep the message so that it doesn't trigger an endless loop
    49  			}
    50  			leaky.logger.Debugf("reset_filter didn't match")
    51  		}
    52  		return &msg
    53  	}
    54  }
    55  
    56  func (u *CancelOnFilter) OnBucketOverflow(bucketFactory *BucketFactory) func(*Leaky, types.RuntimeAlert, *types.Queue) (types.RuntimeAlert, *types.Queue) {
    57  	return func(leaky *Leaky, alert types.RuntimeAlert, queue *types.Queue) (types.RuntimeAlert, *types.Queue) {
    58  		return alert, queue
    59  	}
    60  }
    61  
    62  func (u *CancelOnFilter) AfterBucketPour(bucketFactory *BucketFactory) func(types.Event, *Leaky) *types.Event {
    63  	return func(msg types.Event, leaky *Leaky) *types.Event {
    64  		return &msg
    65  	}
    66  }
    67  
    68  func (u *CancelOnFilter) OnBucketInit(bucketFactory *BucketFactory) error {
    69  	var err error
    70  	var compiledExpr struct {
    71  		CancelOnFilter *vm.Program
    72  	}
    73  
    74  	if cancelExprCache == nil {
    75  		cancelExprCache = make(map[string]struct {
    76  			CancelOnFilter *vm.Program
    77  		})
    78  	}
    79  
    80  	cancelExprCacheLock.Lock()
    81  	if compiled, ok := cancelExprCache[bucketFactory.CancelOnFilter]; ok {
    82  		cancelExprCacheLock.Unlock()
    83  		u.CancelOnFilter = compiled.CancelOnFilter
    84  		return nil
    85  	} else {
    86  		cancelExprCacheLock.Unlock()
    87  		//release the lock during compile
    88  
    89  		compiledExpr.CancelOnFilter, err = expr.Compile(bucketFactory.CancelOnFilter, exprhelpers.GetExprOptions(map[string]interface{}{"evt": &types.Event{}})...)
    90  		if err != nil {
    91  			bucketFactory.logger.Errorf("reset_filter compile error : %s", err)
    92  			return err
    93  		}
    94  		u.CancelOnFilter = compiledExpr.CancelOnFilter
    95  		if bucketFactory.Debug {
    96  			u.Debug = true
    97  		}
    98  		cancelExprCacheLock.Lock()
    99  		cancelExprCache[bucketFactory.CancelOnFilter] = compiledExpr
   100  		cancelExprCacheLock.Unlock()
   101  	}
   102  	return err
   103  }