A tour of the WasmPlugin resource in Istio 1.12

Istio Wasm Solo.io

WebAssembly (Wasm) is a fast, efficient, and portable binary instruction format for programming languages that helps users to extend their web applications as well as their server side applications. It provides an embeddable and safe execution environment for platform extensions and has been gaining momentum with popular cloud-native technologies including Kubernetes, Envoy Proxy, and Istio (see the new WasmPlugin resource in Istio 1.12 here). 

Over the past few years, we’ve been helping to drive excitement and development around the potential of Wasm across the industry, specifically as a way to extend an Envoy-based data plane in API Gateways and service meshes. In March of 2020, Christian Posta published a blog about Web Assembly deployment for Istio, where users can define their WebAssembly extensions using a declarative format. The declarative custom resource mentioned in the blog was later renamed to the WasmDeployment resource and became a first class API in Gloo Mesh in late 2020.

The WasmDeployment custom resource along with the WebAssembly wasme tooling allows users to easily build Wasm OCI (Open Container Initiative) images and publish these images to the free WebAssemblyHub. These tools have enabled many of our enterprise users to customize the capabilities of an Istio data plane for backward compatibility or interoperability. Some of the top use cases we’ve recently seen are focused around security, metrics, and logging. In this blog post, we dive into deploying Wasm filters with the new WasmPlugin resource in Istio 1.12.

Istio 1.12’s WasmPlugin

In the recent 1.12 release, Istio introduced a new WasmPlugin resource to declaratively deploy Wasm filters in Istio. This resource has a similar configuration to the WasmDeployment custom resource which has been in Gloo Mesh since late 2020. Upon installation of Istio 1.12, you will notice a total of 14 Custom Resource Definitions (CRDs) including the new WasmPlugin custom resource as part of the extensions group.

kubectl get crd                                                                                                                            
NAME                                    CREATED AT
authorizationpolicies.security.istio.io 2021-11-30T14:43:19Z
destinationrules.networking.istio.io    2021-11-30T14:43:19Z
envoyfilters.networking.istio.io        2021-11-30T14:43:19Z
gateways.networking.istio.io            2021-11-30T14:43:19Z
istiooperators.install.istio.io         2021-11-30T14:43:19Z
peerauthentications.security.istio.io   2021-11-30T14:43:19Z
requestauthentications.security.istio.io   2021-11-30T14:43:19Z
serviceentries.networking.istio.io      2021-11-30T14:43:19Z
sidecars.networking.istio.io            2021-11-30T14:43:19Z
telemetries.telemetry.istio.io          2021-11-30T14:43:19Z
virtualservices.networking.istio.io     2021-11-30T14:43:19Z
wasmplugins.extensions.istio.io         2021-11-30T14:43:19Z
workloadentries.networking.istio.io     2021-11-30T14:43:19Z
workloadgroups.networking.istio.io      2021-11-30T14:43:19Z

To get started, let’s deploy the httpbin and sleep examples from the Istio distribution to the default namespace:

kubectl apply -f samples/sleep/sleep.yaml
kubectl apply -f samples/httpbin/httpbin.yaml

Download the Gloo Mesh Open Source meshctl CLI if you don’t have it yet. Using the meshctl CLI is not required for Istio, but it is proven to be the easiest way to build a Wasm plugin.

curl -sL https://run.solo.io/meshctl/install | sh

In my environment, I have meshctl version 1.2.4:

meshctl version
{
  "client": {
     "version": "1.2.4"
  },
  "server": null
}

Use the meshctl wasm command to create a simple wasm project called “myfilter”:

meshctl wasm init myfilter --language=assemblyscript

This will create the myfilter directory with scaffold code in assemblyscript. The filter simply adds “hello: world!” to the response header and you could modify it to add your own custom logic based on your business needs.

Use the meshctl wasm command to build a simple wasm image:

cd myfilter
meshctl wasm build assemblyscript -t webassemblyhub.io/linsun/helloworld:latest 

Push the image to the WebAssembly Hub. Log in to the Hub if you haven’t logged in first.

Deploy a simple WasmPlugin custom resource to your Kubernetes cluster. Note the selector selects the httpbin service by matching the “app: httpbin” label and the URL points to the pushed image to webassemblyhub.io.

cat wasm-plugin-helloworld.yaml                                                              
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
  name: helloworld
  namespace: default
spec:
  Selector:
    matchLabels:
      app: httpbin
  pluginName: add_header
  url: oci://webassemblyhub.io/linsun/helloworld:latest
kubectl apply -f wasm-plugin-helloworld.yaml 

