New WebAssembly infrastructure in Istio makes it easy to inject additional functionality into mesh deployments

Three years in the making, Istio now has a powerful extension mechanism for adding custom and third-party Wasm modules to sidecars in the mesh. Tetrate engineers Takeshi Yoneda and Lizan Zhou have been instrumental in making this happen. This post will cover the basics of Wasm in Istio and why it matters followed by a short tutorial on building your own Wasm plugin and deploying it to the mesh.

Why Wasm in Istio matters

Wasm promises to make the mesh and gateways easily extensible by developers. At Tetrate, we believe this technology is maturing quickly and therefore we have been investing in upstream Istio to make the configuration API, distribution mechanism, and extensibility experience starting from Go easier. We think this will enable a whole new direction for Istio.

What to expect: a new plugin configuration API and a reliable fetching and installation mechanism

There’s a new first-class API called WasmPlugin that lets you configure which plugins to install, where to fetch them (an OCI image, container-local file, or remote HTTP resource), where to install them (via a Workload selector), and a configuration struct to pass in to plugin instances. 

The image fetcher mechanism in istio-agent (introduced in Istio 1.9) that reliably retrieves Wasm binaries from remote HTTP sources has been expanded to support the retrieval of Wasm OCI images from any OCI registry, including Docker Hub, Google Container Registry (GCR), Amazon Elastic Container Registry (Amazon ECR), an others. 

This means you can create your own Wasm plugins or choose them off-the-shelf from any registry to extend Istio’s functionality with just a few lines of configuration. Istio will do all the work behind the scenes to fetch, validate, install, and configure them for you.

Istio Wasm under the hood

Istio’s extension mechanism uses the Proxy-Wasm application binary interface (ABI) specification, spearheaded by Lizan and Takeshi, which provides a set of proxy-agnostic streaming APIs and utility functions that can be implemented in any language for which there is a suitable SDK. As of this writing, there are Proxy-Wasm SDKs for AssemblyScript (TypeScript-ish), C++, Rust, Zig, and Go—which uses the TinyGo WebAssembly System Interface (WASI), to which Takeshi is also a primary contributor

How to get it: Tetrate Istio Distro

The easiest way to get Istio is with Tetrate’s open source get-mesh CLI and the Tetrate Istio Distro, a simple, safe enterprise-grade distribution of upstream Istio.

Wasm in action: build your own rate-limiting WebAssembly plugin

In our previous blog on Wasm extensions in Envoy, we showed how to develop WebAssembly plugins to enhance service mesh capabilities. The new Wasm Extension API makes it even simpler. This tutorial will explain how to use the Istio Wasm Extension API to implement rate limiting in Golang.

Prerequisites

Instructions

In this example we’ll deploy two applications to the cluster (sleep and httpbin). We’ll send a couple of requests from one container to the other without any Wasm extensions deployed. 

Next, we’ll create a Wasm module in Go that adds a custom header to the response and rejects any requests that go over the two requests/second rate limit.

We’ll push the Wasm module to a Docker container registry and use the new WasmPlugin resource that tells Istio where to download the Wasm module from and which workloads to apply the module to.

Step 1: Install Istio and deploy applications

We’ll start by downloading and installing Istio 1.12, and labelling the default Kubernetes namespace for automatic sidecar injection.

Next, we’ll deploy the sample httpbin and sleep apps.

Once the apps are deployed and running, we’ll send 4 requests per second from the sleep container to the httpbin container.

You’ll notice all requests succeeded and returned an HTTP 200.

Step 2: Develop, compile, and push the Wasm module

We’ll develop the Wasm module using Golang and the Proxy Wasm Golang SDK. We’ll use an existing example in the SDK repository called istio-rate-limiting.  To get started, clone the Github repository:

We’ll look at the code in the main.go. This is where we’ve implemented the rate limiting logic using the Proxy Wasm Golang SDK. The Wasm module does two things:

  • Adds a customized header to the response.
  • Performs a 2 requests/second rate limiting and rejects the exceeded requests.

    Here’s the snippet from main.go that shows how the functionality is implemented.

    In the OnHttpResponseHeaders function we’re iterating through the additionalHeaders variable and adding the headers to the response.

    In the OnHttpRequestHeaders function we get the current timestamp, compare it to the timestamp of a last refill time (for the rate limiter), and refill the tokens if needed. 

    If there’s no remaining tokens left, we send a 403 response with an extra header (powered-by: proxy-wasm-go-sdk!!

    Let’s compile the Golang program into the Wasm module using tinygo, and package it as a Docker image.

    We build a Docker image and push it to a container registry (replace the ${YOUR_DOCKER_REGISTRY_IMAGE} with your own Docker registry and image name). After this, your Wasm plugin is available to be consumed by your service mesh.

    Alternatively, you can use a pre-built Docker image that has the same code and it’s located a ghcr.io/tetratelabs/wasm-rate-limiting:v1.

    Step 3 :Configure the Istio Wasm extension API

    The Istio Wasm Extension API and the new WasmPlugin resource allows us to add the rate limiting Wasm module we pushed to the Docker registry to the httpbin workload. Here’s the YAML config for for the WasmPlugin resource:

    Once this configuration is deployed, Istiod pushes the corresponding configuration to the Envoy sidecars (the ones matching the labels we specified in the matchLabels field). The Istio agent in the sidecar will perform a remote fetch to download the Wasm module we just pushed, and then load it into the Wasm engine in Envoy runtime to be executed.

    Let’s save the above YAML to wasm.yaml and deploy it to the cluster:

    Step 4: Verify Rate Limiting Effect

    After we’ve deployed the WasmPlugin resource and Istio fetched the Wasm module from the registry, we can now verify how the rate limiting implemented in the Wasm plugin works.

    Just like before, we send 3 requests from the sleep container to the httpbin container. This time, once the Wasm plugin code is executed we can notice some differences in the output. First the who-am-i header gets injected by the Wasm plugin. The first two requests succeed with an HTTP 200 response code, and the remaining request fails with the HTTP 429. Additionally, we can notice an extra header called “powered-by” that’s also being injected by the Wasm plugin.

    Tutorial Summary

    To summarize, this tutorial demonstrates how to easily implement plugin functionality to extend the capabilities of Istio to suit your particular needs. This requires three steps

    1. Implement your plugin functionality in Golang.
    2. Compile, build, and push the Wasm module to an OCI-compliant Docker registry.
    3. Configure the service mesh workloads using the WasmPlugin resource to consume the Wasm module from the remote registry.

    The tutorial implements a single Wasm plugin to process HTTP requests. Beyond that, you can have multiple Wasm plugins where each individual plugin is responsible for a certain portion of the functionality.

    For example, one plugin at the AUTHN phase fetches or validates authentication credentials; another plugin at the AUTHZ phase implements your own customized authorization logic, and so on.

    The Istio Wasm extension also allows us to generate plugin metrics, or aggregate them across multiple Wasm plugins. The plugin provides a logging feature that allows us to write log messages to the Envoy sidecar. This is especially helpful for Wasm plugin debuggability and development.

    The current Istio Wasm API is in alpha stage and will be enhanced and stabilized in future Istio versions. This includes securely validating the Wasm Plugin itself by verifying the signature, support for pulling the Wasm plugin with a secret stored as Kubernetes Secret, etc.

    Further reading and additional resources

    At Tetrate we are working on improving the developer experience, and tetratelabs/proxy-wasm-golang-sdk contains the Golang SDK libraries this tutorial uses. You can find more examples such as http header manipulation, sample authorization, change routing behavior, etc.

    Tetrate Istio Distro is the easiest way to install, operate, and upgrade Istio.

    Sign up for Tetrate’s Istio Wasm plugin workshop to learn from creators of Wasm plugins in Istio ›

    Author(s)