From 7dde9e4c81f77934daf04e752e885144b23c0f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Wed, 5 Oct 2022 10:59:19 +0200 Subject: [PATCH 01/14] feat: adds metrics support. --- main.go | 75 ++++++++++++++++++++++++++++++++++-------------------- metrics.go | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 27 deletions(-) create mode 100644 metrics.go diff --git a/main.go b/main.go index c1f18bd..c7bf64b 100644 --- a/main.go +++ b/main.go @@ -6,8 +6,10 @@ package main import ( "context" "embed" + "fmt" "io/fs" "strconv" + "time" "github.com/corazawaf/coraza/v3" ctypes "github.com/corazawaf/coraza/v3/types" @@ -99,7 +101,12 @@ func (ctx *corazaPlugin) OnPluginStart(pluginConfigurationSize int) types.OnPlug // Override types.DefaultPluginContext. func (ctx *corazaPlugin) NewHttpContext(contextID uint32) types.HttpContext { - return &httpContext{contextID: contextID, tx: ctx.waf.NewTransaction(context.Background())} + return &httpContext{ + contextID: contextID, + tx: ctx.waf.NewTransaction(context.Background()), + // TODO(jcchavezs): figure out how/when enable/disable metrics + metrics: &wafMetrics{}, + } } type httpContext struct { @@ -111,11 +118,22 @@ type httpContext struct { httpProtocol string processedRequestBody bool processedResponseBody bool + metrics *wafMetrics +} + +// returnWrapper wraps the action return to make sure we record metrics around them. +func (ctx *httpContext) returnWrapper(phase string) func(types.Action, ...string) types.Action { + start := time.Now() + return func(action types.Action, tagsKV ...string) types.Action { + ctx.metrics.CountAction(phase, action, tagsKV...) + ctx.metrics.Duration(phase, time.Since(start)) + return action + } } // Override types.DefaultHttpContext. func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { - defer logTime("OnHttpRequestHeaders", currentTime()) + wrap := ctx.returnWrapper("on_http_request_headers") tx := ctx.tx // This currently relies on Envoy's behavior of mapping all requests to HTTP/2 semantics @@ -125,13 +143,13 @@ func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t path, err := proxywasm.GetHttpRequestHeader(":path") if err != nil { proxywasm.LogCriticalf("failed to get :path: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } method, err := proxywasm.GetHttpRequestHeader(":method") if err != nil { proxywasm.LogCriticalf("failed to get :method: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } protocol, err := proxywasm.GetProperty([]string{"request", "protocol"}) @@ -148,7 +166,7 @@ func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t hs, err := proxywasm.GetHttpRequestHeaders() if err != nil { proxywasm.LogCriticalf("failed to get request headers: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } for _, h := range hs { @@ -163,49 +181,49 @@ func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t interruption := tx.ProcessRequestHeaders() if interruption != nil { - return ctx.handleInterruption(interruption) + return wrap(ctx.handleInterruption(interruption), "rule_id", fmt.Sprint(interruption.RuleID)) } - return types.ActionContinue + return wrap(types.ActionContinue) } func (ctx *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action { - defer logTime("OnHttpRequestBody", currentTime()) + wrap := ctx.returnWrapper("on_http_request_body") tx := ctx.tx if bodySize > 0 { body, err := proxywasm.GetHttpRequestBody(0, bodySize) if err != nil { proxywasm.LogCriticalf("failed to get request body: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } _, err = tx.RequestBodyWriter().Write(body) if err != nil { proxywasm.LogCriticalf("failed to read request body: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } } if !endOfStream { - return types.ActionContinue + return wrap(types.ActionContinue) } ctx.processedRequestBody = true interruption, err := tx.ProcessRequestBody() if err != nil { proxywasm.LogCriticalf("failed to process request body: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } if interruption != nil { - return ctx.handleInterruption(interruption) + return wrap(ctx.handleInterruption(interruption), "rule_id", fmt.Sprint(interruption.RuleID)) } - return types.ActionContinue + return wrap(types.ActionContinue) } func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action { - defer logTime("OnHttpResponseHeaders", currentTime()) + wrap := ctx.returnWrapper("on_http_response_headers") tx := ctx.tx // Requests without body won't call OnHttpRequestBody, but there are rules in the request body @@ -215,17 +233,17 @@ func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) interruption, err := tx.ProcessRequestBody() if err != nil { proxywasm.LogCriticalf("failed to process request body: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } if interruption != nil { - return ctx.handleInterruption(interruption) + return wrap(ctx.handleInterruption(interruption), "rule_id", fmt.Sprint(interruption.RuleID)) } } status, err := proxywasm.GetHttpResponseHeader(":status") if err != nil { proxywasm.LogCriticalf("failed to get :status: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } code, err := strconv.Atoi(status) if err != nil { @@ -235,7 +253,7 @@ func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) hs, err := proxywasm.GetHttpResponseHeaders() if err != nil { proxywasm.LogCriticalf("failed to get response headers: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } for _, h := range hs { @@ -244,27 +262,27 @@ func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) interruption := tx.ProcessResponseHeaders(code, ctx.httpProtocol) if interruption != nil { - return ctx.handleInterruption(interruption) + return wrap(ctx.handleInterruption(interruption), "rule_id", fmt.Sprint(interruption.RuleID)) } - return types.ActionContinue + return wrap(types.ActionContinue) } func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action { - defer logTime("OnHttpResponseBody", currentTime()) + wrap := ctx.returnWrapper("on_http_response_body") tx := ctx.tx if bodySize > 0 { body, err := proxywasm.GetHttpResponseBody(0, bodySize) if err != nil { proxywasm.LogCriticalf("failed to get response body: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } _, err = tx.ResponseBodyWriter().Write(body) if err != nil { proxywasm.LogCriticalf("failed to read response body: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } } @@ -282,19 +300,22 @@ func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types interruption, err := tx.ProcessResponseBody() if err != nil { proxywasm.LogCriticalf("failed to process response body: %v", err) - return types.ActionContinue + return wrap(types.ActionContinue) } if interruption != nil { // TODO(M4tteoP): Address response body interruption logic after https://github.com/corazawaf/coraza-proxy-wasm/issues/26 return types.ActionContinue } - return types.ActionContinue + return wrap(types.ActionContinue) } // Override types.DefaultHttpContext. func (ctx *httpContext) OnHttpStreamDone() { - defer logTime("OnHttpStreamDone", currentTime()) + defer func(start time.Time) { + ctx.metrics.Duration("on_http_stream_done", time.Since(start)) + }(time.Now()) + tx := ctx.tx // Responses without body won't call OnHttpResponseBody, but there are rules in the response body diff --git a/metrics.go b/metrics.go new file mode 100644 index 0000000..dd3d356 --- /dev/null +++ b/metrics.go @@ -0,0 +1,44 @@ +package main + +import ( + "fmt" + "time" + + "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" + "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" +) + +type wafMetrics struct { + counters map[string]proxywasm.MetricCounter + histograms map[string]proxywasm.MetricHistogram +} + +var actions = map[types.Action]string{ + types.ActionContinue: "continue", + types.ActionPause: "pause", +} + +func (m *wafMetrics) CountAction(phase string, a types.Action, tagsKV ...string) { + fqn := fmt.Sprintf("waf_filter.phase_%s.action_%s", phase, actions[a]) + for i := 0; i < len(tagsKV)/2; i++ { + fqn = fqn + "." + tagsKV[2*i] + "." + tagsKV[2*i+1] + } + // TODO(jcchavezs): figure out if we are OK with dynamic creation of metrics + // or we generate the metrics on before hand. + counter, ok := m.counters[fqn] + if !ok { + counter = proxywasm.DefineCounterMetric(fqn) + m.counters[fqn] = counter + } + counter.Increment(1) +} + +func (m *wafMetrics) Duration(phase string, d time.Duration) { + fqn := fmt.Sprintf("waf_filter.phase_%s", phase) + histo, ok := m.histograms[fqn] + if !ok { + histo = proxywasm.DefineHistogramMetric(fqn) + m.histograms[fqn] = histo + } + histo.Record(uint64(d)) +} From 32e81584387656559560877a116817b36b1c6c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Wed, 5 Oct 2022 11:07:17 +0200 Subject: [PATCH 02/14] docs: adds comments to the metrics methods. --- metrics.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/metrics.go b/metrics.go index dd3d356..ae7423e 100644 --- a/metrics.go +++ b/metrics.go @@ -19,7 +19,10 @@ var actions = map[types.Action]string{ } func (m *wafMetrics) CountAction(phase string, a types.Action, tagsKV ...string) { - fqn := fmt.Sprintf("waf_filter.phase_%s.action_%s", phase, actions[a]) + // This metric is processed as: waf_filter.action_count{action="continue",phase="on_http_request_body",rule_id="100"}. + // The extraction rule is defined in envoy.yaml as a bootstrap configuration. + // See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/metrics/v3/stats.proto#config-metrics-v3-statsconfig. + fqn := fmt.Sprintf("waf_filter.action_count.phase_%s.action_%s", phase, actions[a]) for i := 0; i < len(tagsKV)/2; i++ { fqn = fqn + "." + tagsKV[2*i] + "." + tagsKV[2*i+1] } @@ -34,7 +37,7 @@ func (m *wafMetrics) CountAction(phase string, a types.Action, tagsKV ...string) } func (m *wafMetrics) Duration(phase string, d time.Duration) { - fqn := fmt.Sprintf("waf_filter.phase_%s", phase) + fqn := fmt.Sprintf("waf_filter.phase_duration.phase_%s", phase) histo, ok := m.histograms[fqn] if !ok { histo = proxywasm.DefineHistogramMetric(fqn) From d5dd727079ac2feffa26bee825a7179af1c9a91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Wed, 5 Oct 2022 11:57:10 +0200 Subject: [PATCH 03/14] chore: readds timing. --- main.go | 1 + timing.go | 6 +----- timing_on.go | 4 ---- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index c7bf64b..f72f999 100644 --- a/main.go +++ b/main.go @@ -125,6 +125,7 @@ type httpContext struct { func (ctx *httpContext) returnWrapper(phase string) func(types.Action, ...string) types.Action { start := time.Now() return func(action types.Action, tagsKV ...string) types.Action { + logTime(phase, start) ctx.metrics.CountAction(phase, action, tagsKV...) ctx.metrics.Duration(phase, time.Since(start)) return action diff --git a/timing.go b/timing.go index 80bfb35..af71907 100644 --- a/timing.go +++ b/timing.go @@ -9,10 +9,6 @@ import ( "time" ) -func currentTime() time.Time { - return time.Time{} -} - -func logTime(msg string, start time.Time) { +func logTime(string, time.Time) { // no-op without build tag } diff --git a/timing_on.go b/timing_on.go index be3014b..dbac6da 100644 --- a/timing_on.go +++ b/timing_on.go @@ -11,10 +11,6 @@ import ( "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" ) -func currentTime() time.Time { - return time.Now() -} - func logTime(msg string, start time.Time) { proxywasm.LogDebugf("%s took %s", msg, time.Since(start)) } From 62deb6b7b834c25422d08df12112b49c977b8890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Wed, 5 Oct 2022 11:59:20 +0200 Subject: [PATCH 04/14] chore: fix lint. --- metrics.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/metrics.go b/metrics.go index ae7423e..5553433 100644 --- a/metrics.go +++ b/metrics.go @@ -1,3 +1,6 @@ +// Copyright The OWASP Coraza contributors +// SPDX-License-Identifier: Apache-2.0 + package main import ( From 46ba8611345ab876be38d429d45ca2346fb1fc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Wed, 5 Oct 2022 12:08:36 +0200 Subject: [PATCH 05/14] chore: adds support for metrics in e2e. --- main.go | 2 +- metrics.go | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index f72f999..d1a7b1e 100644 --- a/main.go +++ b/main.go @@ -105,7 +105,7 @@ func (ctx *corazaPlugin) NewHttpContext(contextID uint32) types.HttpContext { contextID: contextID, tx: ctx.waf.NewTransaction(context.Background()), // TODO(jcchavezs): figure out how/when enable/disable metrics - metrics: &wafMetrics{}, + metrics: NewWAFMetrics(), } } diff --git a/metrics.go b/metrics.go index 5553433..feef906 100644 --- a/metrics.go +++ b/metrics.go @@ -16,6 +16,13 @@ type wafMetrics struct { histograms map[string]proxywasm.MetricHistogram } +func NewWAFMetrics() *wafMetrics { + return &wafMetrics{ + counters: make(map[string]proxywasm.MetricCounter), + histograms: make(map[string]proxywasm.MetricHistogram), + } +} + var actions = map[types.Action]string{ types.ActionContinue: "continue", types.ActionPause: "pause", @@ -25,9 +32,9 @@ func (m *wafMetrics) CountAction(phase string, a types.Action, tagsKV ...string) // This metric is processed as: waf_filter.action_count{action="continue",phase="on_http_request_body",rule_id="100"}. // The extraction rule is defined in envoy.yaml as a bootstrap configuration. // See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/metrics/v3/stats.proto#config-metrics-v3-statsconfig. - fqn := fmt.Sprintf("waf_filter.action_count.phase_%s.action_%s", phase, actions[a]) + fqn := fmt.Sprintf("waf_filter.action_count.phase=%s.action=%s", phase, actions[a]) for i := 0; i < len(tagsKV)/2; i++ { - fqn = fqn + "." + tagsKV[2*i] + "." + tagsKV[2*i+1] + fqn = fqn + "." + tagsKV[2*i] + "=" + tagsKV[2*i+1] } // TODO(jcchavezs): figure out if we are OK with dynamic creation of metrics // or we generate the metrics on before hand. @@ -40,7 +47,7 @@ func (m *wafMetrics) CountAction(phase string, a types.Action, tagsKV ...string) } func (m *wafMetrics) Duration(phase string, d time.Duration) { - fqn := fmt.Sprintf("waf_filter.phase_duration.phase_%s", phase) + fqn := fmt.Sprintf("waf_filter.phase_duration.phase=%s", phase) histo, ok := m.histograms[fqn] if !ok { histo = proxywasm.DefineHistogramMetric(fqn) From 1cd544268e18d5b93c7ca16ec6647188e3b93144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Wed, 5 Oct 2022 12:17:11 +0200 Subject: [PATCH 06/14] tests: adds metrics tests. --- main_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/main_test.go b/main_test.go index 583213e..ba516e3 100644 --- a/main_test.go +++ b/main_test.go @@ -15,6 +15,12 @@ import ( "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" ) +func checkMetric(t *testing.T, host proxytest.HostEmulator, phase string, a types.Action, expectedCounter int) { + value, err := host.GetCounterMetric(fmt.Sprintf("waf_filter.action_count.phase=%s.action=%s", phase, actions[a])) + require.NoError(t, err) + require.Equal(t, uint64(expectedCounter), value) +} + func TestLifecycle(t *testing.T) { reqHdrs := [][2]string{ {":path", "/hello"}, @@ -320,11 +326,21 @@ SecRuleEngine On\nSecResponseBodyAccess On\nSecRule RESPONSE_BODY \"@contains he require.Equal(t, types.ActionContinue, requestBodyAction) } } + checkMetric(t, host, "on_http_request_headers", types.ActionContinue, 1) + checkMetric(t, host, "on_http_request_headers", types.ActionPause, 0) + } else { + checkMetric(t, host, "on_http_request_headers", types.ActionContinue, 0) + checkMetric(t, host, "on_http_request_headers", types.ActionPause, 1) } if requestBodyAction == types.ActionContinue { responseHdrsAction = host.CallOnResponseHeaders(id, respHdrs, false) require.Equal(t, tt.responseHdrsAction, responseHdrsAction) + checkMetric(t, host, "on_http_request_body", types.ActionContinue, 1) + checkMetric(t, host, "on_http_request_body", types.ActionPause, 0) + } else { + checkMetric(t, host, "on_http_request_body", types.ActionContinue, 0) + checkMetric(t, host, "on_http_request_body", types.ActionPause, 1) } if responseHdrsAction == types.ActionContinue { @@ -339,6 +355,11 @@ SecRuleEngine On\nSecResponseBodyAccess On\nSecRule RESPONSE_BODY \"@contains he action := host.CallOnResponseBody(id, body, eos) require.Equal(t, types.ActionContinue, action) } + checkMetric(t, host, "on_http_response_headers", types.ActionContinue, 1) + checkMetric(t, host, "on_http_response_headers", types.ActionPause, 0) + } else { + checkMetric(t, host, "on_http_response_headers", types.ActionContinue, 0) + checkMetric(t, host, "on_http_response_headers", types.ActionPause, 1) } // Call OnHttpStreamDone. From e853eb5d3c627ba348af6fb89de9030f59d94b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Wed, 5 Oct 2022 15:18:34 +0200 Subject: [PATCH 07/14] chore: adds admin to envoy to be able to see the metrics. --- e2e/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 028a199..9369abc 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -10,7 +10,7 @@ services: - /conf/envoy-config.yaml volumes: - ../build:/build - - ../example:/conf # relying on envoy-config file from /example/ + - ../example:/conf # relying on envoy-config file from /example/ tests: depends_on: - envoy From 130307bffa7bccf7fe459f3ba8e2d227ba938964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 6 Oct 2022 10:57:34 +0200 Subject: [PATCH 08/14] chore: simplify metrics. --- main.go | 74 +++++++++++++++++++++------------------------------- main_test.go | 22 ++++------------ metrics.go | 41 ++++++++++------------------- 3 files changed, 49 insertions(+), 88 deletions(-) diff --git a/main.go b/main.go index d1a7b1e..3f34ac4 100644 --- a/main.go +++ b/main.go @@ -6,10 +6,8 @@ package main import ( "context" "embed" - "fmt" "io/fs" "strconv" - "time" "github.com/corazawaf/coraza/v3" ctypes "github.com/corazawaf/coraza/v3/types" @@ -45,6 +43,8 @@ type corazaPlugin struct { types.DefaultPluginContext waf coraza.WAF + + metrics *wafMetrics } // Override types.DefaultPluginContext. @@ -96,6 +96,8 @@ func (ctx *corazaPlugin) OnPluginStart(pluginConfigurationSize int) types.OnPlug ctx.waf = waf + ctx.metrics = NewWAFMetrics() + return types.OnPluginStartStatusOK } @@ -105,7 +107,7 @@ func (ctx *corazaPlugin) NewHttpContext(contextID uint32) types.HttpContext { contextID: contextID, tx: ctx.waf.NewTransaction(context.Background()), // TODO(jcchavezs): figure out how/when enable/disable metrics - metrics: NewWAFMetrics(), + metrics: ctx.metrics, } } @@ -121,20 +123,9 @@ type httpContext struct { metrics *wafMetrics } -// returnWrapper wraps the action return to make sure we record metrics around them. -func (ctx *httpContext) returnWrapper(phase string) func(types.Action, ...string) types.Action { - start := time.Now() - return func(action types.Action, tagsKV ...string) types.Action { - logTime(phase, start) - ctx.metrics.CountAction(phase, action, tagsKV...) - ctx.metrics.Duration(phase, time.Since(start)) - return action - } -} - // Override types.DefaultHttpContext. func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { - wrap := ctx.returnWrapper("on_http_request_headers") + ctx.metrics.CountTX() tx := ctx.tx // This currently relies on Envoy's behavior of mapping all requests to HTTP/2 semantics @@ -144,13 +135,13 @@ func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t path, err := proxywasm.GetHttpRequestHeader(":path") if err != nil { proxywasm.LogCriticalf("failed to get :path: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } method, err := proxywasm.GetHttpRequestHeader(":method") if err != nil { proxywasm.LogCriticalf("failed to get :method: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } protocol, err := proxywasm.GetProperty([]string{"request", "protocol"}) @@ -167,7 +158,7 @@ func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t hs, err := proxywasm.GetHttpRequestHeaders() if err != nil { proxywasm.LogCriticalf("failed to get request headers: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } for _, h := range hs { @@ -182,49 +173,47 @@ func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t interruption := tx.ProcessRequestHeaders() if interruption != nil { - return wrap(ctx.handleInterruption(interruption), "rule_id", fmt.Sprint(interruption.RuleID)) + return ctx.handleInterruption("http_request_headers", interruption) } - return wrap(types.ActionContinue) + return types.ActionContinue } func (ctx *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action { - wrap := ctx.returnWrapper("on_http_request_body") tx := ctx.tx if bodySize > 0 { body, err := proxywasm.GetHttpRequestBody(0, bodySize) if err != nil { proxywasm.LogCriticalf("failed to get request body: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } _, err = tx.RequestBodyWriter().Write(body) if err != nil { proxywasm.LogCriticalf("failed to read request body: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } } if !endOfStream { - return wrap(types.ActionContinue) + return types.ActionContinue } ctx.processedRequestBody = true interruption, err := tx.ProcessRequestBody() if err != nil { proxywasm.LogCriticalf("failed to process request body: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } if interruption != nil { - return wrap(ctx.handleInterruption(interruption), "rule_id", fmt.Sprint(interruption.RuleID)) + return ctx.handleInterruption("http_request_body", interruption) } - return wrap(types.ActionContinue) + return types.ActionContinue } func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action { - wrap := ctx.returnWrapper("on_http_response_headers") tx := ctx.tx // Requests without body won't call OnHttpRequestBody, but there are rules in the request body @@ -234,17 +223,17 @@ func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) interruption, err := tx.ProcessRequestBody() if err != nil { proxywasm.LogCriticalf("failed to process request body: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } if interruption != nil { - return wrap(ctx.handleInterruption(interruption), "rule_id", fmt.Sprint(interruption.RuleID)) + return ctx.handleInterruption("http_response_headers", interruption) } } status, err := proxywasm.GetHttpResponseHeader(":status") if err != nil { proxywasm.LogCriticalf("failed to get :status: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } code, err := strconv.Atoi(status) if err != nil { @@ -254,7 +243,7 @@ func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) hs, err := proxywasm.GetHttpResponseHeaders() if err != nil { proxywasm.LogCriticalf("failed to get response headers: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } for _, h := range hs { @@ -263,27 +252,26 @@ func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) interruption := tx.ProcessResponseHeaders(code, ctx.httpProtocol) if interruption != nil { - return wrap(ctx.handleInterruption(interruption), "rule_id", fmt.Sprint(interruption.RuleID)) + return ctx.handleInterruption("http_response_headers", interruption) } - return wrap(types.ActionContinue) + return types.ActionContinue } func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action { - wrap := ctx.returnWrapper("on_http_response_body") tx := ctx.tx if bodySize > 0 { body, err := proxywasm.GetHttpResponseBody(0, bodySize) if err != nil { proxywasm.LogCriticalf("failed to get response body: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } _, err = tx.ResponseBodyWriter().Write(body) if err != nil { proxywasm.LogCriticalf("failed to read response body: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } } @@ -301,22 +289,18 @@ func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types interruption, err := tx.ProcessResponseBody() if err != nil { proxywasm.LogCriticalf("failed to process response body: %v", err) - return wrap(types.ActionContinue) + return types.ActionContinue } if interruption != nil { // TODO(M4tteoP): Address response body interruption logic after https://github.com/corazawaf/coraza-proxy-wasm/issues/26 return types.ActionContinue } - return wrap(types.ActionContinue) + return types.ActionContinue } // Override types.DefaultHttpContext. func (ctx *httpContext) OnHttpStreamDone() { - defer func(start time.Time) { - ctx.metrics.Duration("on_http_stream_done", time.Since(start)) - }(time.Now()) - tx := ctx.tx // Responses without body won't call OnHttpResponseBody, but there are rules in the response body @@ -334,7 +318,9 @@ func (ctx *httpContext) OnHttpStreamDone() { proxywasm.LogInfof("%d finished", ctx.contextID) } -func (ctx *httpContext) handleInterruption(interruption *ctypes.Interruption) types.Action { +func (ctx *httpContext) handleInterruption(phase string, interruption *ctypes.Interruption) types.Action { + ctx.metrics.CountTXInterruption(phase, interruption.RuleID) + proxywasm.LogInfof("%d interrupted, action %q", ctx.contextID, interruption.Action) statusCode := interruption.Status if statusCode == 0 { diff --git a/main_test.go b/main_test.go index ba516e3..06abec3 100644 --- a/main_test.go +++ b/main_test.go @@ -15,8 +15,9 @@ import ( "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" ) -func checkMetric(t *testing.T, host proxytest.HostEmulator, phase string, a types.Action, expectedCounter int) { - value, err := host.GetCounterMetric(fmt.Sprintf("waf_filter.action_count.phase=%s.action=%s", phase, actions[a])) +func checkTXMetric(t *testing.T, host proxytest.HostEmulator, expectedCounter int) { + t.Helper() + value, err := host.GetCounterMetric("waf_filter.tx.total") require.NoError(t, err) require.Equal(t, uint64(expectedCounter), value) } @@ -308,6 +309,8 @@ SecRuleEngine On\nSecResponseBodyAccess On\nSecRule RESPONSE_BODY \"@contains he requestHdrsAction := host.CallOnRequestHeaders(id, reqHdrs, false) require.Equal(t, tt.requestHdrsAction, requestHdrsAction) + checkTXMetric(t, host, 1) + // Stream bodies in chunks of 5 if requestHdrsAction == types.ActionContinue { @@ -326,21 +329,11 @@ SecRuleEngine On\nSecResponseBodyAccess On\nSecRule RESPONSE_BODY \"@contains he require.Equal(t, types.ActionContinue, requestBodyAction) } } - checkMetric(t, host, "on_http_request_headers", types.ActionContinue, 1) - checkMetric(t, host, "on_http_request_headers", types.ActionPause, 0) - } else { - checkMetric(t, host, "on_http_request_headers", types.ActionContinue, 0) - checkMetric(t, host, "on_http_request_headers", types.ActionPause, 1) } if requestBodyAction == types.ActionContinue { responseHdrsAction = host.CallOnResponseHeaders(id, respHdrs, false) require.Equal(t, tt.responseHdrsAction, responseHdrsAction) - checkMetric(t, host, "on_http_request_body", types.ActionContinue, 1) - checkMetric(t, host, "on_http_request_body", types.ActionPause, 0) - } else { - checkMetric(t, host, "on_http_request_body", types.ActionContinue, 0) - checkMetric(t, host, "on_http_request_body", types.ActionPause, 1) } if responseHdrsAction == types.ActionContinue { @@ -355,11 +348,6 @@ SecRuleEngine On\nSecResponseBodyAccess On\nSecRule RESPONSE_BODY \"@contains he action := host.CallOnResponseBody(id, body, eos) require.Equal(t, types.ActionContinue, action) } - checkMetric(t, host, "on_http_response_headers", types.ActionContinue, 1) - checkMetric(t, host, "on_http_response_headers", types.ActionPause, 0) - } else { - checkMetric(t, host, "on_http_response_headers", types.ActionContinue, 0) - checkMetric(t, host, "on_http_response_headers", types.ActionPause, 1) } // Call OnHttpStreamDone. diff --git a/metrics.go b/metrics.go index feef906..dedb7d0 100644 --- a/metrics.go +++ b/metrics.go @@ -5,37 +5,21 @@ package main import ( "fmt" - "time" "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" - "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" ) type wafMetrics struct { - counters map[string]proxywasm.MetricCounter - histograms map[string]proxywasm.MetricHistogram + counters map[string]proxywasm.MetricCounter } func NewWAFMetrics() *wafMetrics { return &wafMetrics{ - counters: make(map[string]proxywasm.MetricCounter), - histograms: make(map[string]proxywasm.MetricHistogram), + counters: make(map[string]proxywasm.MetricCounter), } } -var actions = map[types.Action]string{ - types.ActionContinue: "continue", - types.ActionPause: "pause", -} - -func (m *wafMetrics) CountAction(phase string, a types.Action, tagsKV ...string) { - // This metric is processed as: waf_filter.action_count{action="continue",phase="on_http_request_body",rule_id="100"}. - // The extraction rule is defined in envoy.yaml as a bootstrap configuration. - // See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/metrics/v3/stats.proto#config-metrics-v3-statsconfig. - fqn := fmt.Sprintf("waf_filter.action_count.phase=%s.action=%s", phase, actions[a]) - for i := 0; i < len(tagsKV)/2; i++ { - fqn = fqn + "." + tagsKV[2*i] + "=" + tagsKV[2*i+1] - } +func (m *wafMetrics) incrementCounter(fqn string) { // TODO(jcchavezs): figure out if we are OK with dynamic creation of metrics // or we generate the metrics on before hand. counter, ok := m.counters[fqn] @@ -46,12 +30,15 @@ func (m *wafMetrics) CountAction(phase string, a types.Action, tagsKV ...string) counter.Increment(1) } -func (m *wafMetrics) Duration(phase string, d time.Duration) { - fqn := fmt.Sprintf("waf_filter.phase_duration.phase=%s", phase) - histo, ok := m.histograms[fqn] - if !ok { - histo = proxywasm.DefineHistogramMetric(fqn) - m.histograms[fqn] = histo - } - histo.Record(uint64(d)) +func (m *wafMetrics) CountTX() { + // This metric is processed as: waf_filter.tx.total + m.incrementCounter("waf_filter.tx.total") +} + +func (m *wafMetrics) CountTXInterruption(phase string, ruleID int) { + // This metric is processed as: waf_filter.tx.interruption{phase="on_http_request_body",rule_id="100"}. + // The extraction rule is defined in envoy.yaml as a bootstrap configuration. + // See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/metrics/v3/stats.proto#config-metrics-v3-statsconfig. + fqn := fmt.Sprintf("waf_filter.tx.interruptions.phase=%s.rule_id=%d", phase, ruleID) + m.incrementCounter(fqn) } From 095da5e2dc2dc060f0a2ab2e204042d4b0f58e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 6 Oct 2022 12:10:58 +0200 Subject: [PATCH 09/14] chore: adds support for tinygo 0.26. --- go.mod | 2 +- go.sum | 4 ++-- magefile.go | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3640e7d..a9e7bf3 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/tetratelabs/wazero v1.0.0-beta.2 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect - golang.org/x/net v0.0.0-20221002022538-bcab6841153b // indirect + golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 76cba5c..3d8d05b 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/net v0.0.0-20221002022538-bcab6841153b h1:6e93nYa3hNqAvLr0pD4PN1fFS+gKzp2zAXqrnTCstqU= -golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= +golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sys v0.0.0-20220913175220-63ea55921009 h1:PuvuRMeLWqsf/ZdT1UUZz0syhioyv1mzuFZsXs4fvhw= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/magefile.go b/magefile.go index 4fcbbae..57d5196 100644 --- a/magefile.go +++ b/magefile.go @@ -12,6 +12,7 @@ import ( "io" "os" "path/filepath" + "strings" "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" @@ -19,6 +20,7 @@ import ( "github.com/tetratelabs/wabin/wasm" ) +var tinygoVersion = "0.26" var addLicenseVersion = "04bfe4ee9ca5764577b029acc6a1957fd1997153" // https://github.com/google/addlicense var golangCILintVer = "v1.48.0" // https://github.com/golangci/golangci-lint/releases var gosImportsVer = "v0.3.1" // https://github.com/rinchsan/gosimports/releases/tag/v0.3.1 @@ -26,6 +28,26 @@ var gosImportsVer = "v0.3.1" // https://gi var errCommitFormatting = errors.New("files not formatted, please commit formatting changes") var errNoGitDir = errors.New("no .git directory found") +func init() { + if err := checkTinygoVersion(); err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } +} + +func checkTinygoVersion() error { + v, err := sh.Output("tinygo", "version") + if err != nil { + return fmt.Errorf("unexpected tinygo error: %v", err) + } + + if !strings.HasPrefix(v, fmt.Sprintf("tinygo version %s", tinygoVersion)) { + return fmt.Errorf("unexpected tinygo error, wanted %s", tinygoVersion) + } + + return nil +} + // Format formats code in this repository. func Format() error { if err := sh.RunV("go", "mod", "tidy"); err != nil { From 55c2bfe8c034c4f489e156b2f1236660ac4b0777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 6 Oct 2022 12:20:14 +0200 Subject: [PATCH 10/14] chore: restore timing. --- main.go | 5 +++++ timing.go | 6 ++++++ timing_on.go | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/main.go b/main.go index 3f34ac4..20af02e 100644 --- a/main.go +++ b/main.go @@ -125,6 +125,7 @@ type httpContext struct { // Override types.DefaultHttpContext. func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { + defer logTime("OnHttpRequestHeaders", currentTime()) ctx.metrics.CountTX() tx := ctx.tx @@ -180,6 +181,7 @@ func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) t } func (ctx *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action { + defer logTime("OnHttpRequestBody", currentTime()) tx := ctx.tx if bodySize > 0 { @@ -214,6 +216,7 @@ func (ctx *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types. } func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action { + defer logTime("OnHttpResponseHeaders", currentTime()) tx := ctx.tx // Requests without body won't call OnHttpRequestBody, but there are rules in the request body @@ -259,6 +262,7 @@ func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) } func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action { + defer logTime("OnHttpResponseBody", currentTime()) tx := ctx.tx if bodySize > 0 { @@ -301,6 +305,7 @@ func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types // Override types.DefaultHttpContext. func (ctx *httpContext) OnHttpStreamDone() { + defer logTime("OnHttpStreamDone", currentTime()) tx := ctx.tx // Responses without body won't call OnHttpResponseBody, but there are rules in the response body diff --git a/timing.go b/timing.go index af71907..f19d5de 100644 --- a/timing.go +++ b/timing.go @@ -9,6 +9,12 @@ import ( "time" ) +var zeroTime = time.Time{} + +func currentTime() time.Time { + return zeroTime +} + func logTime(string, time.Time) { // no-op without build tag } diff --git a/timing_on.go b/timing_on.go index dbac6da..be3014b 100644 --- a/timing_on.go +++ b/timing_on.go @@ -11,6 +11,10 @@ import ( "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" ) +func currentTime() time.Time { + return time.Now() +} + func logTime(msg string, start time.Time) { proxywasm.LogDebugf("%s took %s", msg, time.Since(start)) } From 65473ccabfc38fd9a5df5f4d003d5a773a4662b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 6 Oct 2022 14:07:37 +0200 Subject: [PATCH 11/14] chore: adds readme. --- README.md | 32 +++++++++++++++++++++++++++++--- example/docker-compose.yml | 5 +++++ example/envoy-config.yaml | 16 ++++++++++++++++ metrics.go | 6 +++--- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0d7bd5d..01886ad 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ Web Application Firewall WASM filter built on top of [Coraza](https://github.com ## Getting started `go run mage.go -l` lists all the available commands: -``` + +```bash ▶ go run mage.go -l Targets: build* builds the Coraza wasm plugin. @@ -26,9 +27,10 @@ Targets: ### Building the filter -``` +```bash go run mage.go build ``` + You will find the WASM plugin under `./build/main.wasm`. For performance purposes, some libs are built from non-Go implementations. The compiled polyglot wasm libs are already checked in under [./lib/](./lib/). It is possible to rely on the Dockerfiles under [./buildtools/](./buildtools/) if you wish to rebuild them from scratch @@ -100,19 +102,25 @@ configuration: ### Running go-ftw (CRS Regression tests) The following command runs the [go-ftw](https://github.com/fzipi/go-ftw) test suite against the filter with the CRS fully loaded. -``` + +```bash go run mage.go ftw ``` + Take a look at its config file [ftw.yml](./ftw/ftw.yml) for details about tests currently excluded. ## Example: Spinning up the coraza-wasm-filter for manual tests + Once the filter is built, via the commands `mage runExample` and `mage teardownExample` you can spin up and tear down the test environment. Envoy with the coraza-wasm filter will be reachable at `localhost:8080`. The filter is configured with the CRS loaded working in Anomaly Scoring mode. For details and locally tweaking the configuration refer to [coraza-demo.conf](./rules/coraza-demo.conf) and [crs-setup-demo.conf](./rules/crs-setup-demo.conf). In order to monitor envoy logs while performing requests you can run: + - Envoy logs: `docker-compose -f ./example/docker-compose.yml logs -f envoy-logs`. - Critical wasm (audit) logs: `docker-compose -f ./example/docker-compose.yml logs -f wasm-logs` ### Manual requests + Run `./e2e/e2e-example.sh` in order to run the following requests against the just set up test environment, otherwise manually execute and tweak them to grasp the behaviour of the filter: + ```bash # True positive requests: # Custom rule phase 1 @@ -140,3 +148,21 @@ curl -i -X POST 'http://localhost:8080/anything' --data "Hello world" # An usual user-agent curl -I --user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" localhost:8080 ``` + +### WAF Metrics + +Metrics are exposed in the prometheus format under `localhost:8082` (admin cluster in the envoy config). + +```bash +curl -s localhost:8082/stats/prometheus | grep waf_filter +``` + +and we get the metrics with the corresponding tags: + +```bash +# TYPE waf_filter_tx_interruptions counter +waf_filter_tx_interruptions{phase="http_request_body",rule_id="949110"} 1 +waf_filter_tx_interruptions{phase="http_response_headers",rule_id="949110"} 3 +# TYPE waf_filter_tx_total counter +waf_filter_tx_total{} 7 +``` diff --git a/example/docker-compose.yml b/example/docker-compose.yml index 88a7cda..ea25747 100644 --- a/example/docker-compose.yml +++ b/example/docker-compose.yml @@ -11,6 +11,7 @@ services: - chown -R 101:101 /home/envoy/logs volumes: - logs:/home/envoy/logs:rw + envoy: depends_on: - chown @@ -32,6 +33,8 @@ services: - logs:/home/envoy/logs:rw ports: - 8080:8080 + - 8082:8082 + envoy-logs: depends_on: - envoy @@ -43,6 +46,7 @@ services: - tail -c +0 -f /home/envoy/logs/envoy.log volumes: - logs:/home/envoy/logs:ro + wasm-logs: depends_on: - envoy @@ -53,5 +57,6 @@ services: - tail -c +0 -f /home/envoy/logs/envoy.log | grep --line-buffered "[critical][wasm]" volumes: - logs:/home/envoy/logs:ro + volumes: logs: diff --git a/example/envoy-config.yaml b/example/envoy-config.yaml index 905f829..f841914 100644 --- a/example/envoy-config.yaml +++ b/example/envoy-config.yaml @@ -1,3 +1,12 @@ +stats_config: + stats_tags: + # Envoy extracs the first matching group as a value. + # See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/metrics/v3/stats.proto#config-metrics-v3-statsconfig. + - tag_name: phase + regex: "(_phase=([a-z_]+))" + - tag_name: rule_id + regex: "(_ruleid=([0-9]+))" + static_resources: listeners: - address: @@ -64,3 +73,10 @@ static_resources: socket_address: address: httpbin port_value: 80 + +admin: + access_log_path: "/dev/null" + address: + socket_address: + address: 0.0.0.0 + port_value: 8082 diff --git a/metrics.go b/metrics.go index dedb7d0..731c136 100644 --- a/metrics.go +++ b/metrics.go @@ -31,14 +31,14 @@ func (m *wafMetrics) incrementCounter(fqn string) { } func (m *wafMetrics) CountTX() { - // This metric is processed as: waf_filter.tx.total + // This metric is processed as: waf_filter_tx_total m.incrementCounter("waf_filter.tx.total") } func (m *wafMetrics) CountTXInterruption(phase string, ruleID int) { - // This metric is processed as: waf_filter.tx.interruption{phase="on_http_request_body",rule_id="100"}. + // This metric is processed as: waf_filter_tx_interruption{phase="http_request_body",rule_id="100"}. // The extraction rule is defined in envoy.yaml as a bootstrap configuration. // See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/metrics/v3/stats.proto#config-metrics-v3-statsconfig. - fqn := fmt.Sprintf("waf_filter.tx.interruptions.phase=%s.rule_id=%d", phase, ruleID) + fqn := fmt.Sprintf("waf_filter.tx.interruptions_ruleid=%d_phase=%s", ruleID, phase) m.incrementCounter(fqn) } From 1323f3de5a86cfe91c6bac9a1a0fd0586c54bcde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Fri, 7 Oct 2022 11:05:09 +0200 Subject: [PATCH 12/14] chore: revert tinygo change. --- magefile.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/magefile.go b/magefile.go index 57d5196..d69ca47 100644 --- a/magefile.go +++ b/magefile.go @@ -20,7 +20,6 @@ import ( "github.com/tetratelabs/wabin/wasm" ) -var tinygoVersion = "0.26" var addLicenseVersion = "04bfe4ee9ca5764577b029acc6a1957fd1997153" // https://github.com/google/addlicense var golangCILintVer = "v1.48.0" // https://github.com/golangci/golangci-lint/releases var gosImportsVer = "v0.3.1" // https://github.com/rinchsan/gosimports/releases/tag/v0.3.1 @@ -28,26 +27,6 @@ var gosImportsVer = "v0.3.1" // https://gi var errCommitFormatting = errors.New("files not formatted, please commit formatting changes") var errNoGitDir = errors.New("no .git directory found") -func init() { - if err := checkTinygoVersion(); err != nil { - fmt.Printf("Error: %v\n", err) - os.Exit(1) - } -} - -func checkTinygoVersion() error { - v, err := sh.Output("tinygo", "version") - if err != nil { - return fmt.Errorf("unexpected tinygo error: %v", err) - } - - if !strings.HasPrefix(v, fmt.Sprintf("tinygo version %s", tinygoVersion)) { - return fmt.Errorf("unexpected tinygo error, wanted %s", tinygoVersion) - } - - return nil -} - // Format formats code in this repository. func Format() error { if err := sh.RunV("go", "mod", "tidy"); err != nil { From c17d3cac0ae95b68b97f726988c8c560a47fa4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Fri, 7 Oct 2022 11:10:38 +0200 Subject: [PATCH 13/14] chore: fix lint. --- magefile.go | 1 - 1 file changed, 1 deletion(-) diff --git a/magefile.go b/magefile.go index d69ca47..4fcbbae 100644 --- a/magefile.go +++ b/magefile.go @@ -12,7 +12,6 @@ import ( "io" "os" "path/filepath" - "strings" "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" From 4f2ad420dd5bdf5de7515fbf105737d68cef2057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Fri, 7 Oct 2022 11:07:49 +0200 Subject: [PATCH 14/14] docs: fixes typo. Co-authored-by: Anuraag Agrawal --- example/envoy-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/envoy-config.yaml b/example/envoy-config.yaml index f841914..1254236 100644 --- a/example/envoy-config.yaml +++ b/example/envoy-config.yaml @@ -1,6 +1,6 @@ stats_config: stats_tags: - # Envoy extracs the first matching group as a value. + # Envoy extracts the first matching group as a value. # See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/metrics/v3/stats.proto#config-metrics-v3-statsconfig. - tag_name: phase regex: "(_phase=([a-z_]+))"