Test httpbin from the sleep pod:

kubectl exec -it deploy/sleep -c sleep sh -- curl httpbin:8000/status/200 -v
*   Trying 10.96.77.189:8000...
* Connected to httpbin (10.96.77.189) port 8000 (#0)
> GET /status/200 HTTP/1.1
> Host: httpbin:8000
> User-Agent: curl/7.80.0-DEV
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< server: envoy
< date: Thu, 02 Dec 2021 03:24:14 GMT
< content-type: text/html; charset=utf-8
< access-control-allow-origin: *
< access-control-allow-credentials: true
< content-length: 0
< x-envoy-upstream-service-time: 2
< hello: world!
<
* Connection #0 to host httpbin left intact

The header “hello: world!” is there from httpbin’s response. You won’t have to annotate the httpbin deployment with anything extra, nor will you need to redeploy anything. Check the Istiod log:

kubectl logs deploy/istiod -n istio-system

The push event to push the “helloworld” WasmPlugin configuration to Envoy Proxy is logged:

2021-12-02T03:22:36.776725Z    info    ads    Push debounce stable[33] 1 for config WasmPlugin/default/helloworld: 101.0969ms since last change, 101.0917ms since last push, full=true

Check the httpbin’s istio-proxy log:

kubectl logs deploy/httpbin -c istio-proxy

You can see the add_header plugin from the helloworld WasmPlugin resource is executed in Envoy:

2021-12-02T03:22:38.046620Z    warning    envoy wasm    wasm log add_header: returning context for add_header

Understanding Envoy configuration for the new WasmPlugin resource

Let’s obtain the listener configuration for httpbin for port 15006. You may be asking, why port 15006? By default, the Istio sidecar captures all inbound traffic and redirects it to port 15006 of the Istio proxy container.

istioctl pc listener deploy/httpbin --port 15006 -ojson | grep helloworld -A 11 -B 1

The output shows the virtualInbound listener on port 15006, with six occurrences of the helloworld http filters, each added to its filter chain:

                             {
                                 "name": "default.helloworld",
                                 "configDiscovery": {
                                     "configSource": {
                                         "ads": {},
                                         "initialFetchTimeout": "0s",
                                         "resourceApiVersion": "V3"
                                     },
                                     "typeUrls": [
                                         "type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm"
                                     ]
                                 }
                             },

So, why are there six occurrences of these? Feel free to examine the output in detail to understand the filterchain, transport protocol, application protocol, or destination port that each filter chain matches.

Enable peer authentication policy for the default namespace:

cat peerauth.yaml                                                                           
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: default
spec:
  mtls:
mode: STRICT
kubectl apply -f peerauth.yaml                                                                    
peerauthentication.security.istio.io/default created

Obtain the listener configuration for httpbin for port 15006 again.

istioctl pc listener deploy/httpbin --port 15006 -ojson | grep helloworld -A 11 -B 1

You will only see three occurrences of the helloworld wasm httpfilter, which is easier for you to analyze the output. The reason for this is that after strict mutual TLS is enabled in the default namespace, the sidecar is configured to only allow the tls transport protocol thus no need to have the filter chains for the raw_buffer transport protocol, e.g. "transportProtocol": "raw_buffer".

The above example shows the most simple Wasm plugin you could build and apply to your services in the mesh with the newly-added WasmPlugin resource in Istio 1.12, without any need to patch or redeploy your service. The WasmPlugin resource contains rich configurations such as Wasm plugin injection phase in the filter chains and priorities within a given phase. While this example uses a Wasm OCI image built from Solo’s Gloo Mesh Open Source “meshctl wasm” CLI, you could also use Wasm compat images from Docker Hub using the docker or buildah CLI. Feel free to refer to Istio’s WasmPlugin resource for more details.

The future of Wasm

We are excited that our earlier vision around “WasmDeployment” has been shared across the Istio community, and the new WasmPlugin resource has now been developed and executed in Istio 1.12 through collaboration and hard work from many Istio contributors. We continue to believe Wasm will play a critical role for developers and operators to extend their data plane services and it’s great to see innovation across the open source ecosystem. We look forward to collaborating on future solutions to provide the simplest tooling and the best deployment experience for Wasm users. At Solo.io, we’re also continuing to drive development and improvements for customers using Wasm and provide common extension scenarios as formal APIs such as rate limiting or external auth.

To get involved with Wasm, join the discussion in the community slack or register for an upcoming event. Or if you’re ready to start building and sharing modules with the community, visit the WebAssembly Hub and tutorials in docs.