Building WASM go filter plugin for envoy

WASM, web assembly is a binary code format generated from programming language. This portable code can be easily run on web or server clients. It executes fast when compared to javascript or proprietary codes.

Envoy, a service or edge proxy is used as a side-car in container based applications. It supports L3, L4 and L7 filters to play around with the ingress and egress traffic for the applications.

Since envoy runs as a side car so it is necessary that the filters used are efficient, portable and fast. So envoy started support to include wasm based filters for traffic management.

Web assembly Hub, from solo is developed to build, host and distribute such filters which can be used by envoy or other service mesh control-planes like Istio and Gloo which uses envoy.

It provides a wasme cli which can be used for building and hosting envoy based filters. In this blog we will explore this cli to build a sample go based plugin for envoy.

To install wasme

$ curl -sL https://run.solo.io/wasme/install | sh
$ export PATH=$HOME/.wasme/bin:$PATH

Check wasme version

$ wasme --versionwasme version 0.0.33

We will now use wasme to create a build a new filter.

$ wasme init ./new-filter --language tinygo✔ istio:1.7.x, gloo:1.6.x, istio:1.8.x, istio:1.9.xINFO[0002] extracting 1416 bytes to /root/new-filter

We can see a new-filter folder being created with the following folder structure.

cd new-filter/
root@test:~/new-filter# tree
.
├── go.mod
├── go.sum
├── main.go
└── runtime-config.json
0 directories, 4 files

Now edit the following function in main.go, this adds a sample HTTP header to the response.

// Override DefaultHttpContext.
func (ctx *httpHeaders) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
if err := proxywasm.SetHttpResponseHeader("hello", "world"); err != nil {
proxywasm.LogCriticalf("failed to set response header: %v", err)
}
return types.ActionContinue
}

Using the above go files let us now build the WASM proxy filter.

$ wasme build tinygo . -t testingwasme:goBuilding with tinygo...go: downloading github.com/tetratelabs/proxy-wasm-go-sdk v0.1.1
INFO[0004] adding image to cache... filter file=/tmp/wasme747396715/filter.wasm tag="testingwasme:go"
INFO[0004] tagged image digest="sha256:750d63889653e7117fcbc0831f10f0e1d3f7ec0c82fe5787b71d08a783e3393f" image="docker.io/library/testingwasme:go"

Check the filter image

$ wasme list
NAME TAG SIZE SHA UPDATED
docker.io/library/testingwasme go 247.6 kB 750d6388 31 Aug 21 07:51 UTC

We will now use the above filter and deploy envoy.

$ wasme deploy envoy testingwasme:go --envoy-image docker.io/istio/proxyv2:1.7.1

The above command will start the envoy proxy with the given filter.

We will now send a sample curl request to the proxy, to observe the HTTP headers in the response.

$ curl localhost:8080/posts/1 -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /posts/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Sun, 05 Sep 2021 09:20:09 GMT
< content-type: application/json; charset=utf-8
< content-length: 292
< x-powered-by: Express
< x-ratelimit-limit: 1000
< x-ratelimit-remaining: 999
< x-ratelimit-reset: 1623103733
< vary: Origin, Accept-Encoding
< access-control-allow-credentials: true
< cache-control: max-age=43200
< pragma: no-cache
< expires: -1
< x-content-type-options: nosniff
< etag: W/"124-yiKdLzqO5gfBrJFrcdJ8Yq0LGnU"
< via: 1.1 vegur
< cf-cache-status: HIT
< age: 1140
< accept-ranges: bytes
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=DHGGS1UMwNUu4DJqllZyPq32wpNwlrlWZzBBrPuccKi%2FASeuYnFb3x9qof23M%2F0nmQvgUXzMeECpiV4rDdrvOzAKQ38be1oAqc4vfPjEvGjW4zIFO946BT3rfyro9wphSRxAoQ%2FiYU8NRteztXGK"}],"group":"cf-nel","max_age":604800}
< nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
< server: envoy
< cf-ray: 689e614b5e6b0d2e-ARN
< alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400, h3=":443"; ma=86400
< x-envoy-upstream-service-time: 11
< hello: world
<
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
* Connection #0 to host localhost left intact
}

We can now notice that the sample header is added to the responses.

This provides a simple tutorial for building go based envoy filter. It can be extended more to build the required custom filters.

  1. https://webassemblyhub.io/
  2. https://docs.solo.io/
  3. https://docs.solo.io/web-assembly-hub/latest/tutorial_code/getting_started/

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Sirishagopigiri

Engineer by profession. Chef by passion (applicable only for some dishes :-P). Trying to become a blogger.