This commit is contained in:
Sebastien Blot 2023-11-27 10:43:32 +01:00
parent 946fbbb8a2
commit b1653aea63
No known key found for this signature in database
GPG key ID: DFC2902F40449F6A
6 changed files with 144 additions and 43 deletions

View file

@ -6,7 +6,6 @@ import (
"time"
"github.com/crowdsecurity/coraza/v3"
"github.com/crowdsecurity/coraza/v3/experimental"
corazatypes "github.com/crowdsecurity/coraza/v3/types"
"github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/crowdsecurity/crowdsec/pkg/waf"
@ -77,7 +76,7 @@ func (r *WaapRunner) Init(datadir string) error {
return nil
}
func (r *WaapRunner) processRequest(tx experimental.FullTransaction, request *waf.ParsedRequest) error {
func (r *WaapRunner) processRequest(tx waf.ExtendedTransaction, request *waf.ParsedRequest) error {
var in *corazatypes.Interruption
var err error
request.Tx = tx
@ -92,7 +91,14 @@ func (r *WaapRunner) processRequest(tx experimental.FullTransaction, request *wa
//We don't close the transaction here, as it will reset coraza internal state and break variable tracking
}()
request.Tx.ProcessConnection(request.RemoteAddr, 0, "", 0)
//pre eval (expr) rules
err = r.WaapRuntime.ProcessPreEvalRules(request)
if err != nil {
r.logger.Errorf("unable to process PreEval rules: %s", err)
//FIXME: should we abort here ?
}
request.Tx.Tx.ProcessConnection(request.RemoteAddr, 0, "", 0)
for k, v := range request.Args {
for _, vv := range v {
@ -151,14 +157,16 @@ func (r *WaapRunner) processRequest(tx experimental.FullTransaction, request *wa
}
func (r *WaapRunner) ProcessInBandRules(request *waf.ParsedRequest) error {
tx := r.WaapInbandEngine.NewTransactionWithID(request.UUID)
err := r.processRequest(tx.(experimental.FullTransaction), request)
tx := waf.NewExtendedTransaction(r.WaapInbandEngine, request.UUID)
r.WaapRuntime.InBandTx = tx
err := r.processRequest(tx, request)
return err
}
func (r *WaapRunner) ProcessOutOfBandRules(request *waf.ParsedRequest) error {
tx := r.WaapOutbandEngine.NewTransactionWithID(request.UUID)
err := r.processRequest(tx.(experimental.FullTransaction), request)
tx := waf.NewExtendedTransaction(r.WaapInbandEngine, request.UUID)
r.WaapRuntime.OutOfBandTx = tx
err := r.processRequest(tx, request)
return err
}
@ -180,14 +188,8 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error {
//to measure the time spent in the WAF
startParsing := time.Now()
//pre eval (expr) rules
err := r.WaapRuntime.ProcessPreEvalRules(request)
if err != nil {
r.logger.Errorf("unable to process PreEval rules: %s", err)
continue
}
//inband WAAP rules
err = r.ProcessInBandRules(&request)
err := r.ProcessInBandRules(&request)
if err != nil {
r.logger.Errorf("unable to process InBand rules: %s", err)
continue
@ -206,7 +208,7 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error {
r.logger.Debugf("inband rules matched : %d", in.RuleID)
r.WaapRuntime.Response.InBandInterrupt = true
err = r.WaapRuntime.ProcessOnMatchRules(request)
err = r.WaapRuntime.ProcessOnMatchRules(&request)
if err != nil {
r.logger.Errorf("unable to process OnMatch rules: %s", err)
continue
@ -236,7 +238,7 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error {
if in := request.Tx.Interruption(); in != nil {
r.logger.Debugf("outband rules matched : %d", in.RuleID)
r.WaapRuntime.Response.OutOfBandInterrupt = true
err = r.WaapRuntime.ProcessOnMatchRules(request)
err = r.WaapRuntime.ProcessOnMatchRules(&request)
if err != nil {
r.logger.Errorf("unable to process OnMatch rules: %s", err)
continue

View file

@ -6,7 +6,6 @@ import (
"net/http"
"net/url"
"github.com/crowdsecurity/coraza/v3/experimental"
"github.com/google/uuid"
)
@ -74,7 +73,7 @@ type ParsedRequest struct {
Body []byte
TransferEncoding []string
UUID string
Tx experimental.FullTransaction
Tx ExtendedTransaction
ResponseChannel chan WaapTempResponse
IsInBand bool
IsOutBand bool

View file

@ -3,6 +3,8 @@ package waf
import (
"github.com/crowdsecurity/coraza/v3"
"github.com/crowdsecurity/coraza/v3/experimental"
"github.com/crowdsecurity/coraza/v3/experimental/plugins/plugintypes"
"github.com/crowdsecurity/coraza/v3/types"
)
type ExtendedTransaction struct {
@ -24,3 +26,63 @@ func (t *ExtendedTransaction) RemoveRuleByIDWithError(id int) error {
t.Tx.RemoveRuleByID(id)
return nil
}
func (t *ExtendedTransaction) IsRuleEngineOff() bool {
return t.Tx.IsRuleEngineOff()
}
func (t *ExtendedTransaction) ProcessLogging() {
t.Tx.ProcessLogging()
}
func (t *ExtendedTransaction) ProcessConnection(client string, cPort int, server string, sPort int) {
t.Tx.ProcessConnection(client, cPort, server, sPort)
}
func (t *ExtendedTransaction) AddGetRequestArgument(name string, value string) {
t.Tx.AddGetRequestArgument(name, value)
}
func (t *ExtendedTransaction) ProcessURI(uri string, method string, httpVersion string) {
t.Tx.ProcessURI(uri, method, httpVersion)
}
func (t *ExtendedTransaction) AddRequestHeader(name string, value string) {
t.Tx.AddRequestHeader(name, value)
}
func (t *ExtendedTransaction) SetServerName(name string) {
t.Tx.SetServerName(name)
}
func (t *ExtendedTransaction) ProcessRequestHeaders() *types.Interruption {
return t.Tx.ProcessRequestHeaders()
}
func (t *ExtendedTransaction) ProcessRequestBody() (*types.Interruption, error) {
return t.Tx.ProcessRequestBody()
}
func (t *ExtendedTransaction) WriteRequestBody(body []byte) (*types.Interruption, int, error) {
return t.Tx.WriteRequestBody(body)
}
func (t *ExtendedTransaction) Interruption() *types.Interruption {
return t.Tx.Interruption()
}
func (t *ExtendedTransaction) IsInterrupted() bool {
return t.Tx.IsInterrupted()
}
func (t *ExtendedTransaction) Variables() plugintypes.TransactionVariables {
return t.Tx.Variables()
}
func (t *ExtendedTransaction) MatchedRules() []types.MatchedRule {
return t.Tx.MatchedRules()
}
func (t *ExtendedTransaction) ID() string {
return t.Tx.ID()
}

View file

@ -21,18 +21,34 @@ type Hook struct {
ApplyExpr []*vm.Program `yaml:"-"`
}
// @tko : todo - debug mode
func (h *Hook) Build() error {
const (
hookOnLoad = iota
hookPreEval
hookOnMatch
)
// @tko : todo - debug mode
func (h *Hook) Build(hookStage int) error {
ctx := map[string]interface{}{}
switch hookStage {
case hookOnLoad:
ctx = GetOnLoadEnv(&WaapRuntimeConfig{})
case hookPreEval:
ctx = GetPreEvalEnv(&WaapRuntimeConfig{}, &ParsedRequest{})
case hookOnMatch:
ctx = GetOnMatchEnv(&WaapRuntimeConfig{}, &ParsedRequest{})
}
opts := GetExprWAFOptions(ctx)
if h.Filter != "" {
program, err := expr.Compile(h.Filter) //FIXME: opts
program, err := expr.Compile(h.Filter, opts...) //FIXME: opts
if err != nil {
return fmt.Errorf("unable to compile filter %s : %w", h.Filter, err)
}
h.FilterExpr = program
}
for _, apply := range h.Apply {
program, err := expr.Compile(apply, GetExprWAFOptions(GetHookEnv(&WaapRuntimeConfig{}, ParsedRequest{}))...)
program, err := expr.Compile(apply, opts...)
if err != nil {
return fmt.Errorf("unable to compile apply %s : %w", apply, err)
}
@ -204,7 +220,7 @@ func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) {
//load hooks
for _, hook := range wc.OnLoad {
err := hook.Build()
err := hook.Build(hookOnLoad)
if err != nil {
return nil, fmt.Errorf("unable to build on_load hook : %s", err)
}
@ -212,7 +228,7 @@ func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) {
}
for _, hook := range wc.PreEval {
err := hook.Build()
err := hook.Build(hookPreEval)
if err != nil {
return nil, fmt.Errorf("unable to build pre_eval hook : %s", err)
}
@ -220,7 +236,7 @@ func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) {
}
for _, hook := range wc.OnMatch {
err := hook.Build()
err := hook.Build(hookOnMatch)
if err != nil {
return nil, fmt.Errorf("unable to build on_match hook : %s", err)
}
@ -243,7 +259,7 @@ func (w *WaapRuntimeConfig) ProcessOnLoadRules() error {
if rule.FilterExpr != nil {
output, err := expr.Run(rule.FilterExpr, GetOnLoadEnv(w))
if err != nil {
return fmt.Errorf("unable to run filter %s : %w", rule.Filter, err)
return fmt.Errorf("unable to run waap on_load filter %s : %w", rule.Filter, err)
}
switch t := output.(type) {
case bool:
@ -259,7 +275,7 @@ func (w *WaapRuntimeConfig) ProcessOnLoadRules() error {
for _, applyExpr := range rule.ApplyExpr {
_, err := expr.Run(applyExpr, GetOnLoadEnv(w))
if err != nil {
log.Errorf("unable to apply filter: %s", err)
log.Errorf("unable to apply waap on_load expr: %s", err)
continue
}
}
@ -267,13 +283,13 @@ func (w *WaapRuntimeConfig) ProcessOnLoadRules() error {
return nil
}
func (w *WaapRuntimeConfig) ProcessOnMatchRules(request ParsedRequest) error {
func (w *WaapRuntimeConfig) ProcessOnMatchRules(request *ParsedRequest) error {
for _, rule := range w.CompiledOnMatch {
if rule.FilterExpr != nil {
output, err := expr.Run(rule.FilterExpr, GetOnMatchEnv(w, request))
if err != nil {
return fmt.Errorf("unable to run filter %s : %w", rule.Filter, err)
return fmt.Errorf("unable to run waap on_match filter %s : %w", rule.Filter, err)
}
switch t := output.(type) {
case bool:
@ -289,7 +305,7 @@ func (w *WaapRuntimeConfig) ProcessOnMatchRules(request ParsedRequest) error {
for _, applyExpr := range rule.ApplyExpr {
_, err := expr.Run(applyExpr, GetOnMatchEnv(w, request))
if err != nil {
log.Errorf("unable to apply filter: %s", err)
log.Errorf("unable to apply waap on_match expr: %s", err)
continue
}
}
@ -297,12 +313,12 @@ func (w *WaapRuntimeConfig) ProcessOnMatchRules(request ParsedRequest) error {
return nil
}
func (w *WaapRuntimeConfig) ProcessPreEvalRules(request ParsedRequest) error {
func (w *WaapRuntimeConfig) ProcessPreEvalRules(request *ParsedRequest) error {
for _, rule := range w.CompiledPreEval {
if rule.FilterExpr != nil {
output, err := expr.Run(rule.FilterExpr, GetPreEvalEnv(w, request))
if err != nil {
return fmt.Errorf("unable to run filter %s : %w", rule.Filter, err)
return fmt.Errorf("unable to run waap pre_eval filter %s : %w", rule.Filter, err)
}
switch t := output.(type) {
case bool:
@ -319,7 +335,7 @@ func (w *WaapRuntimeConfig) ProcessPreEvalRules(request ParsedRequest) error {
for _, applyExpr := range rule.ApplyExpr {
_, err := expr.Run(applyExpr, GetPreEvalEnv(w, request))
if err != nil {
log.Errorf("unable to apply filter: %s", err)
log.Errorf("unable to apply waap pre_eval expr: %s", err)
continue
}
}
@ -343,6 +359,13 @@ func (w *WaapRuntimeConfig) RemoveInbandRuleByID(params ...any) (any, error) {
return nil, nil
}
// func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(id int) error {
func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(params ...any) (any, error) {
id := params[0].(int)
_ = w.OutOfBandTx.RemoveRuleByIDWithError(id)
return nil, nil
}
func (w *WaapRuntimeConfig) CancelEvent(params ...any) (any, error) {
w.Response.SendEvent = false
return nil, nil
@ -399,13 +422,6 @@ func (w *WaapRuntimeConfig) SetActionByID(params ...any) (any, error) {
return nil, nil
}
// func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(id int) error {
func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(params ...any) (any, error) {
id := params[0].(int)
_ = w.OutOfBandTx.RemoveRuleByIDWithError(id)
return nil, nil
}
// func (w *WaapRuntimeConfig) SetAction(action string) error {
func (w *WaapRuntimeConfig) SetAction(params ...any) (any, error) {
//log.Infof("setting to %s", action)

View file

@ -8,7 +8,26 @@ type exprCustomFunc struct {
signature []interface{}
}
var onLoadExprFuncs = []exprCustomFunc{}
/*
func GetOnLoadEnv(w *WaapRuntimeConfig) map[string]interface{} {
return map[string]interface{}{
"DisableInBandRuleByID": w.DisableInBandRuleByID,
"DisableOutBandRuleByID": w.DisableOutBandRuleByID,
"DisableInBandRuleByTag": w.DisableInBandRuleByTag,
"DisableOutBandRuleByTag": w.DisableOutBandRuleByTag,
}
}
*/
/*var onLoadExprFuncs = []exprCustomFunc{
{
name: "DisableInBandRuleByID",
function: w.DisableInBandRuleByID,
signature: []interface{}{
new(func(int) error),
},
},
}*/
var preEvalExprFuncs = []exprCustomFunc{}

View file

@ -32,6 +32,7 @@ func GetExprWAFOptions(ctx map[string]interface{}) []expr.Option {
}
func GetOnLoadEnv(w *WaapRuntimeConfig) map[string]interface{} {
//FIXME: use expr.Function instead of this
return map[string]interface{}{
"DisableInBandRuleByID": w.DisableInBandRuleByID,
"DisableOutBandRuleByID": w.DisableOutBandRuleByID,
@ -40,7 +41,8 @@ func GetOnLoadEnv(w *WaapRuntimeConfig) map[string]interface{} {
}
}
func GetPreEvalEnv(w *WaapRuntimeConfig, request ParsedRequest) map[string]interface{} {
func GetPreEvalEnv(w *WaapRuntimeConfig, request *ParsedRequest) map[string]interface{} {
//FIXME: use expr.Function instead of this
return map[string]interface{}{
"IsInBand": request.IsInBand,
"IsOutBand": request.IsOutBand,
@ -51,7 +53,8 @@ func GetPreEvalEnv(w *WaapRuntimeConfig, request ParsedRequest) map[string]inter
}
}
func GetOnMatchEnv(w *WaapRuntimeConfig, request ParsedRequest) map[string]interface{} {
func GetOnMatchEnv(w *WaapRuntimeConfig, request *ParsedRequest) map[string]interface{} {
//FIXME: use expr.Function instead of this
return map[string]interface{}{
"req": request,
"IsInBand": request.IsInBand,