mod_wasm is an Apache Server (httpd) extension module able to run and serve WebAssembly binaries as endpoints.
It was unveiled at the ApacheCon North America on Oct 3rd, 2022 (see the slides). In addition, a full-detailed article can be found at VMware's Wasm Labs page.
mod_wasm can be useful in the different scenarios:
- Run existing applications from a variety of languages without modification.
- Execute untrusted third-party code in a secure environment without using containers.
- The Wasm capabilities model allows to enable/disable capabilites per HTTP request.
- Run the container:
docker run -p 8080:8080 projects.registry.vmware.com/wasmlabs/containers/httpd-mod-wasm:latest
- Open browser at: http://localhost:8080/wasm-module-endpoint
More details about the 'PrettyFy' WebApp Demo below.
- Overview
- 'PrettyFy' WebApp Demo
- Examples
- Building mod_wasm in your environment
- Building the container image
- Troubleshooting
- Debugging
The mod_wasm project is composed by two different libraries:
mod_wasm.so
(written in C) acts as the extension module for the Apache Server (httpd).libwasm_runtime.so
(written in Rust) offers a very high-level C-API to manage WebAssembly modules via Wasmtime.
To setup and manage WebAssembly binaries, mod_wasm offers new directives to the httpd.conf
configuration file:
Directive | Description |
---|---|
WasmRoot |
Set the root directory for Wasm modules. |
WasmModule |
Set the Wasm module file name. |
WasmDir |
Pre-open a host directory for the Wasm context. |
WasmMapDir |
Pre-open a host directory for the Wasm context and mount into a given directory. |
WasmArg |
Set an argument to be passed to the Wasm module context. |
WasmEnv |
Set an environment variable to be passed to the Wasm module context. |
WasmEnableCGI |
Enable/Disable CGI emulation mode for HTTP requests. |
mod_wasm plays a role in two different stages of the Apache Server workflow:
- The different
WasmXXX
directives are read fromhttpd.conf
during the boot up sequence. Once the configuration if fully processed, mod_wasm requests to the Wasm runtime to start loading the Wasm binaries. This is by far the most expensive operation and that is why it is executed only once during the Apache boot up sequence. When completed, the Apache Sever is ready to response to incoming HTTP requests. - For each HTTP request, mod_wasm builds the WASI context for the already-loaded Wasm binary. Next, the Wasm module is instantiated and the entry point is executed. The
stdout
from the Wasm module is redirected to the HTTP response, and thestderr
is appended to Apache Server's trace (usually at<httpd_dir>/dist/logs/error_log
).
mod_wasm also offers the ability to build a specific execution context per HTTP request. When setting up WasmEnableCGI On
, mod_wasm will pass HTTP headers as environtment variables to the Wasm module (they will be prefixed as HTTP_
). In addition, URL parameters are also passed in the environment variable QUERY_STRING
.
The 'PrettyFy' demo is a simple one-script, Python-based WebApp (see Examples).
- The Python interpreter has been compiled to WebAssembly.
- Note how the system platform is identified:
sys.platform = WASI
. - The app accepts
file=
as URL parameter to highlight a previously uploaded file: - Now, if you try a basic path traversal attack, it won't be succesful thanks to the WebAssembly sandboxed model where the Python interpreter is running:
This repo cointains several pre-built WebAssembly examples to play with.
Feel free to explore, modify and crash them!
- Apache Portable Runtime Project (apr)
- Apache Portable Runtime Utility Library (aprutil)
- Apache HTTP Server (development headers)
- Rust
- C compiler
pkg-config
libtool
For example, in an Ubuntu environment, you can install all dependencies by running:
apt install make pkg-config libtool-bin cargo libapr1-dev libaprutil1-dev apache2-dev
Also, cbindgen is needed to generate the C bindings from Rust:
cargo install cbindgen
make build
After the build is complete, you can find the module and an example
Apache configuration file under the dist
directory:
$ tree dist
dist
|-- conf
| `-- httpd.conf
`-- modules
`-- mod_wasm.so
Now, you can load this module in your Apache installation.
This repository contains all you need to build a local container image
To build the container you will need docker
, or podman
with the docker
alias
enabled.
For convenience we've organized the build commands in a Makefile, so you can use make
if you prefer.
You can build this image like so:
make container-image
The dev image will include all examples, along with additional tools required for future development. If you want to benchmark and compare running a python script via cgi vs via mod_wasm you will need to build this image.
make dev-image
This is a common error related to LD_LIBRARY_PATH
:
$ httpd
httpd: Syntax error on line XXX of <...>/httpd/dist/conf/httpd.conf:
Cannot load modules/mod_wasm.so into server: libwasm_runtime.so: cannot open shared object file: No such file or directory
Apache is loading modules/mod_wasm.so
but during the process it cannot find libwasm_runtime.so
. Either run Apache with LD_LIBRARY_PATH
pointing to the directory where libwasm_runtime.so
is located, or copy libwasm_runtime.so
to a directory such as /usr/lib
.
To get detailed debugging information about the Wasm execution, run the Apache Server with the following environment variables:
WASMTIME_BACKTRACE_DETAILS=1
RUST_BACKTRACE=full
Also, it is recommended to run Apache in debug mode (-X
option), this means only one process, only one worker, and without detaching from the terminal.
WASMTIME_BACKTRACE_DETAILS=1 RUST_BACKTRACE=full ./httpd -X