This commit is contained in:
bui 2023-09-13 17:12:09 +02:00
parent c435447d8e
commit 2e60e8021c
6 changed files with 144 additions and 77 deletions

View file

@ -52,6 +52,11 @@ type WaapSource struct {
WaapRunners []WaapRunner //one for each go-routine
}
// @tko + @sbl : we might want to get rid of that or improve it
type BodyResponse struct {
Action string `json:"action"`
}
func (wc *WaapSource) UnmarshalConfig(yamlConfig []byte) error {
err := yaml.UnmarshalStrict(yamlConfig, &wc.config)
@ -165,10 +170,13 @@ func (w *WaapSource) Configure(yamlConfig []byte, logger *log.Entry) error {
})
}
//we copy WaapRutime for each runner
wrt := *w.WaapRuntime
runner := WaapRunner{
inChan: w.InChan,
UUID: wafUUID,
logger: wafLogger,
inChan: w.InChan,
UUID: wafUUID,
logger: wafLogger,
WaapRuntime: &wrt,
}
w.WaapRunners[nbRoutine] = runner
//most likely missign somethign here to actually start the runner :)
@ -247,10 +255,6 @@ func (w *WaapSource) Dump() interface{} {
return w
}
type BodyResponse struct {
Action string `json:"action"`
}
// should this be in the runner ?
func (w *WaapSource) waapHandler(rw http.ResponseWriter, r *http.Request) {
// parse the request only once
@ -264,6 +268,7 @@ func (w *WaapSource) waapHandler(rw http.ResponseWriter, r *http.Request) {
message := <-parsedRequest.ResponseChannel
//@tko this parts needs to be redone
if message.Err != nil {
log.Errorf("Error while processing InBAND: %s", err)
rw.WriteHeader(http.StatusInternalServerError)

View file

@ -31,10 +31,10 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error {
return nil
case request := <-r.inChan:
r.logger.Infof("Requests handled by runner %s", request.UUID)
r.WaapRuntime.ClearResponse()
//tx := waf.NewExtendedTransaction(r.WaapInbandEngine, r.UUID)
WafReqCounter.With(prometheus.Labels{"source": request.RemoteAddr}).Inc()
//measure the time spent in the WAF
//to measure the time spent in the WAF
startParsing := time.Now()
//pre eval (expr) rules
@ -43,7 +43,6 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error {
r.logger.Errorf("unable to process PreEval rules: %s", err)
continue
}
//inband WAAP rules
interrupt, err := r.WaapRuntime.ProcessInBandRules(request)
elapsed := time.Since(startParsing)

View file

@ -25,18 +25,17 @@ func (t *ExtendedTransaction) RemoveRuleByIDWithError(id int) error {
return nil
}
func GetEnv() map[string]interface{} {
ResponseRequest := ResponseRequest{}
ParsedRequest := ParsedRequest{}
Rules := &WaapCollection{}
Tx := ExtendedTransaction{}
// simply used to ease the compilation & runtime of the hooks
func GetHookEnv(w WaapRuntimeConfig, request ParsedRequest) map[string]interface{} {
return map[string]interface{}{
"rules": Rules,
"req": ParsedRequest,
"SetRemediation": ResponseRequest.SetRemediation,
"SetRemediationByID": ResponseRequest.SetRemediationByID,
"CancelEvent": ResponseRequest.CancelEvent,
"RemoveRuleByID": Tx.RemoveRuleByIDWithError,
"inband_rules": w.InBandRules,
"outband_rules": w.OutOfBandRules,
"req": request,
"RemoveInbandRuleByID": w.RemoveInbandRuleByID,
"RemoveOutbandRuleByID": w.RemoveOutbandRuleByID,
"SetAction": w.SetAction,
"SetHTTPCode": w.SetHTTPCode,
"SetActionByID": w.SetActionnByID,
"CancelEvent": w.CancelEvent,
}
}

View file

@ -36,29 +36,29 @@ func NewResponseRequest(Tx experimental.FullTransaction, in *corazatypes.Interru
}
}
func (r *ResponseRequest) SetRemediation(remediation string) error {
if r.Interruption == nil {
return nil
}
r.Interruption.Action = remediation
return nil
}
// func (r *ResponseRequest) SetRemediation(remediation string) error {
// if r.Interruption == nil {
// return nil
// }
// r.Interruption.Action = remediation
// return nil
// }
func (r *ResponseRequest) SetRemediationByID(ID int, remediation string) error {
if r.Interruption == nil {
return nil
}
if r.Interruption.RuleID == ID {
r.Interruption.Action = remediation
}
return nil
}
// func (r *ResponseRequest) SetRemediationByID(ID int, remediation string) error {
// if r.Interruption == nil {
// return nil
// }
// if r.Interruption.RuleID == ID {
// r.Interruption.Action = remediation
// }
// return nil
// }
func (r *ResponseRequest) CancelEvent() error {
// true by default
r.SendEvents = false
return nil
}
// func (r *ResponseRequest) CancelEvent() error {
// // true by default
// r.SendEvents = false
// return nil
// }
type ParsedRequest struct {
RemoteAddr string
@ -77,6 +77,7 @@ type ParsedRequest struct {
ResponseChannel chan ResponseRequest
}
// Generate a ParsedRequest from a http.Request. ParsedRequest can be consumed by the Waap Engine
func NewParsedRequestFromRequest(r *http.Request) (ParsedRequest, error) {
var err error
body := make([]byte, 0)

View file

@ -21,6 +21,7 @@ type Hook struct {
ApplyExpr []*vm.Program `yaml:"-"`
}
// @tko : todo - debug mode
func (h *Hook) Build() error {
if h.Filter != "" {
@ -31,7 +32,7 @@ func (h *Hook) Build() error {
h.FilterExpr = program
}
for _, apply := range h.Apply {
program, err := expr.Compile(apply, GetExprWAFOptions(GetEnv())...)
program, err := expr.Compile(apply, GetExprWAFOptions(GetHookEnv(WaapRuntimeConfig{}, ParsedRequest{}))...)
if err != nil {
return fmt.Errorf("unable to compile apply %s : %w", apply, err)
}
@ -40,18 +41,30 @@ func (h *Hook) Build() error {
return nil
}
type WaapTempResponse struct {
InBandInterrupt bool
OutOfBandInterrupt bool
Action string //allow, deny, captcha, log
HTTPResponseCode int
SendEvent bool //do we send an internal event on rule match
}
// runtime version of WaapConfig
type WaapRuntimeConfig struct {
Name string
OutOfBandRules []WaapCollection
OutOfBandTx ExtendedTransaction //is it a good idea ?
InBandRules []WaapCollection
InBandTx ExtendedTransaction //is it a good idea ?
DefaultRemediation string
CompiledOnLoad []Hook
CompiledPreEval []Hook
CompiledOnMatch []Hook
CompiledVariablesTracking []*regexp.Regexp
Config *WaapConfig
//those are ephemeral, created/destroyed with every req
OutOfBandTx ExtendedTransaction //is it a good idea ?
InBandTx ExtendedTransaction //is it a good idea ?
Response WaapTempResponse
}
type WaapConfig struct {
@ -59,12 +72,24 @@ type WaapConfig struct {
OutOfBandRules []string `yaml:"outofband_rules"`
InBandRules []string `yaml:"inband_rules"`
DefaultRemediation string `yaml:"default_remediation"`
DefaultPassAction string `yaml:"default_pass_action"`
BlockedHTTPCode int `yaml:"blocked_http_code"`
PassedHTTPCode int `yaml:"passed_http_code"`
OnLoad []Hook `yaml:"on_load"`
PreEval []Hook `yaml:"pre_eval"`
OnMatch []Hook `yaml:"on_match"`
VariablesTracking []string `yaml:"variables_tracking"`
}
func (w *WaapRuntimeConfig) ClearResponse() {
log.Infof("#-> %p", w)
w.Response = WaapTempResponse{}
log.Infof("-> %p", w.Config)
w.Response.Action = w.Config.DefaultPassAction
w.Response.HTTPResponseCode = w.Config.PassedHTTPCode
w.Response.SendEvent = true
}
func (wc *WaapConfig) Load(file string) error {
yamlFile, err := os.ReadFile(file)
if err != nil {
@ -74,6 +99,21 @@ func (wc *WaapConfig) Load(file string) error {
if err != nil {
return fmt.Errorf("unable to parse yaml file %s : %s", file, err)
}
if wc.Name == "" {
return fmt.Errorf("name cannot be empty")
}
if wc.DefaultRemediation == "" {
return fmt.Errorf("default_remediation cannot be empty")
}
if wc.BlockedHTTPCode == 0 {
wc.BlockedHTTPCode = 403
}
if wc.PassedHTTPCode == 0 {
wc.PassedHTTPCode = 200
}
if wc.DefaultPassAction == "" {
wc.DefaultPassAction = "allow"
}
return nil
}
@ -81,6 +121,7 @@ func (wc *WaapConfig) Load(file string) error {
func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) {
ret := &WaapRuntimeConfig{}
ret.Name = wc.Name
ret.Config = wc
ret.DefaultRemediation = wc.DefaultRemediation
//load rules
@ -160,13 +201,12 @@ func (w *WaapRuntimeConfig) ProcessOnMatchRules(request ParsedRequest, response
}
for _, applyExpr := range rule.ApplyExpr {
_, err := expr.Run(applyExpr, map[string]interface{}{
//"rules": w.InBandTx.Tx.Rules, //what is it supposed to be ? matched rules ?
"req": request,
"RemoveInbandRuleByID": w.RemoveInbandRuleByID,
"RemoveOutbandRuleByID": w.RemoveOutbandRuleByID,
"SetRemediation": response.SetRemediation,
"SetRemediationByID": response.SetRemediationByID,
"CancelEvent": response.CancelEvent,
// "req": request,
// "RemoveInbandRuleByID": w.RemoveInbandRuleByID,
// "RemoveOutbandRuleByID": w.RemoveOutbandRuleByID,
// "SetAction": response.SetAction,
// "SetRemediationByID": response.SetRemediationByID,
// "CancelEvent": response.CancelEvent,
})
if err != nil {
log.Errorf("unable to apply filter: %s", err)
@ -180,10 +220,7 @@ func (w *WaapRuntimeConfig) ProcessOnMatchRules(request ParsedRequest, response
func (w *WaapRuntimeConfig) ProcessPreEvalRules(request ParsedRequest) error {
for _, rule := range w.CompiledPreEval {
if rule.FilterExpr != nil {
output, err := expr.Run(rule.FilterExpr, map[string]interface{}{
//"rules": rules, //is it still useful ?
"req": request,
})
output, err := expr.Run(rule.FilterExpr, GetHookEnv(*w, request))
if err != nil {
return fmt.Errorf("unable to run filter %s : %w", rule.Filter, err)
}
@ -200,13 +237,7 @@ func (w *WaapRuntimeConfig) ProcessPreEvalRules(request ParsedRequest) error {
}
// here means there is no filter or the filter matched
for _, applyExpr := range rule.ApplyExpr {
_, err := expr.Run(applyExpr, map[string]interface{}{
"inband_rules": w.InBandRules,
"outband_rules": w.OutOfBandRules,
"req": request,
"RemoveInbandRuleByID": w.RemoveInbandRuleByID,
"RemoveOutbandRuleByID": w.RemoveOutbandRuleByID,
})
_, err := expr.Run(applyExpr, GetHookEnv(*w, request))
if err != nil {
log.Errorf("unable to apply filter: %s", err)
continue
@ -221,15 +252,37 @@ func (w *WaapRuntimeConfig) ProcessPreEvalRules(request ParsedRequest) error {
add the helpers to:
- remove by id-range
- remove by tag
- set remediation by tag/id-range
*/
func (w *WaapRuntimeConfig) RemoveInbandRuleByID(id int) {
w.InBandTx.RemoveRuleByIDWithError(id)
func (w *WaapRuntimeConfig) RemoveInbandRuleByID(id int) error {
return w.InBandTx.RemoveRuleByIDWithError(id)
}
func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(id int) {
w.OutOfBandTx.RemoveRuleByIDWithError(id)
func (w *WaapRuntimeConfig) CancelEvent() error {
w.Response.SendEvent = false
return nil
}
func (w *WaapRuntimeConfig) SetActionnByID(id int, action string) error {
panic("not implemented")
return nil
}
func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(id int) error {
return w.OutOfBandTx.RemoveRuleByIDWithError(id)
}
func (w *WaapRuntimeConfig) SetAction(action string) error {
w.Response.Action = action
return nil
}
func (w *WaapRuntimeConfig) SetHTTPCode(code int) error {
w.Response.HTTPResponseCode = code
return nil
}
func (w *WaapRuntimeConfig) ProcessInBandRules(request ParsedRequest) (*corazatypes.Interruption, error) {
@ -257,3 +310,22 @@ func (w *WaapRuntimeConfig) ProcessOutOfBandRules(request ParsedRequest) (*coraz
}
return nil, nil
}
type BodyResponse struct {
Action string `json:"action"`
HTTPStatus int `json:"http_status"`
}
func (w *WaapRuntimeConfig) GenerateResponse(interrupted bool) (BodyResponse, error) {
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
}
resp.Action = w.Config.DefaultRemediation
resp.HTTPStatus = w.Config.BlockedHTTPCode
return resp, nil
}

View file

@ -30,12 +30,3 @@ func GetExprWAFOptions(ctx map[string]interface{}) []expr.Option {
}
return baseHelpers
}
func SetRulesToInband(params ...any) (any, error) {
return nil, nil
}
func SetRulesToOutOfBand(params ...any) (any, error) {
return nil, nil
}