← Back Home

Using Wasm(Rust) in Envoy Proxy - Part 2

  1. rust
  2. wasm
  3. proxy
  4. envoy
  5. networking

This is my second blog post in this series. In last post, we used tinyGo to compile to wasm and use it in envoy proxy.. Today we are going to use Rust to compile to Wasm.

What is WASM

According to webassembly website,

“WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.”

Wasm is Efficient and Fast, Safe, Open And debuggable, and part of open web Platform.

What is Rust

Rust is a modern programming language that empower everyone to build reliable and efficient software. What is Envoy

According to envoy website,

“ENVOY IS AN OPEN SOURCE EDGE AND SERVICE PROXY, DESIGNED FOR CLOUD-NATIVE APPLICATIONS”

There are multiple ways to run envoy as documented here

Install Rust

Rust Install page has all the ways that Rust can be installed.

If installation is successful, you should be able to run this:

~ rustc --version
rustc 1.52.1 (9bc8c42bb 2021-05-09)

And get the rust compiler version on your machine.

Proxy Wasm Spec

To write webAssembly for proxies, there is an ABI specification called Proxy Wasm Spec

Proxy wasm spec also provide SDK’s in Go, Rust, C++ and many other.

Rust code for wasm filter

use log::trace;
use proxy_wasm::traits::;
use proxy_wasm::types::;

struct EvenAuthorizer {
context_id: u32,
}

impl Context for EvenAuthorizer {}

impl HttpContext for EvenAuthorizer {
fn on_http_request_headers(&mut self, _: usize) -> Action {
for (name, value) in &self.get_http_request_headers() {
trace!("In WASM : #{} -> {}: {}", self.context_id, name, value);
}

    match self.get_http_request_header("token") {
        Some(token) if token.parse::<u64>().is_ok() && is_even(token.parse().unwrap()) => {
            self.resume_http_request();
            Action::Continue
        }
        _ => {
            self.send_http_response(
                403,
                vec![("Powered-By", "proxy-wasm")],
                Some(b"Access forbidden.\n"),
            );
            Action::Pause
        }
    }
}
}

fn is_even(n: u64) -> bool {
return n % 2 == 0;
}

In this code, we are checking for a token header value and if the value is not even we return a 403.

Compiling to Wasm

We will use cargo build to compiler with wasm target.

cargo build –target=wasm32-unknown-unknown –release

This will give us a wasm file, that we can use as a plugin/filter in envoy.

Running Wasm plugin in envoy

We need to tell envoy from where to load this wasm filter/plugin. We do that by providing following information in envoy config file.

http_filters:
- name: envoy.filters.http.wasm
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
value:
config:
name: "my_plugin"
root_id: "my_root_id"
vm_config:
vm_id: "my_vm_id"
runtime: "envoy.wasm.runtime.v8"
code:
local:
filename: "hello.wasm"
allow_precompiled: true

Once everything is in place and we run envoy, and make request to a service that is proxied from envoy, I see following output.

$ curl  -H "token":"2" 0.0.0.0:18000
"Welcome to WASM land"
$ curl  -H "token":"1" 0.0.0.0:18000
Access forbidden.

We can see that envoy is able to apply our wasm filter on our http requests and if token value is not even, we get an error message.