This commit is contained in:
Sebastien Blot 2023-09-19 13:16:33 +02:00
parent d3ce4cbf8e
commit 535738b962
No known key found for this signature in database
GPG key ID: DFC2902F40449F6A
4 changed files with 146 additions and 34 deletions

View file

@ -180,6 +180,10 @@ func (w *WaapSource) Configure(yamlConfig []byte, logger *log.Entry) error {
logger: wafLogger,
WaapRuntime: &wrt,
}
err := runner.Init()
if err != nil {
return fmt.Errorf("unable to initialize runner : %s", err)
}
w.WaapRunners[nbRoutine] = runner
//most likely missign somethign here to actually start the runner :)
}
@ -269,10 +273,14 @@ func (w *WaapSource) waapHandler(rw http.ResponseWriter, r *http.Request) {
w.InChan <- parsedRequest
response := <-parsedRequest.ResponseChannel
log.Infof("resp %+v", response)
rw.WriteHeader(response.HTTPResponseCode)
body, err := json.Marshal(BodyResponse{Action: response.Action})
waapResponse := w.WaapRuntime.GenerateResponse(response.InBandInterrupt)
log.Infof("resp %+v", response)
log.Infof("waap resp %+v", waapResponse)
rw.WriteHeader(waapResponse.HTTPStatus)
body, err := json.Marshal(BodyResponse{Action: waapResponse.Action})
if err != nil {
log.Errorf("unable to marshal response: %s", err)
rw.WriteHeader(http.StatusInternalServerError)

View file

@ -1,9 +1,14 @@
package wafacquisition
import (
"fmt"
"os"
"time"
"github.com/crowdsecurity/coraza/v3"
"github.com/crowdsecurity/coraza/v3/experimental"
corazatypes "github.com/crowdsecurity/coraza/v3/types"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/crowdsecurity/crowdsec/pkg/waf"
"github.com/prometheus/client_golang/prometheus"
@ -22,6 +27,114 @@ type WaapRunner struct {
logger *log.Entry
}
func (r *WaapRunner) Init() error {
var err error
fs := os.DirFS(csconfig.DataDir)
inBandRules := ""
outOfBandRules := ""
for _, collection := range r.WaapRuntime.InBandRules {
inBandRules += collection.String()
}
for _, collection := range r.WaapRuntime.OutOfBandRules {
outOfBandRules += collection.String()
}
r.WaapInbandEngine, err = coraza.NewWAF(
coraza.NewWAFConfig().WithDirectives(inBandRules).WithRootFS(fs),
)
if err != nil {
return fmt.Errorf("unable to initialize inband engine : %w", err)
}
r.WaapOutbandEngine, err = coraza.NewWAF(
coraza.NewWAFConfig().WithDirectives(outOfBandRules).WithRootFS(fs),
)
if err != nil {
return fmt.Errorf("unable to initialize outband engine : %w", err)
}
return nil
}
func (r *WaapRunner) ProcessInBandRules(request *waf.ParsedRequest) error {
var in *corazatypes.Interruption
var err error
tx := r.WaapInbandEngine.NewTransactionWithID(request.UUID)
request.Tx = tx.(experimental.FullTransaction)
if request.Tx.IsRuleEngineOff() {
r.logger.Debugf("rule engine is off, skipping")
return nil
}
defer func() {
request.Tx.ProcessLogging()
//We don't close the transaction here, as it will reset coraza internal state and break out of bands rules
}()
request.Tx.ProcessConnection(request.RemoteAddr, 0, "", 0)
request.Tx.ProcessURI(request.URI, request.Method, request.Proto) //TODO: The doc mentions that GET args needs to be added, but we never call AddArguments ?
for k, vr := range request.Headers {
for _, v := range vr {
request.Tx.AddRequestHeader(k, v)
}
}
if request.ClientHost != "" {
request.Tx.AddRequestHeader("Host", request.ClientHost)
request.Tx.SetServerName(request.ClientHost)
}
if request.TransferEncoding != nil {
request.Tx.AddRequestHeader("Transfer-Encoding", request.TransferEncoding[0])
}
in = request.Tx.ProcessRequestHeaders()
if in != nil {
r.logger.Infof("inband rules matched for headers : %d", in.Action)
return nil
}
if request.Body != nil && len(request.Body) > 0 {
in, _, err = request.Tx.WriteRequestBody(request.Body)
if err != nil {
r.logger.Errorf("unable to write request body : %s", err)
return err
}
if in != nil {
return nil
}
}
in, err = request.Tx.ProcessRequestBody()
if err != nil {
r.logger.Errorf("unable to process request body : %s", err)
return err
}
if in != nil {
r.logger.Infof("inband rules matched for body : %d", in.RuleID)
return nil
}
return nil
}
func (r *WaapRunner) ProcessOutOfBandRules(request waf.ParsedRequest) (*corazatypes.Interruption, error) {
return nil, nil
}
func (r *WaapRunner) Run(t *tomb.Tomb) error {
r.logger.Infof("Waap Runner ready to process event")
for {
@ -45,11 +158,16 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error {
}
log.Infof("now response is -> %s", r.WaapRuntime.Response.Action)
//inband WAAP rules
err = r.WaapRuntime.ProcessInBandRules(request)
err = r.ProcessInBandRules(&request)
if err != nil {
r.logger.Errorf("unable to process InBand rules: %s", err)
continue
}
if in := request.Tx.Interruption(); in != nil {
r.logger.Debugf("inband rules matched : %d", in.RuleID)
r.WaapRuntime.Response.InBandInterrupt = true
}
elapsed := time.Since(startParsing)
WafInbandParsingHistogram.With(prometheus.Labels{"source": request.RemoteAddr}).Observe(elapsed.Seconds())

View file

@ -7,7 +7,6 @@ import (
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
corazatypes "github.com/crowdsecurity/coraza/v3/types"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)
@ -131,7 +130,7 @@ func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) {
//load rules
for _, rule := range wc.OutOfBandRules {
wc.Logger.Debugf("loading outofband rule %s", rule)
wc.Logger.Infof("loading outofband rule %s", rule)
collection, err := LoadCollection(rule)
if err != nil {
return nil, fmt.Errorf("unable to load outofband rule %s : %s", rule, err)
@ -139,8 +138,9 @@ func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) {
ret.OutOfBandRules = append(ret.OutOfBandRules, collection)
}
wc.Logger.Infof("Loaded %d outofband rules", len(ret.OutOfBandRules))
for _, rule := range wc.InBandRules {
wc.Logger.Debugf("loading inband rule %s", rule)
wc.Logger.Infof("loading inband rule %s", rule)
collection, err := LoadCollection(rule)
if err != nil {
return nil, fmt.Errorf("unable to load inband rule %s : %s", rule, err)
@ -148,6 +148,8 @@ func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) {
ret.InBandRules = append(ret.InBandRules, collection)
}
wc.Logger.Infof("Loaded %d inband rules", len(ret.InBandRules))
//load hooks
for _, hook := range wc.OnLoad {
err := hook.Build()
@ -311,45 +313,21 @@ func (w *WaapRuntimeConfig) SetHTTPCode(code int) error {
return nil
}
func (w *WaapRuntimeConfig) ProcessInBandRules(request ParsedRequest) error {
for _, rule := range w.InBandRules {
_, err := rule.Eval(request)
if err != nil {
return fmt.Errorf("unable to process inband rule %s : %s", rule.GetDisplayName(), err)
}
//...
}
return nil
}
func (w *WaapRuntimeConfig) ProcessOutOfBandRules(request ParsedRequest) (*corazatypes.Interruption, error) {
for _, rule := range w.OutOfBandRules {
interrupt, err := rule.Eval(request)
if err != nil {
return nil, fmt.Errorf("unable to process inband rule %s : %s", rule.GetDisplayName(), err)
}
if interrupt != nil {
return interrupt, nil
}
}
return nil, nil
}
type BodyResponse struct {
Action string `json:"action"`
HTTPStatus int `json:"http_status"`
}
func (w *WaapRuntimeConfig) GenerateResponse(interrupted bool) (BodyResponse, error) {
func (w *WaapRuntimeConfig) GenerateResponse(interrupted bool) BodyResponse {
resp := BodyResponse{}
//if there is no interrupt, we should allow with default code
if !interrupted {
resp.Action = w.Config.DefaultPassAction
resp.HTTPStatus = w.Config.PassedHTTPCode
return resp, nil
return resp
}
resp.Action = w.Config.DefaultRemediation
resp.HTTPStatus = w.Config.BlockedHTTPCode
return resp, nil
return resp
}

View file

@ -120,3 +120,11 @@ func (w WaapCollection) Eval(req ParsedRequest) (*corazatypes.Interruption, erro
func (w WaapCollection) GetDisplayName() string {
return w.collectionName
}
func (w WaapCollection) String() string {
ret := ""
for _, rule := range w.Rules {
ret += rule + "\n"
}
return ret
}