All Products
Search
Document Center

Alibaba Cloud Service Mesh:Implement a WAF on an ASM gateway with the Coraza Wasm plugin

Last Updated:Oct 23, 2026

Service Mesh (ASM) provides rich routing and request manipulation capabilities through the VirtualService API and extensive security capabilities through the AuthorizationPolicy API. However, in some scenarios, these APIs may not meet all your requirements. You can write custom WebAssembly (Wasm) filters and add them to an ASM gateway or sidecar proxy to implement custom behaviors for complex scenarios. This topic describes how to enable a Coraza Web Application Firewall (WAF) on an ASM gateway using the WasmPlugin API.

Prerequisites

Step 1: Build and push the Wasm OCI image

You can load a Wasm plugin in three ways. This topic demonstrates the second method: building a Wasm plugin into an OCI image and pushing it to Alibaba Cloud Container Registry.

  • Store the Wasm extension in a ConfigMap and mount it to the container. The ASM sidecar proxy or ASM gateway loads it from the local file system.

  • Package the Wasm extension as an OCI image and upload it to an image repository. The ASM sidecar proxy or ASM gateway pulls the image from the repository.

  • Upload the Wasm extension to cloud storage or another service that supports HTTP downloads. The ASM sidecar proxy or gateway downloads it over the network.

  1. Download the Coraza Wasm plugin and build the image.

    1. Run the following commands to download and unzip the Coraza Wasm plugin:

      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.zip
    2. Run the following command to rename the Coraza Wasm plugin file.

      After unzipping, you get a file named coraza-proxy-wasm.wasm. Rename it to plugin.wasm, as required by ASM.

      mv coraza-proxy-wasm.wasm plugin.wasm
    3. In the current directory, create a file named Dockerfile that contains the following content:

      FROM scratch
      ADD ./plugin.wasm ./plugin.wasm
    4. Run the following command to build the image:

      docker build -t coraza-proxy-wasm:latest . 
  2. Push the image to your Alibaba Cloud Container Registry Enterprise Edition instance.

    1. Create a namespace.

      1. Log on to the Container Registry console. In the left-side navigation pane, click Instances.

      2. On the Instances page, click the target Enterprise Edition instance.

      3. In the left-side navigation pane, choose Repository Management > Namespaces and click Create Namespace. Configure the settings and click OK.

        In this example, set the Namespace name to wasm.

    2. In the left-side navigation pane, choose Repository Management > Access Control. Click the Public Network tab, turn on the Access Entry switch, and add IP addresses to the public whitelist as needed.

      If you do not require a whitelist, delete the default entry.

      Note

      This topic uses a public network address for demonstration purposes. In a production environment, you can configure VPC access for enhanced privacy and network performance. For more information, see Configure access control over a VPC.

    3. Run the following command to log in to your Alibaba Cloud Container Registry Enterprise Edition instance. You will be prompted for your password.

      docker login --username=<your-username> enterprise-registry.cn-hangzhou.cr.aliyuncs.com
    4. Run the following commands to push the image to the image repository:

      docker tag ${ImageId} enterprise-registry.cn-hangzhou.cr.aliyuncs.com/wasm/coraza-proxy-wasm:latest
      docker push enterprise-registry.cn-hangzhou.cr.aliyuncs.com/wasm/coraza-proxy-wasm:latest
    5. Verify that the image was pushed successfully.

      1. Log on to the Container Registry console. In the left-side navigation pane, click Instances.

      2. On the Instances page, click the target Enterprise Edition instance.

      3. In the left-side navigation pane, choose Repository Management > Image Repositories. Click coraza-proxy-wasm and then, in the left-side navigation pane, click Image Versions.

        If an image with the latest tag is displayed, the image was pushed successfully.

Step 2: Configure image pull permissions

After creating the private repository, you must create a Kubernetes Secret that contains the credentials for your container registry. The ASM gateway or sidecar proxy uses this Secret to authenticate and pull the OCI image.

  1. Run the following command to create the Secret:

    kubectl create secret docker-registry -n istio-system coraza-wasm-proxy --docker-server=enterprise-registry.cn-hangzhou.cr.aliyuncs.com --docker-username=<your-username> --docker-password=<your-password>

    The Secret must be in the same namespace as the WasmPlugin. In this topic, the WasmPlugin is deployed to the istio-system namespace, so the Secret is also created there. Adjust the namespace specified by the -n flag based on your actual configuration.

  2. Run the following command to verify that the Secret was created successfully:

    kubectl -n istio-system get secret coraza-wasm-proxy 

Step 3: Apply the WasmPlugin

The WasmPlugin API declares the plugin and related configurations for the target gateway or sidecar proxy.

  1. Create a file named wasm-plugin.yaml with the following content:

    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://enterprise-registry.cn-hangzhou.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: default

    Parameter

    Description

    spec.url

    Specifies the URL and tag of the OCI image.

    spec.imagePullSecret

    Specifies the Secret to use for authenticating with the image repository.

    spec.selector

    Specifies the target workload where the plugin is applied.

    spec.phase

    Specifies the phase in the filter chain where the plugin is inserted.

    spec.pluginConfig

    Specifies the configuration for the plugin.

    • directives_map: Defines a directive set named default. The rule specifies that requests that contain the x-user-type header with a value of baned are denied.

    • default_directives: Specifies default as the directive set.

  2. In the KubeConfig environment for your ASM instance, run the following command to apply the WasmPlugin:

    kubectl apply -f wasm-plugin.yaml

Step 4: Verify the WasmPlugin

  1. Run the following command to access the httpbin application.

    Replace http://120.27.XXX.XX/ with your actual gateway address. For information about how to obtain the gateway address, see Obtain the IP address of an ingress gateway.

    curl -v http://120.27.XXX.XX/

    Expected output:

    *   Trying 120.27.XXX.XX:80...
    * Connected to 120.27.XXX.XX (120.27.XXX.XX) port 80 (#0)
    > GET / HTTP/1.1
    > Host: 120.27.XXX.XX
    > User-Agent: curl/7.86.0
    > Accept: */*
    >
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 200 OK
    < server: istio-envoy
    < date: Fri, 27 Oct 2023 03:21:19 GMT
    < content-type: text/html; charset=utf-8
    < content-length: 9593
    < access-control-allow-origin: *
    < access-control-allow-credentials: true
    < x-envoy-upstream-service-time: 2
    ......

    The command returns a 200 OK status code, which indicates the request was successful.

  2. Run the following command again to access the httpbin application, this time with the x-user-type: baned header.

    Replace http://120.27.XXX.XX/ with your actual gateway address.

    curl -v -H 'x-user-type: baned' http://120.27.XXX.XX/

    Expected output:

    *   Trying 120.27.XXX.XX:80...
    * Connected to 120.27.XXX.XX (120.27.XXX.XX) port 80 (#0)
    > GET / HTTP/1.1
    > Host: 120.27.XXX.XX
    > User-Agent: curl/7.86.0
    > Accept: */*
    > x-user-type: baned
    >
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 403 Forbidden
    < date: Fri, 27 Oct 2023 03:22:19 GMT
    < server: istio-envoy
    < content-length: 0
    <
    * Connection #0 to host 120.27.XXX.XX left intact

    The command returns a 403 Forbidden status code, which indicates that the gateway denied the request.