VirtualService routing rules and AuthorizationPolicy access controls do not cover application-layer threats such as SQL injection, cross-site scripting (XSS), or other OWASP Top 10 attacks. The Coraza WebAssembly (Wasm) plug-in solves this by adding a Web Application Firewall (WAF) directly inside the gateway's Envoy process. Every inbound request is inspected and malicious traffic is blocked before it reaches your services -- no external sidecar or extra network hop required.
Coraza is an open-source, OWASP-maintained WAF engine that uses ModSecurity SecRule syntax. To deploy it, package the Coraza Wasm binary as an OCI image and push it to a container registry. Then apply a WasmPlugin resource to load it into the Envoy filter chain.
How it works
Package the Coraza Wasm binary as an OCI image and push it to a container registry.
Create a Kubernetes Secret so the ASM gateway can pull the image.
Apply a
WasmPluginresource that loads the plug-in into the gateway's Envoy filter chain and configures SecRule directives.The gateway evaluates every inbound request against the configured rules and returns
403 Forbiddenfor any request that matches a deny rule.
Prerequisites
Before you begin, make sure that you have:
An ASM instance v1.18 or later with a Container Service for Kubernetes (ACK) cluster added. For more information, see Add a cluster to an ASM instance
Automatic sidecar proxy injection enabled. For more information, see Manage global namespaces
The HTTPBin application deployed and accessible. For more information, see Deploy the HTTPBin application
A Container Registry Enterprise Edition instance (supports OCI images). For more information, see Create a Container Registry Enterprise Edition instance
Docker installed. For more information, see Get Docker
Wasm plug-in loading methods
ASM supports three ways to load a Wasm plug-in. This tutorial uses the OCI image method.
Method | How it works | When to use |
OCI image (used in this tutorial) | Package the | Recommended for most production deployments. Versioned, auditable, and works with existing registry infrastructure. |
ConfigMap | Store the | Quick prototyping or air-gapped environments where a registry is unavailable. Limited by the ConfigMap 1 MiB size limit. |
HTTP download | Host the | Environments where a centralized artifact server already serves binaries over HTTP. |
Step 1: Build and push the Coraza Wasm OCI image
Download and prepare the plug-in binary
Download the Coraza Wasm plug-in and decompress it:
wget https://github.com/corazawaf/coraza-proxy-wasm/releases/download/0.3.0/coraza-proxy-wasm-0.3.0.zip unzip coraza-proxy-wasm-0.3.0.zipRename the binary to
plugin.wasm. ASM requires this exact file name:mv coraza-proxy-wasm.wasm plugin.wasmCreate a
Dockerfilein the same directory:FROM scratch ADD ./plugin.wasm ./plugin.wasmBuild the OCI image:
docker build -t coraza-proxy-wasm:latest .
Push the image to Container Registry
Create a namespace in Container Registry:
Log on to the Container Registry console. In the left-side navigation pane, click Instances.
On the Instances page, click the card of your Container Registry Enterprise Edition instance.
In the left-side navigation pane, choose . Click Create Namespace, set Namespace to
wasm, and click OK.
Enable Internet access for the registry: In the left-side navigation pane, choose . Click the Internet tab, turn on Enable Access over Internet, and add Internet whitelists based on your requirements. If you do not need whitelist-based control, delete the default whitelist.
NoteThis example uses the public endpoint. For production use, configure Virtual Private Cloud (VPC) access for better security and network performance. For more information, see Configure a VPC ACL.
Log in to the registry: Enter your password when prompted. Replace the following placeholders with your actual values:
Placeholder
Description
Example
<your-username>Logon username for the Container Registry instance
admin@china<your-registry-endpoint>The endpoint of your Container Registry instance
enterprise-registry.cn-hangzhoudocker login --username=<your-username> <your-registry-endpoint>.cr.aliyuncs.comTag and push the image: Replace
<image-id>with the image ID returned bydocker buildin the previous section.docker tag <image-id> <your-registry-endpoint>.cr.aliyuncs.com/wasm/coraza-proxy-wasm:latest docker push <your-registry-endpoint>.cr.aliyuncs.com/wasm/coraza-proxy-wasm:latestVerify the image was pushed: If the image with the
latesttag appears, the push was successful.In the Container Registry console, open your Enterprise Edition instance.
In the left-side navigation pane, choose . Click coraza-proxy-wasm.
In the left-side navigation pane, click Tags.
Step 2: Create a Secret for image pulls
The repository is private, so the ASM gateway needs credentials to pull the Wasm image. Create a Kubernetes Secret with the registry credentials:
kubectl create secret docker-registry coraza-wasm-proxy \
-n istio-system \
--docker-server=<your-registry-endpoint>.cr.aliyuncs.com \
--docker-username=<your-username> \
--docker-password=<your-password>The Secret must be in the same namespace as the WasmPlugin resource. This tutorial uses istio-system. Adjust the -n value if your plug-in targets a different namespace.
Verify the Secret:
kubectl -n istio-system get secret coraza-wasm-proxyStep 3: Apply the WasmPlugin resource
The WasmPlugin resource declares which plug-in to load, where to insert it in the Envoy filter chain, and how to configure it.
Create a file named
wasm-plugin.yamlwith the following content: The following table describes the key fields: In this example, thedefaultdirective set enables the Coraza rule engine and defines a single rule: deny any request whosex-user-typeheader equalsbaned.Field
Description
spec.urlOCI image address including the tag. Supports
oci://,file://, andhttp[s]://schemes.spec.imagePullSecretName of the Kubernetes Secret with registry credentials.
spec.imagePullPolicyImage pull policy.
spec.selectorLabel selector that determines which workloads load this plug-in. Here it targets the ingress gateway.
spec.phaseWhere to insert the plug-in in the filter chain.
AUTHNplaces it before authentication filters, so requests are inspected as early as possible.spec.pluginConfigCoraza-specific configuration.
directives_mapdefines named rule sets using SecRule syntax.default_directivesspecifies which rule set to apply by default.apiVersion: extensions.istio.io/v1alpha1 kind: WasmPlugin metadata: name: coraza-proxy-wasm namespace: istio-system spec: imagePullPolicy: IfNotPresent imagePullSecret: coraza-wasm-proxy selector: matchLabels: istio: ingressgateway url: oci://<your-registry-endpoint>.cr.aliyuncs.com/wasm/coraza-proxy-wasm:latest phase: AUTHN pluginConfig: directives_map: default: - "SecDebugLogLevel 9" - "SecRuleEngine On" - "SecRule REQUEST_HEADERS:x-user-type \"@streq baned\" \"id:101,phase:1,t:lowercase,deny,msg:'denied by header'\"" default_directives: defaultConnect to the ASM instance with kubectl and apply the resource:
kubectl apply -f wasm-plugin.yaml
Step 4: Verify the WAF plug-in
Test both allowed and blocked requests to confirm the plug-in is working.
Send a normal request to the HTTPBin application through the ingress gateway: Replace
<gateway-ip>with the IP address of your ingress gateway. For more information, see Obtain the IP address of the ingress gateway. Expected result -- the gateway returns200 OK:curl -v http://<gateway-ip>/> GET / HTTP/1.1 > Host: <gateway-ip> ... < HTTP/1.1 200 OK < server: istio-envoy ...Send a request with the blocked header: Expected result -- the gateway returns
403 Forbidden: The403response confirms that the Coraza WAF plug-in is active and enforcing the configured rule.curl -v -H 'x-user-type: baned' http://<gateway-ip>/> GET / HTTP/1.1 > Host: <gateway-ip> > x-user-type: baned ... < HTTP/1.1 403 Forbidden < server: istio-envoy ...