Securing WebAssembly outside the browser
What's particularly interesting for many of us is the rise of WebAssembly System Interface (WASI), a way of running WebAssembly outside browsers and in classic server contexts.
WebAssembly and WASI already have some great security features, but I'm involved in a project called Enarx (completely open source, of course!) where we're looking to improve security for workloads in such a way that even the administrator, kernel, or hypervisor can't look inside them or impact their integrity. You will be able to run your workloads on hosts that you don't even trust, apart from the CPU and associated firmware!
We chose WebAssembly as our runtime because it offers the sort of platform-independence and easy integration into existing development environments that we value and that we believe developers and enterprises are looking for when they're designing and deploying workloads that include sensitive material, whether that's data or algorithms. WASI, in particular, turns out to be an excellent fit for the Trusted Execution Environments (TEEs) that we're targeting for running what we call "Keeps": the runtime environments for your workloads.
Hardware abstraction with WebAssembly
It is, however, a mammoth job, particularly as we're abstracting away the underlying processor architectures (currently two: Intel's SGX and AMD's SEV), so that you, the user, don't need to worry about them—all you need to do is write and compile your application (to WebAssembly, of course!), then request that it be deployed. Enarx, then, has lots of moving parts, and one of the key tasks for us has been to start the work to abstract away the underlying processor architectures so that we can prepare the runtime layers on top. Here's a general picture of the software layers and how they sit on top of the hardware platforms:
Let's step back and examine those layers briefly:
- Application: This is what you write, in whichever language you chose (C, C++, Rust, Go, Python, Java, Haskell…) and is the whole point of the project.
- Language bindings: When you compile your application into WebAssembly, the compiler that you use does all the work to ensure your application is compiled correctly for WebAssembly and pulls along any pieces it needs.
- WASI (WebAssembly System Interface): This is the implementation that allows your WebAssembly application to execute in a server-type context rather than in a browser (which was the initial implementation target for WebAssembly). WASI happens to fit the capabilities offered by TEEs pretty well.
- WebAssembly: This is the platform-specific implementation of Wasm that provides the key runtime pieces for the layers above it.
- Process-based Keep/VM-based Keep: This is where lots of the implementation of Enarx is happening now: providing a base layer for the various hardware options (currently, we're focusing on Intel's SGX and AMD's SEV) on which the WebAssembly layer can sit and execute.
What we announced—and demoed—at the end of March at Red Hat Summit is that Enarx now has an initial implementation of code to allow us to abstract away process-based and virtual machine-based types of architecture (with examples for SGX and SEV), so we can do this:
This seems deceptively simple, but what's going on under the covers is more than what is exposed in the picture above. The reality is more like this:
This gives more detail: the application that's running on both architectures (SGX on the left, SEV on the right) is the very same Linux binary (in fact, to be specific, it's an ELF static-PIE binary - but that's not particularly important at this level of detail). To be clear, this is not only the same source code, compiled for different platforms, but exactly the same binary with the very same hash signature. What's pretty astounding about this is that, in order to make it run on both platforms, the engineering team had to write two sets of seriously low-level code, including more than a little Assembly language providing the "plumbing" to allow the binary to run on both.
Securing WebAssembly one syscall at a time
This is a very big deal because, although we've only implemented a handful of syscalls on each platform—enough to make our simple binary run and print out a message, we now have a framework on which we know we can build. And what's next? Well, we need to expand that framework so that we can build the WebAssembly layers that will allow WebAssembly applications to run on top:
There's a long way to go, but this milestone shows that we have an initial framework that we can improve and on which we can build.
What's exciting about this milestone, from our point of view, is that we think it puts Enarx at a stage where more people can join and take part. There's still lots of low-level work to be done, but it's going to be easier to split up and to start some of the higher-level work, too. Enarx is completely open source, and we do all of our design work in the open, along with our daily stand-ups. You're welcome to browse our documentation, RFCs (mostly in draft at the moment), raise issues, and join our calls. You can find loads more information on the Enarx wiki, and we look forward to your involvement in the project.
As I explained at the beginning of this article, the goal is to support WebAssembly binaries, giving you—a developer or enterprise wanting to deploy your applications—a hardware-protected runtime environment where you can execute your workloads without having to trust the host on which they're running. If you're interested in this, please get in touch; we'd love to hear from you.