When using WebAssembly (Wasm) on platforms different than the browser, we have to care about distribution. How can we securely transfer our Wasm modules to desired environments? How can we verify integrity to ensure Wasm modules were not compromised during distribution?
Let’s take a look at the container world. Container registries (those that implement the Docker Registry 2.0 standard (or to be more precise, the OCI distribution spec)) are a trusted distribution channel that addresses all the questions raised before. This does not mean that we have to package our Wasm modules as containers! Container registries can store and distribute more than just containers. Container registries can store any OCI compliant artifact. And that’s exactly what we will create from our Wasm modules to leverage container registries as a trusted distribution channel.
Create a Wasm module in Rust
For demonstration purposes, we will create a small Wasm module that calculates the product of three numbers. Nothing fancy here. Just something that we can take, package as OCI artifact, and distribute using a container registry.No worries, you don’t need to set up Rust - if you don’t want to - to follow this article. The Rust application and the resulting Wasm module are available on GitHub in this repository.
First, let’s create a new Rust library project using cargo new sample --lib
.
We have to tell the Rust compiler that we want a dynamic system library (which results in a .wasm
file when compiling to the wasm32-wasi
platform). We do so by updating the Cargo.toml
file and specifying cdylib
as crate-type
:
[package]
name = "sample"
version = "0.1.0"
edition = "2018"
[dependencies]
[lib]
crate-type = ['cdylib']
Now, we will replace the contents of lib.rs
with the following:
/// returns the product calculated from three parameters v1, v2, and v3
#[no_mangle]
pub extern "C" fn multiply(v1: i32, v2: i32, v3: i32) -> i32 {
v1 * v2 *v3
}
To create a Wasm module from our Rust code, we have to compile it, targeting the wasm32-wasi
platform:
# compile library crate for the wasm32-wasi platform
cargo build --target wasm32-wasi
At this point, we can also test the Wasm module locally using wasmtime
to verify it works as expected:
# Execute the multiply function via wasmtime
wasmtime --invoke multiply ./target/wasm32-wasi/debug/sample.wasm 3 3 3
27
Great! Our module works as expected, so we can finally distribute our module as OCI compliant artifact.
Publish OCI artifact in Azure Container Registry
We need an OCI distribution spec-compliant registry as the distribution channel. I will use an instance of Azure Container Registry (ACR). Let’s quickly create a new ACR instance for the sake of demonstration:
# variables
rgName="rg-blog-sample"
location="germanywestcentral"
acrName="blogsample"
subId="YOUR_AZURE_SUBSCRIPTION_ID"
# Login to Azure
az login
# Select Subscription
az account set --subscription $subId
# Create a Resource Group
az group create -n $rgName -l $location
# Create a Container Registry
az acr create -n $acrName\
-g $rgName\
-l $location\
--sku Basic\
--admin-enabled true
We can use a small tool called wasm-to-oci
to distribute Wasm modules using a container registry.
Install wasm-to-oci
You can download wasm-to-oci
- pre-compiled for your operating system - directly from its releases page on GitHub. On macOS and Linux, ensure that x
file-mode is set (chmod +x wasm-to-oci
). Add wasm-to-oci
to your PATH
environment variable and reload the terminal session for all operating systems.
Authenticate against ACR using Docker CLI
wasm-to-oci
uses authentication established via Docker CLI under the hood. That said, let’s quickly authenticate against ACR. The following snippet will use the administrative credentials for ACR (az acr credential show -n blogsample
). If you’ve access to an ACR with Premium SKU, you should create an individual token to and configure fine-granular access control for authentication and authorization instead.
# use Docker CLI to authenticate against ACR
docker login blogsample.azurecr.io -u blogsample -p yOurP@55w0rD
Push Wasm modules to ACR
Finally! We can push our Wasm module using wasm-to-oci push
and provide a fully-qualified name for the OCI artifact. The actual qualifier (here blogsample.azurecr.io
) may differ if you’re using something different than ACR:
# push wasm module as OCI artifact to ACR
wasm-to-oci push sample.wasm blogsample.azurecr.io/sample-wasm:0.0.1
Pull Wasm modules from ACR
We can use wasm-to-oci
also on the other side to pull Wasm modules. On the consumer side, you also have to authenticate against ACR using Docker CLI. Once authenticated, wasm-to-oci pull
is used to pull the desired Wasm module.
When wasm-to-oci
finished downloading the OCI artifact, the .wasm
file will be placed in the current folder.
# pull OCI artifact
wasm-to-oci pull blogsample.azurecr.io/sample-wasm:0.0.1
# verify WASM module works by invoking multiply again
wasmtime --invoke multiply sample.wasm 4 4 4
64
Conclusion
Using container registries such as Azure Container Registry as a distribution channel for Wasm modules helps a lot. Container registries are trusted services with enterprise degree authentication and authorization capabilities baked in. wasm-to-oci
simplifies the distribution process dramatically and addresses both needs distribution and consumption.
Several other container registries support OCI compliant artifacts too. You are not limited to Azure Container Registry at this point. However, integration with other Azure services and flexible authentication and authorization mechanisms (especially when using ACR on premium SKU) are great arguments why you should use ACR here.