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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/corazawaf/coraza/v3"
|
"github.com/corazawaf/coraza/v3"
|
||||||
"github.com/corazawaf/coraza/v3/experimental"
|
"github.com/corazawaf/coraza/v3/experimental"
|
||||||
corazatypes "github.com/corazawaf/coraza/v3/types"
|
corazatypes "github.com/corazawaf/coraza/v3/types"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
|
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
@ -195,12 +199,12 @@ func (w *WafSource) Dump() interface{} {
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
func processReqWithEngine(waf coraza.WAF, r *http.Request) (*corazatypes.Interruption, error) {
|
func processReqWithEngine(waf coraza.WAF, r *http.Request, uuid string) (*corazatypes.Interruption, corazatypes.Transaction, error) {
|
||||||
tx := waf.NewTransaction()
|
tx := waf.NewTransactionWithID(uuid)
|
||||||
|
|
||||||
if tx.IsRuleEngineOff() {
|
if tx.IsRuleEngineOff() {
|
||||||
log.Printf("engine is off")
|
log.Printf("engine is off")
|
||||||
return nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -239,60 +243,146 @@ func processReqWithEngine(waf coraza.WAF, r *http.Request) (*corazatypes.Interru
|
||||||
in := tx.ProcessRequestHeaders()
|
in := tx.ProcessRequestHeaders()
|
||||||
if in != nil {
|
if in != nil {
|
||||||
log.Printf("headerss")
|
log.Printf("headerss")
|
||||||
return in, nil
|
return in, tx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.IsRequestBodyAccessible() {
|
if tx.IsRequestBodyAccessible() {
|
||||||
if r.Body != nil && r.Body != http.NoBody {
|
if r.Body != nil && r.Body != http.NoBody {
|
||||||
_, _, err := tx.ReadRequestBodyFrom(r.Body)
|
_, _, err := tx.ReadRequestBodyFrom(r.Body)
|
||||||
if err != nil {
|
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()
|
bodyReader, err := tx.RequestBodyReader()
|
||||||
if err != nil {
|
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)
|
body := io.MultiReader(bodyReader, r.Body)
|
||||||
r.Body = ioutil.NopCloser(body)
|
r.Body = ioutil.NopCloser(body)
|
||||||
in, err = tx.ProcessRequestBody()
|
in, err = tx.ProcessRequestBody()
|
||||||
if err != nil {
|
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 {
|
if in != nil {
|
||||||
log.Printf("nothing here")
|
log.Printf("exception while processing body")
|
||||||
return in, nil
|
return in, tx, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Printf("done")
|
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) {
|
func (w *WafSource) wafHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
log.Printf("yolo here %v", r)
|
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
|
//inband first
|
||||||
in, err := processReqWithEngine(w.inBandWaf, r)
|
in, tx, err := processReqWithEngine(w.inBandWaf, r, uuid)
|
||||||
if err != nil { //things went south
|
if err != nil { //things went south
|
||||||
log.Errorf("Error while processing request : %s", err)
|
log.Errorf("Error while processing request : %s", err)
|
||||||
rw.WriteHeader(http.StatusForbidden)
|
rw.WriteHeader(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if in != nil {
|
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)
|
rw.WriteHeader(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
//Now we can do out of band
|
//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
|
if err != nil { //things went south
|
||||||
log.Errorf("Error while processing request : %s", err)
|
log.Errorf("Error while processing request : %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if in2 != nil {
|
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)
|
log.Infof("WAF triggered : %+v", in2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue