up wip
This commit is contained in:
parent
c435447d8e
commit
2e60e8021c
|
@ -52,6 +52,11 @@ type WaapSource struct {
|
||||||
WaapRunners []WaapRunner //one for each go-routine
|
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 {
|
func (wc *WaapSource) UnmarshalConfig(yamlConfig []byte) error {
|
||||||
|
|
||||||
err := yaml.UnmarshalStrict(yamlConfig, &wc.config)
|
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{
|
runner := WaapRunner{
|
||||||
inChan: w.InChan,
|
inChan: w.InChan,
|
||||||
UUID: wafUUID,
|
UUID: wafUUID,
|
||||||
logger: wafLogger,
|
logger: wafLogger,
|
||||||
|
WaapRuntime: &wrt,
|
||||||
}
|
}
|
||||||
w.WaapRunners[nbRoutine] = runner
|
w.WaapRunners[nbRoutine] = runner
|
||||||
//most likely missign somethign here to actually start the runner :)
|
//most likely missign somethign here to actually start the runner :)
|
||||||
|
@ -247,10 +255,6 @@ func (w *WaapSource) Dump() interface{} {
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
type BodyResponse struct {
|
|
||||||
Action string `json:"action"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// should this be in the runner ?
|
// should this be in the runner ?
|
||||||
func (w *WaapSource) waapHandler(rw http.ResponseWriter, r *http.Request) {
|
func (w *WaapSource) waapHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
// parse the request only once
|
// parse the request only once
|
||||||
|
@ -264,6 +268,7 @@ func (w *WaapSource) waapHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
message := <-parsedRequest.ResponseChannel
|
message := <-parsedRequest.ResponseChannel
|
||||||
|
|
||||||
|
//@tko this parts needs to be redone
|
||||||
if message.Err != nil {
|
if message.Err != nil {
|
||||||
log.Errorf("Error while processing InBAND: %s", err)
|
log.Errorf("Error while processing InBAND: %s", err)
|
||||||
rw.WriteHeader(http.StatusInternalServerError)
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
|
|
@ -31,10 +31,10 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error {
|
||||||
return nil
|
return nil
|
||||||
case request := <-r.inChan:
|
case request := <-r.inChan:
|
||||||
r.logger.Infof("Requests handled by runner %s", request.UUID)
|
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()
|
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()
|
startParsing := time.Now()
|
||||||
|
|
||||||
//pre eval (expr) rules
|
//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)
|
r.logger.Errorf("unable to process PreEval rules: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
//inband WAAP rules
|
//inband WAAP rules
|
||||||
interrupt, err := r.WaapRuntime.ProcessInBandRules(request)
|
interrupt, err := r.WaapRuntime.ProcessInBandRules(request)
|
||||||
elapsed := time.Since(startParsing)
|
elapsed := time.Since(startParsing)
|
||||||
|
|
|
@ -25,18 +25,17 @@ func (t *ExtendedTransaction) RemoveRuleByIDWithError(id int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEnv() map[string]interface{} {
|
// simply used to ease the compilation & runtime of the hooks
|
||||||
ResponseRequest := ResponseRequest{}
|
func GetHookEnv(w WaapRuntimeConfig, request ParsedRequest) map[string]interface{} {
|
||||||
ParsedRequest := ParsedRequest{}
|
|
||||||
Rules := &WaapCollection{}
|
|
||||||
Tx := ExtendedTransaction{}
|
|
||||||
|
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"rules": Rules,
|
"inband_rules": w.InBandRules,
|
||||||
"req": ParsedRequest,
|
"outband_rules": w.OutOfBandRules,
|
||||||
"SetRemediation": ResponseRequest.SetRemediation,
|
"req": request,
|
||||||
"SetRemediationByID": ResponseRequest.SetRemediationByID,
|
"RemoveInbandRuleByID": w.RemoveInbandRuleByID,
|
||||||
"CancelEvent": ResponseRequest.CancelEvent,
|
"RemoveOutbandRuleByID": w.RemoveOutbandRuleByID,
|
||||||
"RemoveRuleByID": Tx.RemoveRuleByIDWithError,
|
"SetAction": w.SetAction,
|
||||||
|
"SetHTTPCode": w.SetHTTPCode,
|
||||||
|
"SetActionByID": w.SetActionnByID,
|
||||||
|
"CancelEvent": w.CancelEvent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,29 +36,29 @@ func NewResponseRequest(Tx experimental.FullTransaction, in *corazatypes.Interru
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResponseRequest) SetRemediation(remediation string) error {
|
// func (r *ResponseRequest) SetRemediation(remediation string) error {
|
||||||
if r.Interruption == nil {
|
// if r.Interruption == nil {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
r.Interruption.Action = remediation
|
// r.Interruption.Action = remediation
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (r *ResponseRequest) SetRemediationByID(ID int, remediation string) error {
|
// func (r *ResponseRequest) SetRemediationByID(ID int, remediation string) error {
|
||||||
if r.Interruption == nil {
|
// if r.Interruption == nil {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
if r.Interruption.RuleID == ID {
|
// if r.Interruption.RuleID == ID {
|
||||||
r.Interruption.Action = remediation
|
// r.Interruption.Action = remediation
|
||||||
}
|
// }
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (r *ResponseRequest) CancelEvent() error {
|
// func (r *ResponseRequest) CancelEvent() error {
|
||||||
// true by default
|
// // true by default
|
||||||
r.SendEvents = false
|
// r.SendEvents = false
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
type ParsedRequest struct {
|
type ParsedRequest struct {
|
||||||
RemoteAddr string
|
RemoteAddr string
|
||||||
|
@ -77,6 +77,7 @@ type ParsedRequest struct {
|
||||||
ResponseChannel chan ResponseRequest
|
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) {
|
func NewParsedRequestFromRequest(r *http.Request) (ParsedRequest, error) {
|
||||||
var err error
|
var err error
|
||||||
body := make([]byte, 0)
|
body := make([]byte, 0)
|
||||||
|
|
122
pkg/waf/waap.go
122
pkg/waf/waap.go
|
@ -21,6 +21,7 @@ type Hook struct {
|
||||||
ApplyExpr []*vm.Program `yaml:"-"`
|
ApplyExpr []*vm.Program `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @tko : todo - debug mode
|
||||||
func (h *Hook) Build() error {
|
func (h *Hook) Build() error {
|
||||||
|
|
||||||
if h.Filter != "" {
|
if h.Filter != "" {
|
||||||
|
@ -31,7 +32,7 @@ func (h *Hook) Build() error {
|
||||||
h.FilterExpr = program
|
h.FilterExpr = program
|
||||||
}
|
}
|
||||||
for _, apply := range h.Apply {
|
for _, apply := range h.Apply {
|
||||||
program, err := expr.Compile(apply, GetExprWAFOptions(GetEnv())...)
|
program, err := expr.Compile(apply, GetExprWAFOptions(GetHookEnv(WaapRuntimeConfig{}, ParsedRequest{}))...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to compile apply %s : %w", apply, err)
|
return fmt.Errorf("unable to compile apply %s : %w", apply, err)
|
||||||
}
|
}
|
||||||
|
@ -40,18 +41,30 @@ func (h *Hook) Build() error {
|
||||||
return nil
|
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
|
// runtime version of WaapConfig
|
||||||
type WaapRuntimeConfig struct {
|
type WaapRuntimeConfig struct {
|
||||||
Name string
|
Name string
|
||||||
OutOfBandRules []WaapCollection
|
OutOfBandRules []WaapCollection
|
||||||
OutOfBandTx ExtendedTransaction //is it a good idea ?
|
|
||||||
InBandRules []WaapCollection
|
InBandRules []WaapCollection
|
||||||
InBandTx ExtendedTransaction //is it a good idea ?
|
|
||||||
DefaultRemediation string
|
DefaultRemediation string
|
||||||
CompiledOnLoad []Hook
|
CompiledOnLoad []Hook
|
||||||
CompiledPreEval []Hook
|
CompiledPreEval []Hook
|
||||||
CompiledOnMatch []Hook
|
CompiledOnMatch []Hook
|
||||||
CompiledVariablesTracking []*regexp.Regexp
|
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 {
|
type WaapConfig struct {
|
||||||
|
@ -59,12 +72,24 @@ type WaapConfig struct {
|
||||||
OutOfBandRules []string `yaml:"outofband_rules"`
|
OutOfBandRules []string `yaml:"outofband_rules"`
|
||||||
InBandRules []string `yaml:"inband_rules"`
|
InBandRules []string `yaml:"inband_rules"`
|
||||||
DefaultRemediation string `yaml:"default_remediation"`
|
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"`
|
OnLoad []Hook `yaml:"on_load"`
|
||||||
PreEval []Hook `yaml:"pre_eval"`
|
PreEval []Hook `yaml:"pre_eval"`
|
||||||
OnMatch []Hook `yaml:"on_match"`
|
OnMatch []Hook `yaml:"on_match"`
|
||||||
VariablesTracking []string `yaml:"variables_tracking"`
|
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 {
|
func (wc *WaapConfig) Load(file string) error {
|
||||||
yamlFile, err := os.ReadFile(file)
|
yamlFile, err := os.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -74,6 +99,21 @@ func (wc *WaapConfig) Load(file string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse yaml file %s : %s", file, err)
|
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
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -81,6 +121,7 @@ func (wc *WaapConfig) Load(file string) error {
|
||||||
func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) {
|
func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) {
|
||||||
ret := &WaapRuntimeConfig{}
|
ret := &WaapRuntimeConfig{}
|
||||||
ret.Name = wc.Name
|
ret.Name = wc.Name
|
||||||
|
ret.Config = wc
|
||||||
ret.DefaultRemediation = wc.DefaultRemediation
|
ret.DefaultRemediation = wc.DefaultRemediation
|
||||||
|
|
||||||
//load rules
|
//load rules
|
||||||
|
@ -160,13 +201,12 @@ func (w *WaapRuntimeConfig) ProcessOnMatchRules(request ParsedRequest, response
|
||||||
}
|
}
|
||||||
for _, applyExpr := range rule.ApplyExpr {
|
for _, applyExpr := range rule.ApplyExpr {
|
||||||
_, err := expr.Run(applyExpr, map[string]interface{}{
|
_, err := expr.Run(applyExpr, map[string]interface{}{
|
||||||
//"rules": w.InBandTx.Tx.Rules, //what is it supposed to be ? matched rules ?
|
// "req": request,
|
||||||
"req": request,
|
// "RemoveInbandRuleByID": w.RemoveInbandRuleByID,
|
||||||
"RemoveInbandRuleByID": w.RemoveInbandRuleByID,
|
// "RemoveOutbandRuleByID": w.RemoveOutbandRuleByID,
|
||||||
"RemoveOutbandRuleByID": w.RemoveOutbandRuleByID,
|
// "SetAction": response.SetAction,
|
||||||
"SetRemediation": response.SetRemediation,
|
// "SetRemediationByID": response.SetRemediationByID,
|
||||||
"SetRemediationByID": response.SetRemediationByID,
|
// "CancelEvent": response.CancelEvent,
|
||||||
"CancelEvent": response.CancelEvent,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to apply filter: %s", err)
|
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 {
|
func (w *WaapRuntimeConfig) ProcessPreEvalRules(request ParsedRequest) error {
|
||||||
for _, rule := range w.CompiledPreEval {
|
for _, rule := range w.CompiledPreEval {
|
||||||
if rule.FilterExpr != nil {
|
if rule.FilterExpr != nil {
|
||||||
output, err := expr.Run(rule.FilterExpr, map[string]interface{}{
|
output, err := expr.Run(rule.FilterExpr, GetHookEnv(*w, request))
|
||||||
//"rules": rules, //is it still useful ?
|
|
||||||
"req": request,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to run filter %s : %w", rule.Filter, err)
|
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
|
// here means there is no filter or the filter matched
|
||||||
for _, applyExpr := range rule.ApplyExpr {
|
for _, applyExpr := range rule.ApplyExpr {
|
||||||
_, err := expr.Run(applyExpr, map[string]interface{}{
|
_, err := expr.Run(applyExpr, GetHookEnv(*w, request))
|
||||||
"inband_rules": w.InBandRules,
|
|
||||||
"outband_rules": w.OutOfBandRules,
|
|
||||||
"req": request,
|
|
||||||
"RemoveInbandRuleByID": w.RemoveInbandRuleByID,
|
|
||||||
"RemoveOutbandRuleByID": w.RemoveOutbandRuleByID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to apply filter: %s", err)
|
log.Errorf("unable to apply filter: %s", err)
|
||||||
continue
|
continue
|
||||||
|
@ -221,15 +252,37 @@ func (w *WaapRuntimeConfig) ProcessPreEvalRules(request ParsedRequest) error {
|
||||||
add the helpers to:
|
add the helpers to:
|
||||||
- remove by id-range
|
- remove by id-range
|
||||||
- remove by tag
|
- remove by tag
|
||||||
|
- set remediation by tag/id-range
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (w *WaapRuntimeConfig) RemoveInbandRuleByID(id int) {
|
func (w *WaapRuntimeConfig) RemoveInbandRuleByID(id int) error {
|
||||||
w.InBandTx.RemoveRuleByIDWithError(id)
|
return w.InBandTx.RemoveRuleByIDWithError(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(id int) {
|
func (w *WaapRuntimeConfig) CancelEvent() error {
|
||||||
w.OutOfBandTx.RemoveRuleByIDWithError(id)
|
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) {
|
func (w *WaapRuntimeConfig) ProcessInBandRules(request ParsedRequest) (*corazatypes.Interruption, error) {
|
||||||
|
@ -257,3 +310,22 @@ func (w *WaapRuntimeConfig) ProcessOutOfBandRules(request ParsedRequest) (*coraz
|
||||||
}
|
}
|
||||||
return nil, nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -30,12 +30,3 @@ func GetExprWAFOptions(ctx map[string]interface{}) []expr.Option {
|
||||||
}
|
}
|
||||||
return baseHelpers
|
return baseHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetRulesToInband(params ...any) (any, error) {
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetRulesToOutOfBand(params ...any) (any, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue