Skip to content

Commit

Permalink
fix: full io virtualization
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford committed Jul 29, 2023
1 parent 7bb256d commit b84805f
Show file tree
Hide file tree
Showing 18 changed files with 1,909 additions and 887 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ name = "wasi-virt"
exclude = ["testapp"]
members = [
"virtual-adapter",
"tests/components/do-everything",
"tests/components/file-read",
"tests/components/get-env"
]
Expand Down
79 changes: 53 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,46 @@

The virtualized component can be composed into a WASI Preview2 component with `wasm-tools compose`, providing fully-configurable WASI virtualization with host pass through or full encapsulation as needed.

Subsystem support:
Supports all of the current WASI subsystems:

- [x] Environment virtualization
- [x] Filesystem virtualization
- [ ] Stdio
- [x] Environment: Set environment variables, configure host environment variable permissions
- [x] Filesystem: Mount a read-only filesystem, configure host filesystem pass-through
- [x] Stdio: Currently only supports disabling
- [ ] Sockets
- [ ] Clocks
- [ ] [Your suggestion here](https://github.com/bytecodealliance/WASI-Virt/issues/new)
- [x] Exit
- [ ] Random

While current virtualization support is limited, the goal for this project is to support a wide range of WASI virtualization use cases.

Have an unhandled use case? Post a [virtualization suggestion](https://github.com/bytecodealliance/WASI-Virt/issues/new).

## Explainer

When wanting to run WebAssembly Components depending on WASI APIs in other environments it can provide
a point of friction having to port WASI interop to every target platform.
When wanting to run WebAssembly Components depending on WASI APIs in other environments it can provide a point of friction having to port WASI interop to every target platform.

In addition having full unrestricted access to core operating system APIs is a security concern.

WASI Virt allows taking a component that depends on WASI APIs and using a virtualized adapter to convert
it into a component that no longer depends on those WASI APIs, or conditionally only depends on them in
a configurable way.
WASI Virt allows taking a component that depends on WASI APIs and using a virtualized adapter to convert it into a component that no longer depends on those WASI APIs, or conditionally only depends on them in a configurable way.

For example, consider converting an application to a WebAssembly Component that assumes it can load
read some files from the filesystem, but never needs to write.
For example, consider converting an application to a WebAssembly Component that assumes it can load read some files from the filesystem, but never needs to write.

Using WASI Virt, those specific file paths can be mounted and virtualized into the component itself as
a post-compile operation, while banning the final component from being able to access the host's filesystem at
all. The inner program still imports a wasi filesystem, but the filesystem implementation is provided by another component, rather than in the host environment. The composition of these two components no longer has a
filesystem import, so it can be run in hosts (or other components) which do not provide a filesystem API.
Using WASI Virt, those specific file paths can be mounted and virtualized into the component itself as a post-compile operation, while banning the final component from being able to access the host's filesystem at all. The inner program still imports a wasi filesystem, but the filesystem implementation is provided by another component, rather than in the host environment. The composition of these two components no longer has a filesystem import, so it can be run in hosts (or other components) which do not provide a filesystem API.

## Basic Usage

```
cargo install wasi-virt
```

By default, all virtualizations encapsulate the host virtualization, unless explicitly enabling host passthrough via `--allow-env` or `--preopen`.

In all of the following examples, the `component.wasm` argument is optional. If omitted, then the virtualized adapter is output into `virt.wasm`, which can be composed into any component with:

```
wasm-tools compose component.wasm -d virt.wasm -o component.virt.wasm
```

### Env Virtualization

```
Expand Down Expand Up @@ -80,24 +84,36 @@ wasi-virt component.wasm --preopen /=/restricted/path -o virt.wasm
wasi-virt component.wasm --mount /virt-dir=./local --preopen /host-dir=/host/path -o virt.wasm
```

By default, all virtualizations encapsulate the host virtualization, unless explicitly enabling
host passthrough via `--allow-env` or `--preopen`.

In all of the above examples, the `component.wasm` argument is optional. If omitted, then the virtualized
adapter is output into `virt.wasm`, which can be composed into any component with:
### Stdio Virtualization

```
wasm-tools compose component.wasm -d virt.wasm -o component.virt.wasm
# Ignore all stdio entirely
wasi-virt --allow-stdio
# Throw an error if attempting any stdio
# (this is the default)
wasi-virt --deny-stdio
# Provide stdin as an inline string
wasi-virt --stdin=test
# Provide stdio
```

## API

When using the virtualization API, subsystems are passthrough by default instead of deny by default.

```rs
use std::fs;
use wasi_virt::{WasiVirt, FsEntry};

fn main() {
let virt = WasiVirt::new();
let mut virt = WasiVirt::new_reactor();

// ignore stdio
virt.stdio().ignore();

virt.env()
// provide an allow list of host env vars
.allow(&["PUBLIC_ENV_VAR"])
Expand All @@ -124,8 +140,19 @@ fn main() {
}
```

When calling a subsystem for the first time, its virtualization will be enabled. Subsystems
not used or configured at all will be omitted from the virtualization entirely.
When calling a subsystem for the first time, its virtualization will be enabled. Subsystems not used or configured at all will be omitted from the virtualization entirely.

## Contributing

To build, run `./build-adapter.sh` which builds the master `virtual-adapter` component, followed by `cargo build` to build
the virtualization tooling (located in `src`).

Tests can be run with `cargo test`, which runs the tests in `tests/virt.rs`.

Test components are built from the `tests/components` directory, and run against the configurations provided in `tests/cases`.

To update WASI, `lib/wasi_snapshot_preview1.reactor.wasm` needs
to be updated to the latest Wasmtime build, and the `wit/deps` folder needs to be updated with the latest WASI definitions.

# License

Expand All @@ -136,4 +163,4 @@ See [LICENSE](LICENSE) for more details.

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this project by you, as defined in the Apache-2.0 license,
shall be licensed as above, without any additional terms or conditions.
shall be licensed as above, without any additional terms or conditions.
Binary file modified lib/virtual_adapter.wasm
Binary file not shown.
13 changes: 12 additions & 1 deletion src/bin/wasi-virt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::{Context, Result};
use clap::Parser;
use std::{env, error::Error, fs, path::PathBuf, time::SystemTime};
use wasi_virt::WasiVirt;
use wasi_virt::{VirtExit, WasiVirt};
use wasm_compose::composer::ComponentComposer;

#[derive(Parser, Debug)]
Expand All @@ -21,6 +21,10 @@ struct Args {
#[arg(short, long, verbatim_doc_comment)]
config: Option<String>,

/// Allow the component to exit
#[arg(long)]
allow_exit: Option<bool>,

// ENV
/// Allow host access to all environment variables, or to a specific comma-separated list of variable names.
#[arg(long, num_args(0..), use_value_delimiter(true), require_equals(true), value_name("ENV_VAR"))]
Expand Down Expand Up @@ -79,6 +83,13 @@ fn main() -> Result<()> {
// By default, we virtualize all subsystems
// This ensures full encapsulation in the default (no argument) case

// exit
virt_opts.exit(if args.allow_exit.unwrap_or_default() {
VirtExit::Passthrough
} else {
Default::default()
});

// env options
let env = virt_opts.env();
match args.allow_env {
Expand Down
Loading

0 comments on commit b84805f

Please sign in to comment.