This commit is contained in:
bui 2023-06-07 13:45:36 +02:00
parent d123254949
commit 30455a8eb6
4 changed files with 35 additions and 16 deletions

3
go.mod
View file

@ -52,7 +52,7 @@ require (
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.3
golang.org/x/crypto v0.1.0
golang.org/x/mod v0.6.0
golang.org/x/mod v0.8.0
google.golang.org/grpc v1.47.0
google.golang.org/protobuf v1.28.1
gopkg.in/natefinch/lumberjack.v2 v2.2.1
@ -206,4 +206,3 @@ require (
replace golang.org/x/time/rate => github.com/crowdsecurity/crowdsec/pkg/time/rate v0.0.0
replace github.com/corazawaf/coraza/v3 => ./coraza

View file

@ -15,6 +15,8 @@ import (
corazatypes "github.com/corazawaf/coraza/v3/types"
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
"github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/crowdsecurity/go-cs-lib/pkg/trace"
"github.com/davecgh/go-spew/spew"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
@ -144,6 +146,8 @@ func (w *WafSource) Configure(yamlConfig []byte, logger *log.Entry) error {
return errors.Wrap(err, "Cannot create WAF")
}
w.outOfBandWaf = outofbandwaf
log.Printf("OOB -> %s", spew.Sdump(w.outOfBandWaf))
log.Printf("IB -> %s", spew.Sdump(w.inBandWaf))
//We don´t use the wrapper provided by coraza because we want to fully control what happens when a rule match to send the information in crowdsec
w.mux.HandleFunc(w.config.Path, w.wafHandler)
@ -170,7 +174,7 @@ func (w *WafSource) OneShotAcquisition(out chan types.Event, t *tomb.Tomb) error
func (w *WafSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error {
w.outChan = out
t.Go(func() error {
defer types.CatchPanic("crowdsec/acquis/waf/live")
defer trace.CatchPanic("crowdsec/acquis/waf/live")
w.logger.Infof("Starting WAF server on %s:%d%s", w.config.ListenAddr, w.config.ListenPort, w.config.Path)
t.Go(func() error {
err := w.server.ListenAndServe()
@ -200,6 +204,7 @@ func (w *WafSource) Dump() interface{} {
}
func processReqWithEngine(waf coraza.WAF, r *http.Request, uuid string) (*corazatypes.Interruption, corazatypes.Transaction, error) {
var in *corazatypes.Interruption
tx := waf.NewTransactionWithID(uuid)
if tx.IsRuleEngineOff() {
@ -240,7 +245,8 @@ func processReqWithEngine(waf coraza.WAF, r *http.Request, uuid string) (*coraza
tx.AddRequestHeader("Transfer-Encoding", r.TransferEncoding[0])
}
in := tx.ProcessRequestHeaders()
in = tx.ProcessRequestHeaders()
//if we're inband, we should stop here, but for outofband go to the end
if in != nil {
log.Printf("headerss")
return in, tx, nil
@ -270,19 +276,22 @@ func processReqWithEngine(waf coraza.WAF, r *http.Request, uuid string) (*coraza
}
}
}
log.Printf("done")
return nil, nil, nil
log.Printf("done -> %d", len(tx.MatchedRules()))
// if in != nil {
// log.Printf("exception while processing req")
// return in, tx, nil
// }
return nil, tx, nil
}
func (w *WafSource) TxToEvents(tx corazatypes.Transaction, r *http.Request) ([]types.Event, error) {
func (w *WafSource) TxToEvents(tx corazatypes.Transaction, r *http.Request, kind string) ([]types.Event, error) {
evts := []types.Event{}
if tx == nil {
return nil, fmt.Errorf("tx is nil")
}
for idx, rule := range tx.MatchedRules() {
log.Printf("rule %d", idx)
evt, err := w.RuleMatchToEvent(rule, tx, r)
evt, err := w.RuleMatchToEvent(rule, tx, r, kind)
if err != nil {
return nil, errors.Wrap(err, "Cannot convert rule match to event")
}
@ -293,7 +302,7 @@ func (w *WafSource) TxToEvents(tx corazatypes.Transaction, r *http.Request) ([]t
}
// Transforms a coraza interruption to a crowdsec event
func (w *WafSource) RuleMatchToEvent(rule corazatypes.MatchedRule, tx corazatypes.Transaction, r *http.Request) (types.Event, error) {
func (w *WafSource) RuleMatchToEvent(rule corazatypes.MatchedRule, tx corazatypes.Transaction, r *http.Request, kind string) (types.Event, error) {
evt := types.Event{}
//we might want to change this based on in-band vs out-of-band ?
evt.Type = types.LOG
@ -306,8 +315,9 @@ func (w *WafSource) RuleMatchToEvent(rule corazatypes.MatchedRule, tx corazatype
//why ? because it's more consistent with the other data-sources etc. and it provides users with flexibility to alter our parsers
CorazaEvent := map[string]interface{}{
//core rule info
"rule_id": rule.Rule().ID(),
"rule_action": tx.Interruption().Action,
"rule_type": kind,
"rule_id": rule.Rule().ID(),
//"rule_action": tx.Interruption().Action,
"rule_disruptive": rule.Disruptive(),
"rule_tags": rule.Rule().Tags(),
"rule_file": rule.Rule().File(),
@ -323,6 +333,9 @@ func (w *WafSource) RuleMatchToEvent(rule corazatypes.MatchedRule, tx corazatype
"uri": rule.URI(),
}
if tx.Interruption() != nil {
CorazaEvent["rule_action"] = tx.Interruption().Action
}
corazaEventB, err := json.Marshal(CorazaEvent)
if err != nil {
return evt, fmt.Errorf("Unable to marshal coraza alert: %w", err)
@ -352,7 +365,7 @@ func (w *WafSource) wafHandler(rw http.ResponseWriter, r *http.Request) {
return
}
if in != nil {
events, err := w.TxToEvents(tx, r)
events, err := w.TxToEvents(tx, r, "inband")
log.Infof("Request blocked by WAF, %d events to send", len(events))
for _, evt := range events {
w.outChan <- evt
@ -373,8 +386,9 @@ func (w *WafSource) wafHandler(rw http.ResponseWriter, r *http.Request) {
log.Errorf("Error while processing request : %s", err)
return
}
if in2 != nil {
events, err := w.TxToEvents(tx2, r)
if tx2 != nil && len(tx2.MatchedRules()) > 0 {
log.Printf("got events and stuff to do")
events, err := w.TxToEvents(tx2, r, "outofband")
log.Infof("Request triggered by WAF, %d events to send", len(events))
for _, evt := range events {
w.outChan <- evt

View file

@ -274,6 +274,10 @@ func (n *Node) process(p *types.Event, ctx UnixParserCtx, expressionEnv map[stri
switch out := output.(type) {
case string:
gstr = out
case int:
gstr = fmt.Sprintf("%d", out)
case float64, float32:
gstr = fmt.Sprintf("%f", out)
default:
clog.Errorf("unexpected return type for RunTimeValue : %T", output)
}

View file

@ -132,6 +132,8 @@ func (n *Node) ProcessStatics(statics []types.ExtraField, event *types.Event) er
value = out
case int:
value = strconv.Itoa(out)
case float64, float32:
value = fmt.Sprintf("%f", out)
case map[string]interface{}:
clog.Warnf("Expression '%s' returned a map, please use ToJsonString() to convert it to string if you want to keep it as is, or refine your expression to extract a string", static.ExpValue)
case []interface{}:
@ -139,7 +141,7 @@ func (n *Node) ProcessStatics(statics []types.ExtraField, event *types.Event) er
case nil:
clog.Debugf("Expression '%s' returned nil, skipping", static.ExpValue)
default:
clog.Errorf("unexpected return type for RunTimeValue : %T", output)
clog.Errorf("unexpected return type for '%s' : %T", static.ExpValue, output)
return errors.New("unexpected return type for RunTimeValue")
}
}