wip
This commit is contained in:
parent
ee8b31348b
commit
d123254949
1 changed files with 103 additions and 13 deletions
|
@ -2,16 +2,20 @@ package wafacquisition
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/corazawaf/coraza/v3"
|
||||
"github.com/corazawaf/coraza/v3/experimental"
|
||||
corazatypes "github.com/corazawaf/coraza/v3/types"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -195,12 +199,12 @@ func (w *WafSource) Dump() interface{} {
|
|||
return w
|
||||
}
|
||||
|
||||
func processReqWithEngine(waf coraza.WAF, r *http.Request) (*corazatypes.Interruption, error) {
|
||||
tx := waf.NewTransaction()
|
||||
func processReqWithEngine(waf coraza.WAF, r *http.Request, uuid string) (*corazatypes.Interruption, corazatypes.Transaction, error) {
|
||||
tx := waf.NewTransactionWithID(uuid)
|
||||
|
||||
if tx.IsRuleEngineOff() {
|
||||
log.Printf("engine is off")
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
@ -239,60 +243,146 @@ func processReqWithEngine(waf coraza.WAF, r *http.Request) (*corazatypes.Interru
|
|||
in := tx.ProcessRequestHeaders()
|
||||
if in != nil {
|
||||
log.Printf("headerss")
|
||||
return in, nil
|
||||
return in, tx, nil
|
||||
}
|
||||
|
||||
if tx.IsRequestBodyAccessible() {
|
||||
if r.Body != nil && r.Body != http.NoBody {
|
||||
_, _, err := tx.ReadRequestBodyFrom(r.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Cannot read request body")
|
||||
return nil, nil, errors.Wrap(err, "Cannot read request body")
|
||||
}
|
||||
bodyReader, err := tx.RequestBodyReader()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Cannot read request body")
|
||||
return nil, nil, errors.Wrap(err, "Cannot read request body")
|
||||
|
||||
}
|
||||
body := io.MultiReader(bodyReader, r.Body)
|
||||
r.Body = ioutil.NopCloser(body)
|
||||
in, err = tx.ProcessRequestBody()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Cannot process request body")
|
||||
return nil, nil, errors.Wrap(err, "Cannot process request body")
|
||||
|
||||
}
|
||||
if in != nil {
|
||||
log.Printf("nothing here")
|
||||
return in, nil
|
||||
log.Printf("exception while processing body")
|
||||
return in, tx, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Printf("done")
|
||||
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (w *WafSource) TxToEvents(tx corazatypes.Transaction, r *http.Request) ([]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)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Cannot convert rule match to event")
|
||||
}
|
||||
evts = append(evts, evt)
|
||||
}
|
||||
|
||||
return evts, nil
|
||||
}
|
||||
|
||||
// Transforms a coraza interruption to a crowdsec event
|
||||
func (w *WafSource) RuleMatchToEvent(rule corazatypes.MatchedRule, tx corazatypes.Transaction, r *http.Request) (types.Event, error) {
|
||||
evt := types.Event{}
|
||||
//we might want to change this based on in-band vs out-of-band ?
|
||||
evt.Type = types.LOG
|
||||
evt.ExpectMode = types.LIVE
|
||||
//def needs fixing
|
||||
evt.Stage = "s00-raw"
|
||||
evt.Process = true
|
||||
|
||||
//we build a big-ass object that is going to be marshaled in line.raw and unmarshaled later.
|
||||
//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_disruptive": rule.Disruptive(),
|
||||
"rule_tags": rule.Rule().Tags(),
|
||||
"rule_file": rule.Rule().File(),
|
||||
"rule_file_line": rule.Rule().Line(),
|
||||
"rule_revision": rule.Rule().Revision(),
|
||||
"rule_secmark": rule.Rule().SecMark(),
|
||||
"rule_accuracy": rule.Rule().Accuracy(),
|
||||
|
||||
//http contextual infos
|
||||
"upstream_addr": r.RemoteAddr,
|
||||
"req_uuid": tx.ID(),
|
||||
"source_ip": strings.Split(rule.ClientIPAddress(), ":")[0],
|
||||
"uri": rule.URI(),
|
||||
}
|
||||
|
||||
corazaEventB, err := json.Marshal(CorazaEvent)
|
||||
if err != nil {
|
||||
return evt, fmt.Errorf("Unable to marshal coraza alert: %w", err)
|
||||
}
|
||||
evt.Line = types.Line{
|
||||
Time: time.Now(),
|
||||
//should we add some info like listen addr/port/path ?
|
||||
Labels: map[string]string{"type": "waf"},
|
||||
Process: true,
|
||||
Module: "waf",
|
||||
Src: "waf",
|
||||
Raw: string(corazaEventB),
|
||||
}
|
||||
|
||||
return evt, nil
|
||||
}
|
||||
|
||||
func (w *WafSource) wafHandler(rw http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("yolo here %v", r)
|
||||
//let's gen a transaction id to keep consistance accross in-band and out-of-band
|
||||
uuid := uuid.New().String()
|
||||
//inband first
|
||||
in, err := processReqWithEngine(w.inBandWaf, r)
|
||||
in, tx, err := processReqWithEngine(w.inBandWaf, r, uuid)
|
||||
if err != nil { //things went south
|
||||
log.Errorf("Error while processing request : %s", err)
|
||||
rw.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if in != nil {
|
||||
log.Infof("Request blocked by WAF : %+v", in)
|
||||
events, err := w.TxToEvents(tx, r)
|
||||
log.Infof("Request blocked by WAF, %d events to send", len(events))
|
||||
for _, evt := range events {
|
||||
w.outChan <- evt
|
||||
}
|
||||
log.Infof("done")
|
||||
if err != nil {
|
||||
log.Errorf("Cannot convert transaction to events : %s", err)
|
||||
rw.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
//Now we can do out of band
|
||||
in2, err := processReqWithEngine(w.outOfBandWaf, r)
|
||||
in2, tx2, err := processReqWithEngine(w.outOfBandWaf, r, uuid)
|
||||
if err != nil { //things went south
|
||||
log.Errorf("Error while processing request : %s", err)
|
||||
return
|
||||
}
|
||||
if in2 != nil {
|
||||
events, err := w.TxToEvents(tx2, r)
|
||||
log.Infof("Request triggered by WAF, %d events to send", len(events))
|
||||
for _, evt := range events {
|
||||
w.outChan <- evt
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("Cannot convert transaction to events : %s", err)
|
||||
}
|
||||
log.Infof("done")
|
||||
log.Infof("WAF triggered : %+v", in2)
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue