From 02eda47d468633512fce6b862a73334ee433c221 Mon Sep 17 00:00:00 2001 From: Karsten Siemer Date: Sat, 14 Oct 2023 08:37:31 +0200 Subject: [PATCH] chore(readme): Add Coraza Proxy WASM as WasmPlugin for Istio Guide (#233) Signed-off-by: Karsten Siemer --- README.md | 29 +++--- example/{ => envoy}/docker-compose.yml | 0 example/{ => envoy}/envoy-config.yaml | 0 example/istio/README.md | 130 +++++++++++++++++++++++++ magefiles/magefile.go | 18 ++-- 5 files changed, 156 insertions(+), 21 deletions(-) rename example/{ => envoy}/docker-compose.yml (100%) rename example/{ => envoy}/envoy-config.yaml (100%) create mode 100644 example/istio/README.md diff --git a/README.md b/README.md index bf6b3b7..bb5a2b3 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,18 @@ Web Application Firewall WASM filter built on top of [Coraza](https://github.com ```bash ▶ go run mage.go -l Targets: - build* builds the Coraza wasm plugin. - check runs lint and tests. - coverage runs tests with coverage and race detector enabled. - doc runs godoc, access at http://localhost:6060 - e2e runs e2e tests with a built plugin against the example deployment. - format formats code in this repository. - ftw runs ftw tests with a built plugin and Envoy. - lint verifies code quality. - runExample spins up the test environment, access at http://localhost:8080. - teardownExample tears down the test environment. - test runs all unit tests. + build* builds the Coraza wasm plugin. + check runs lint and tests. + coverage runs tests with coverage and race detector enabled. + doc runs godoc, access at http://localhost:6060 + e2e runs e2e tests with a built plugin against the example deployment. + format formats code in this repository. + ftw runs ftw tests with a built plugin and Envoy. + lint verifies code quality. + runEnvoyExample spins up the test environment, access at http://localhost:8080. + teardownEnvoyExample tears down the test environment. + ReloadEnvoyExample reloads the test environment. + test runs all unit tests. * default target ``` @@ -155,7 +156,11 @@ FTW_INCLUDE=920410 go run mage.go ftw ## Example: Spinning up the coraza-wasm-filter for manual tests -Once the filter is built, via the commands `mage runExample`, `mage reloadExample`, and `mage teardownExample` you can spin up, test, 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 [@demo-conf](./wasmplugin/rules/coraza-demo.conf) and [@crs-setup-demo-conf](./wasmplugin/rules/crs-setup-demo.conf). +Once the filter is built, via the commands `mage runEnvoyExample`, `mage reloadEnvoyExample`, and `mage teardownEnvoyExample` you can spin up, test, 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 [@demo-conf](./wasmplugin/rules/coraza-demo.conf) and [@crs-setup-demo-conf](./wasmplugin/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`. diff --git a/example/docker-compose.yml b/example/envoy/docker-compose.yml similarity index 100% rename from example/docker-compose.yml rename to example/envoy/docker-compose.yml diff --git a/example/envoy-config.yaml b/example/envoy/envoy-config.yaml similarity index 100% rename from example/envoy-config.yaml rename to example/envoy/envoy-config.yaml diff --git a/example/istio/README.md b/example/istio/README.md new file mode 100644 index 0000000..83586e7 --- /dev/null +++ b/example/istio/README.md @@ -0,0 +1,130 @@ +# Coraza Proxy WASM as WasmPlugin for Istio + +WasmPlugins allow the Istio proxy to be enhanced with WebAssembly filters. +The coraza proxy wasm acts as one of these filters, adding WAF features to Istio. +The execution order within Envoy's filter chain is set by phase and priority, facilitating +intricate interactions between user-provided WasmPlugins and Istio's built-in filters. + +## Istio Setup + +Given a multitude of possible Istio setups, we will only cover the most common one with the following assumptions: + +- Istio is installed in the `istio-system` namespace +- The mesh has an entrypoint served by a `istio-ingressgateway` service +- Services served by Istio have an `istio-proxy` sidecar + +## Getting started + +The coraza proxy wasm can filter traffic inside the mesh at multiple locations. + +### At Ingress gateway for all incoming traffic + +The envoy pod of the ingress-gateway can be configured to use the coraza proxy wasm as a filter, thus +filtering all incoming traffic. + +The following example shows how to configure embedded [Core Rule Set](https://github.com/coreruleset/coreruleset) +at the ingress gateway and use the coraza proxy wasm as a filter. + +It utilizes the +[WasmPlugin](https://istio.io/latest/docs/reference/config/proxy_extensions/wasm-plugin/) resource of Istio. +This way the filter can be configured via the `pluginConfig` field and envoy configuration is abstracted away. + +```yaml +apiVersion: extensions.istio.io/v1alpha1 +kind: WasmPlugin +metadata: + name: coraza-ingressgateway + namespace: istio-ingress +spec: + imagePullPolicy: IfNotPresent + phase: AUTHN + pluginConfig: + default_directives: default + directives_map: + default: + - Include @demo-conf + - SecDebugLogLevel 9 + - SecRuleEngine On + - Include @crs-setup-demo-conf + - Include @owasp_crs/*.conf + selector: + matchLabels: + app: istio-ingressgateway + istio: ingressgateway + url: oci://ghcr.io/corazawaf/coraza-proxy-wasm +``` + +The `selector` needs to match labels attached to the pods of the ingress gateway. +The `url` points to the OCI image of the coraza proxy wasm, which is provided by the project. + +All traffic entering the mesh via the ingress gateway will now be filtered by the coraza proxy wasm +and violations will be logged to the istio-proxy's log and a `403 Forbidden` response will be returned to the client. + +### At each namespace individually + +Traffic which has successfully passed the ingress gateway can be filtered at each namespace individually using +a similar approach as above. +The following example will show how to load the entire [Core Rule Set](https://github.com/coreruleset/coreruleset). + +```yaml +apiVersion: extensions.istio.io/v1alpha1 +kind: WasmPlugin +metadata: + name: coraza-core-rule-set + namespace: my-app +spec: + imagePullPolicy: IfNotPresent + phase: AUTHN + pluginConfig: + default_directives: default + directives_map: + default: + - Include @demo-conf + - SecDebugLogLevel 9 + - SecRuleEngine On + - Include @crs-setup-demo-conf + - Include @owasp_crs/*.conf + selector: + matchLabels: + app: my-app + url: oci://ghcr.io/corazawaf/coraza-proxy-wasm +``` + +The `selector` needs to match labels attached to the pods of the namespace where filtering is desired. +The `namespace` field needs to match the namespace of the pods. + +All traffic entering the namespace will now be filtered by the coraza proxy wasm using the +entire [Core Rule Set](https://github.com/coreruleset/coreruleset) and +violations will be logged to the istio-proxy's log and a `403 Forbidden` response will be returned to the client. + +Traffic which has already been filtered by the ingress gateway will not reach the namespace and will only be +logged to the istio-proxy's log in the namespace of the ingress-gateway. + +## Testing and Logs + +The coraza proxy wasm logs violations to the istio-proxy's log. + +The following example shows a violation to the rule `REQUEST-941-APPLICATION-ATTACK-XSS` which is included in the +istio-ingressgateways filter configuration. + +```bash +curl 'https://my-app.my-domain.com/anything?arg=' -IL +HTTP/2 403 +vary: Accept-Encoding +date: Tue, 10 Oct 2023 13:45:47 GMT +server: istio-envoy +``` + +Depending on your configuration a log in the istio-proxy's log will look like this: + +```text +envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1157 +wasm log istio-ingress.coraza-ingressgateway: [client "my-client"] +Coraza: Warning. Javascript method detected [file "@owasp_crs/REQUEST-941-APPLICATION-ATTACK-XSS.conf"] +[line "7982"] [id "941390"] [rev ""] [msg "Javascript method detected"] +[data "Matched Data: alert( found within ARGS_GET:arg: "] +[severity "critical"] [ver "OWASP_CRS/4.0.0-rc1"] [maturity "0"] [accuracy "0"] +[tag "application-multi"] [tag "language-multi"] [tag "attack-xss"] [tag "paranoia-level/1"] +[tag "OWASP_CRS"] [tag "capec/1000/152/242"] [hostname "my-hostname"] [uri "/anything/?arg="] +[unique_id "wTueIQloYpvpWNLzVfy"] thread=27 +``` \ No newline at end of file diff --git a/magefiles/magefile.go b/magefiles/magefile.go index 5607cf4..e70e677 100644 --- a/magefiles/magefile.go +++ b/magefiles/magefile.go @@ -272,19 +272,19 @@ func Ftw() error { return sh.RunWithV(env, "docker-compose", "--file", "ftw/docker-compose.yml", "run", "--rm", task) } -// RunExample spins up the test environment, access at http://localhost:8080. Requires docker-compose. -func RunExample() error { - return sh.RunWithV(map[string]string{"ENVOY_IMAGE": os.Getenv("ENVOY_IMAGE")}, "docker-compose", "--file", "example/docker-compose.yml", "up", "-d", "envoy-logs") +// RunEnvoyExample spins up the test environment of envoy, access at http://localhost:8080. Requires docker-compose. +func RunEnvoyExample() error { + return sh.RunWithV(map[string]string{"ENVOY_IMAGE": os.Getenv("ENVOY_IMAGE")}, "docker-compose", "--file", "example/envoy/docker-compose.yml", "up", "-d", "envoy-logs") } -// TeardownExample tears down the test environment. Requires docker-compose. -func TeardownExample() error { - return sh.RunV("docker-compose", "--file", "example/docker-compose.yml", "down") +// TeardownEnvoyExample tears down the test environment of envoy. Requires docker-compose. +func TeardownEnvoyExample() error { + return sh.RunV("docker-compose", "--file", "example/envoy/docker-compose.yml", "down") } -// ReloadExample reload the test environment (container) in case of envoy or wasm update. Requires docker-compose -func ReloadExample() error { - return sh.RunV("docker-compose", "--file", "example/docker-compose.yml", "restart") +// ReloadEnvoyExample reload the test environment (container) of envoy in case of envoy or wasm update. Requires docker-compose +func ReloadEnvoyExample() error { + return sh.RunV("docker-compose", "--file", "example/envoy/docker-compose.yml", "restart") } var Default = Build