Envoy WASM filters in Rust
- 4 minutes read - 775 wordsA digression thanks to Sal Rashid who’s exploring WASM filters w/ Envoy.
The documentation is sparse but:
There is a Rust SDK but it’s not documented:
I found two useful posts by Rustaceans who were able to make use of it:
Here’s my simple use of the SDK’s examples.
wasme
curl -sL https://run.solo.io/wasme/install | sh
PATH=${PATH}:${HOME}/.wasme/bin
wasme --version
It may be possible to avoid creating an account on WebAssemblyHub if you’re staying local.
NOTE word to the wise, choose a username that does not include uppercase letters.
wasme login \
--username=${USERNAME} \
--password=${PASSWORD}
ls -l ${HOME}/.wasme
4096 Jul 23 10:30 bin
101 Jul 23 10:37 credentials.json
4096 Jul 23 12:11 store
Rust
git clone git@github.com:proxy-wasm/proxy-wasm-rust-sdk.git
cd proxy-wasm-rust
Example: hello_world
cargo build \
--release \
--target wasm32-unknown-unknown \
--example hello_world
ls -l target/wasm32-unknown-unknown/release/examples
670 Jul 23 10:22 hello_world.d
326 Jul 23 10:22 hello_world-d564b707ae954129.d
174406 Jul 23 10:22 hello_world-d564b707ae954129.wasm
174406 Jul 23 10:22 hello_world.wasm
Then build an OCI image for the filter:
wasme build precompiled \
target/wasm32-unknown-unknown/release/examples/hello_world.wasm \
--tag=webassemblyhub.io/${USERNAME}/hello_world:v0.0.1 \
--config=./runtime-config.json
This wasme build
requires a --config
file. I created a dummy project directory using wasme init ...
and copied the runtime-config.json
it created.
{
"type": "envoy_proxy",
"abiVersions": [
"v0-541b2c1155fffb15ccde92b8324f3e38f7339ba6",
"v0-097b7f2e4cc1fb490cc1943d0d633655ac3c522f"
],
"config": {
"rootIds": [
"hello_world"
]
}
}
Then we can wasme deploy
. The following command results in a istio/proxyv2:1.6.5
container configured to use the WASM filter that was built by the previous command:
wasme deploy envoy \
webassemblyhub.io/${USERNAME}/hello_world:v0.0.1 \
--envoy-image=istio/proxyv2:1.6.5
All being well, the container logs include:
wasm log hello_world hello_world : Hello, World!
wasm log hello_world hello_world : Hello, World!
wasm log hello_world hello_world : Hello, World!
wasm log hello_world hello_world : Hello, World!
wasm log hello_world hello_world : Hello, World!
wasm log hello_world hello_world : Hello, World!
wasm log hello_world hello_world : Hello, World!
wasm log hello_world hello_world : Hello, World!
wasm log hello_world : It's 2020-07-23 20:02:39.663791 UTC
wasm log hello_world : It's 2020-07-23 20:02:39.672452 UTC
wasm log hello_world : It's 2020-07-23 20:02:39.676520 UTC
wasm log hello_world : It's 2020-07-23 20:02:39.676535 UTC
wasm log hello_world : It's 2020-07-23 20:02:39.676527 UTC
wasm log hello_world : It's 2020-07-23 20:02:39.676535 UTC
wasm log hello_world : It's 2020-07-23 20:02:39.676527 UTC
wasm log hello_world : It's 2020-07-23 20:02:39.676520 UTC
wasm log hello_world : It's 2020-07-23 20:02:39.676749 UTC
Example: http_headers
cargo build \
--release \
--target wasm32-unknown-unknown \
--example http_headers
wasme build precompiled \
target/wasm32-unknown-unknown/release/examples/http_headers.wasm \
--tag=webassemblyhub.io/${USERNAME}/http_headers:v0.0.1 \
--config=./runtime-config.json
wasme deploy envoy \
webassemblyhub.io/dazwilkin/http_headers:v0.0.1 \
--envoy-image=istio/proxyv2:1.6.5
From another shell, access the Envoy endpoint:
curl \
--verbose \
http://localhost:8080/hello
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-length: 14
< content-type: text/plain
< hello: World
< powered-by: proxy-wasm
< date: Thu, 23 Jul 2020 19:28:48 GMT
< server: envoy
<
Hello, World!
* Connection #0 to host localhost left intact
If the request path is /hello
, the filter adds 2 headers to the response: hello: world
and powered-by: proxy-wasm
Exploring
If you enumerate the containers while wasme deploy
is running, you’ll find the istio/proxyv2:1.6.5
running:
docker container ls --format="{{.ID}}\n{{.Image}}\n{{.Ports}}"
edd5ddc4747c
istio/proxyv2:1.6.5
0.0.0.0:8080->8080/tcp, 0.0.0.0:19000->19000/tcp
The Envoy listener (including the WASM filter) is on :8080
as shown previously and the admin endpoint is on :19000
http://localhost:19000/
Through this admin interface, you can access the proxy’s configuration although this includes everything. You can also see the proxy’s Prometheus metrics
It may be easier to view the Envoy configuration using:
docker inspect hello_world
This includes the Envoy configuration in the Args
:
- "--disable-hot-restart"
- "--config-yaml"
- |
admin:
accessLogPath: /dev/null
address:
socketAddress:
address: 0.0.0.0
portValue: 19000
staticResources:
clusters:
- connectTimeout: 0.250s
dnsLookupFamily: V4_ONLY
hosts:
- socketAddress:
address: jsonplaceholder.typicode.com
ipv4Compat: true
portValue: 443
name: static-cluster
tlsContext:
sni: jsonplaceholder.typicode.com
type: LOGICAL_DNS
listeners:
- address:
socketAddress:
address: 0.0.0.0
portValue: 8080
filterChains:
- filters:
- config:
httpFilters:
- config:
config:
name: hello_world
rootId: hello_world
vmConfig:
code:
local:
filename: /.../.wasme/store/.../filter.wasm
runtime: envoy.wasm.runtime.v8
name: envoy.filters.http.wasm
- name: envoy.router
routeConfig:
name: test
virtualHosts:
- domains:
- '*'
name: jsonplaceholder
routes:
- match:
prefix: /
route:
autoHostRewrite: true
cluster: static-cluster
statPrefix: ingress_http
name: envoy.http_connection_manager
name: listener_0
NOTE
The first (and only) listeners[0]
includes a filterChains.filters[0].config.httpFilters[0]
that includes hello_world
and a filename
that should path to the locatin on your machine where wasme build
created the OCI container (filter.wasm
).
It also references a runtime
of envoy.wasm.runtime.v8
which correctly reflects Envoy’s use of V8 to host WASM binaries.
The exposed addresses|ports are defined as:
Key | Value |
---|---|
admin.address.socketAddress.portValue |
19000 |
listeners[0].address.socketAddress.portValue |
8080 |