From 04fa36d207924ea39e0dc1dee473ecac5aa2a074 Mon Sep 17 00:00:00 2001 From: Thomas Anderson <127358482+zc-devs@users.noreply.github.com> Date: Sat, 15 Jul 2023 14:13:57 +0300 Subject: [PATCH] Added SPOA errors --- docker/haproxy/haproxy.cfg | 8 +++-- internal/spoa.go | 61 ++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/docker/haproxy/haproxy.cfg b/docker/haproxy/haproxy.cfg index e886e69..c179dbd 100644 --- a/docker/haproxy/haproxy.cfg +++ b/docker/haproxy/haproxy.cfg @@ -26,7 +26,7 @@ frontend test_frontend bind *:443 ssl crt /usr/local/etc/haproxy/example.com.pem alpn h2,http/1.1 unique-id-format %[uuid()] unique-id-header X-Unique-ID - log-format "%ci:%cp\ [%t]\ %ft\ %b/%s\ %Th/%Ti/%TR/%Tq/%Tw/%Tc/%Tr/%Tt\ %ST\ %B\ %CC\ %CS\ %tsc\ %ac/%fc/%bc/%sc/%rc\ %sq/%bq\ %hr\ %hs\ %{+Q}r\ %ID\ spoa-error:\ %[var(txn.coraza.error)]\ waf-hit:\ %[var(txn.coraza.fail)]" + log-format "%ci:%cp\ [%t]\ %ft\ %b/%s\ %Th/%Ti/%TR/%Tq/%Tw/%Tc/%Tr/%Tt\ %ST\ %B\ %CC\ %CS\ %tsc\ %ac/%fc/%bc/%sc/%rc\ %sq/%bq\ %hr\ %hs\ %{+Q}r\ %ID\ waf-hit:\ %[var(txn.coraza.fail)]\ spoe-error:\ %[var(txn.coraza.error)]\ spoa-error:\ %[var(txn.coraza.err_code)]\ %[var(txn.coraza.err_msg)]" filter spoe engine coraza config /usr/local/etc/haproxy/coraza.cfg @@ -40,10 +40,14 @@ frontend test_frontend http-request silent-drop if { var(txn.coraza.action) -m str drop } http-response silent-drop if { var(txn.coraza.action) -m str drop } - # Deny in case of an error, when processing with the Coraza SPOA + # Deny in case of an error, when processing with the Coraza SPOE http-request deny deny_status 504 if { var(txn.coraza.error) -m int gt 0 } http-response deny deny_status 504 if { var(txn.coraza.error) -m int gt 0 } + # Deny in case of an error, when processing with the Coraza SPOA + http-request deny deny_status 504 if { var(txn.coraza.err_code) -m int gt 0 } + http-response deny deny_status 504 if { var(txn.coraza.err_code) -m int gt 0 } + # Deprecated, use action instead of fail #http-request deny deny_status 401 hdr waf-block "request" if { var(txn.coraza.fail) -m int eq 1 } #http-response deny deny_status 401 hdr waf-block "response" if { var(txn.coraza.fail) -m int eq 1 } diff --git a/internal/spoa.go b/internal/spoa.go index 2b9264a..3ea618b 100644 --- a/internal/spoa.go +++ b/internal/spoa.go @@ -102,6 +102,39 @@ func (s *SPOA) message(code int) []spoe.Action { } } +func (s *SPOA) error(code int, err error) []spoe.Action { + return []spoe.Action{ + spoe.ActionSetVar{ + Name: "err_code", + Scope: spoe.VarScopeTransaction, + Value: code, + }, + spoe.ActionSetVar{ + Name: "err_msg", + Scope: spoe.VarScopeTransaction, + Value: err.Error(), + }, + } +} + +func (s *SPOA) badRequestError(err error) []spoe.Action { + log.Error().Err(err).Msg("Bad request") + return s.error(1, err) +} + +func (s *SPOA) badResponseError(err error) []spoe.Action { + log.Error().Err(err).Msg("Bad response") + return s.error(2, err) +} + +func (s *SPOA) processRequestError(err error) []spoe.Action { + return s.error(3, err) +} + +func (s *SPOA) processResponseError(err error) []spoe.Action { + return s.error(4, err) +} + func (s *SPOA) readHeaders(headers string) (http.Header, error) { h := http.Header{} hs := strings.Split(headers, "\r\n") @@ -224,12 +257,12 @@ func (s *SPOA) processRequest(spoeMsg *spoe.Message) ([]spoe.Action, error) { req, err = NewRequest(spoeMsg) if err != nil { - return nil, err + return s.badRequestError(err), nil } app, err = s.getApplication(req.app) if err != nil { - return nil, err + return s.badRequestError(err), nil } tx = app.waf.NewTransactionWithID(req.id) @@ -240,12 +273,12 @@ func (s *SPOA) processRequest(spoeMsg *spoe.Message) ([]spoe.Action, error) { err = req.init() if err != nil { - return nil, err + return s.badRequestError(err), nil } headers, err := s.readHeaders(req.headers) if err != nil { - return nil, err + return s.badRequestError(err), nil } for key, values := range headers { for _, v := range values { @@ -255,7 +288,8 @@ func (s *SPOA) processRequest(spoeMsg *spoe.Message) ([]spoe.Action, error) { it, _, err := tx.WriteRequestBody(req.body) if err != nil { - return nil, err + tx.DebugLogger().Error().Err(err).Str("transaction_id", tx.ID()).Msg("Failed to write request body") + return s.processRequestError(err), nil } if it != nil { return s.processInterruption(it, hit), nil @@ -271,7 +305,8 @@ func (s *SPOA) processRequest(spoeMsg *spoe.Message) ([]spoe.Action, error) { it, err = tx.ProcessRequestBody() if err != nil { - return nil, err + tx.DebugLogger().Error().Err(err).Str("transaction_id", tx.ID()).Msg("Failed to process request body") + return s.processRequestError(err), nil } if it != nil { return s.processInterruption(it, hit), nil @@ -293,12 +328,12 @@ func (s *SPOA) processResponse(spoeMsg *spoe.Message) ([]spoe.Action, error) { resp, err = NewResponse(spoeMsg) if err != nil { - return nil, err + return s.badResponseError(err), nil } app, err = s.getApplication(resp.app) if err != nil { - return nil, err + return s.badResponseError(err), nil } txInterface, err := app.cache.Get(resp.id) @@ -312,12 +347,12 @@ func (s *SPOA) processResponse(spoeMsg *spoe.Message) ([]spoe.Action, error) { err = resp.init() if err != nil { - return nil, err + return s.badResponseError(err), nil } headers, err := s.readHeaders(resp.headers) if err != nil { - return nil, err + return s.badResponseError(err), nil } for key, values := range headers { for _, v := range values { @@ -327,7 +362,8 @@ func (s *SPOA) processResponse(spoeMsg *spoe.Message) ([]spoe.Action, error) { it, _, err := tx.WriteResponseBody(resp.body) if err != nil { - return nil, err + tx.DebugLogger().Error().Err(err).Str("transaction_id", tx.ID()).Msg("Failed to write response body") + return s.processResponseError(err), nil } if it != nil { return s.processInterruption(it, hit), nil @@ -340,7 +376,8 @@ func (s *SPOA) processResponse(spoeMsg *spoe.Message) ([]spoe.Action, error) { it, err = tx.ProcessResponseBody() if err != nil { - return nil, err + tx.DebugLogger().Error().Err(err).Str("transaction_id", tx.ID()).Msg("Failed to process response body") + return s.processResponseError(err), nil } if it != nil { return s.processInterruption(it, hit), nil