Skip to content

Commit

Permalink
Merge pull request #182 from bulwark-security/update-wasmtime
Browse files Browse the repository at this point in the history
Update wasmtime
  • Loading branch information
sporkmonger authored Nov 22, 2023
2 parents b18dda1 + 097eb04 commit 0d6b2ab
Show file tree
Hide file tree
Showing 17 changed files with 423 additions and 584 deletions.
715 changes: 272 additions & 443 deletions Cargo.lock

Large diffs are not rendered by default.

20 changes: 11 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ maintenance = { status = "experimental" }
[dependencies]
bulwark-config = { workspace = true }
bulwark-ext-processor = { workspace = true }

wit-component = { workspace = true }

chrono = { workspace = true }
envoy-control-plane = { workspace = true }
http = { workspace = true }
Expand All @@ -27,7 +30,6 @@ tokio = { workspace = true }
toml = { workspace = true }
tonic = { workspace = true }
tracing = { workspace = true }
wit-component = { workspace = true }

axum = { version = "0.6.18", features = ["http2"] }
cargo_metadata = "0.18.1"
Expand Down Expand Up @@ -85,14 +87,14 @@ bulwark-wasm-sdk = { path = "crates/wasm-sdk", version = "=0.3.0" }
bulwark-wasm-sdk-macros = { path = "crates/wasm-sdk-macros", version = "=0.3.0" }

# WASM dependencies
wasi-cap-std-sync = { version = "11" }
wasi-common = { version = "11" }
wasmtime = { version = "11", features = ["component-model"] }
wasmtime-types = { version = "11" }
wasmtime-wasi = { version = "11" }
wat = "1.0.57"
wit-bindgen = "0.7.0"
wit-component = "0.12.0"
wasi-cap-std-sync = { version = "15" }
wasi-common = { version = "15" }
wasmtime = { version = "15", features = ["component-model"] }
wasmtime-types = { version = "15" }
wasmtime-wasi = { version = "15" }
wat = "1.0.81"
wit-bindgen = "0.14.0"
wit-component = "0.18.2"

# Other shared external dependencies
anyhow = "1"
Expand Down
14 changes: 14 additions & 0 deletions adapter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# WASI Preview 1 Adapter

Because the Rust compiler output is WASI Preview 1 and Bulwark currently
uses WASI Preview 2, we need an adapter to convert the compiler's output.
This adapter should be updated with each new `wasmtime` release. The
`git-sha` file is used to record the git commit SHA the adapter came from.

This file is acquired from the `wasmtime` project's "CI" workflow:
<https://github.com/bytecodealliance/wasmtime/actions/workflows/main.yml>

The correct CI run is found by filtering for "branch:release-x.y.z",
locating the run that corresponds to the appropriate release tag, and then by
downloading the zip file for the "bins-wasi-preview1-component-adapter"
artifact.
1 change: 1 addition & 0 deletions adapter/git-sha
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6f0da842311d6de7afbb2936db347b516a298517
Binary file modified adapter/wasi_snapshot_preview1.reactor.wasm
Binary file not shown.
3 changes: 2 additions & 1 deletion crates/ext-processor/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1459,7 +1459,8 @@ impl BulwarkProcessor {
// TODO: refactor to process one plugin at a time and try to avoid having handle_decision_feedback join_all
for plugin_instance in plugin_instances {
let plugin_instance = plugin_instance.lock().await;
let (_, stdout, stderr) = plugin_instance.stdio().into_inner();
let stdout = plugin_instance.stdio().stdout_buffer();
let stderr = plugin_instance.stdio().stderr_buffer();
if !stdout.is_empty() {
let stdout = str::from_utf8(&stdout).unwrap();
for line in stdout.lines() {
Expand Down
1 change: 1 addition & 0 deletions crates/wasm-host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ thiserror = { workspace = true }
validator = { workspace = true }

async-trait = "0.1.68"
bytes = "1.5.0"
url = "2.3.1"

[dev-dependencies]
Expand Down
77 changes: 46 additions & 31 deletions crates/wasm-host/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ use {
net::IpAddr,
ops::DerefMut,
path::Path,
sync::{Arc, Mutex, MutexGuard, RwLock},
sync::{Arc, Mutex, MutexGuard},
},
url::Url,
validator::Validate,
wasmtime::component::{Component, Linker},
wasmtime::{AsContextMut, Config, Engine, Store},
wasmtime_wasi::preview2::{
pipe::{ReadPipe, WritePipe},
Table, WasiCtx, WasiCtxBuilder, WasiView,
pipe::MemoryOutputPipe, HostOutputStream, StdoutStream, Table, WasiCtx, WasiCtxBuilder,
WasiView,
},
};

Expand Down Expand Up @@ -334,20 +334,18 @@ impl RequestContext {
request: Arc<bulwark_wasm_sdk::Request>,
) -> Result<RequestContext, ContextInstantiationError> {
let stdio = PluginStdio::default();
let mut wasi_table = Table::new();
let wasi_ctx = WasiCtxBuilder::new()
.set_stdin(ReadPipe::from_shared(stdio.stdin.clone()))
.set_stdout(WritePipe::from_shared(stdio.stdout.clone()))
.set_stderr(WritePipe::from_shared(stdio.stderr.clone()))
.build(&mut wasi_table)?;
.stdout(stdio.stdout.clone())
.stderr(stdio.stderr.clone())
.build();
let client_ip = request
.extensions()
.get::<ForwardedIP>()
.map(|forwarded_ip| bulwark_host::IpInterface::from(forwarded_ip.0));

Ok(RequestContext {
wasi_ctx,
wasi_table,
wasi_table: Table::new(),
read_only_ctx: ReadOnlyContext {
config: Arc::new(plugin.guest_config()?),
permissions: plugin.permissions(),
Expand Down Expand Up @@ -546,33 +544,50 @@ impl HostMutableContext {
}
}

/// Allows the host to capture plugin standard IO and record it to the log.
#[derive(Clone)]
struct BufStdoutStream(MemoryOutputPipe);

impl BufStdoutStream {
pub fn contents(&self) -> bytes::Bytes {
self.0.contents()
}

pub(crate) fn writer(&self) -> impl HostOutputStream {
self.0.clone()
}
}

impl Default for BufStdoutStream {
fn default() -> Self {
Self(MemoryOutputPipe::new(usize::MAX))
}
}

impl StdoutStream for BufStdoutStream {
fn stream(&self) -> Box<dyn HostOutputStream> {
Box::new(self.writer())
}

fn isatty(&self) -> bool {
false
}
}

/// Wraps buffers to capture plugin stdio.
#[derive(Clone, Default)]
pub struct PluginStdio {
stdin: Arc<RwLock<std::io::Cursor<Vec<u8>>>>,
stdout: Arc<RwLock<std::io::Cursor<Vec<u8>>>>,
stderr: Arc<RwLock<std::io::Cursor<Vec<u8>>>>,
stdout: BufStdoutStream,
stderr: BufStdoutStream,
}

impl PluginStdio {
pub fn into_inner(&self) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
(
self.stdin
.read()
.expect("poisoned mutex")
.clone()
.into_inner(),
self.stdout
.read()
.expect("poisoned mutex")
.clone()
.into_inner(),
self.stderr
.read()
.expect("poisoned mutex")
.clone()
.into_inner(),
)
pub fn stdout_buffer(&self) -> Vec<u8> {
self.stdout.contents().to_vec()
}

pub fn stderr_buffer(&self) -> Vec<u8> {
self.stderr.contents().to_vec()
}
}

Expand Down Expand Up @@ -616,7 +631,7 @@ impl PluginInstance {
// convert from normal request struct to wasm request interface
let mut linker: Linker<RequestContext> = Linker::new(&plugin.engine);

wasmtime_wasi::preview2::wasi::command::add_to_linker(&mut linker)?;
wasmtime_wasi::preview2::command::add_to_linker(&mut linker)?;

let mut store = Store::new(&plugin.engine, request_context);
bulwark_host::HostApi::add_to_linker(&mut linker, |ctx: &mut RequestContext| ctx)?;
Expand Down
60 changes: 14 additions & 46 deletions crates/wasm-sdk-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub fn bulwark_plugin(_: TokenStream, input: TokenStream) -> TokenStream {
} else {
return syn::Error::new(
raw_impl.self_ty.span(),
"`bulwark_plugin` requires an impl for the `Handlers` trait",
"`bulwark_plugin` requires an impl for the `Guest` trait",
)
.to_compile_error()
.into();
Expand Down Expand Up @@ -173,55 +173,23 @@ pub fn bulwark_plugin(_: TokenStream, input: TokenStream) -> TokenStream {
};

let output = quote! {
impl bulwark_wasm_sdk::handlers::Handlers for #struct_type {
mod handlers {
use super::#struct_type;

wit_bindgen::generate!({
world: "bulwark:plugin/handlers",
exports: {
world: #struct_type
}
});
}

use handlers::Guest as Handlers;
impl Handlers for #struct_type {
#init_handler
#(#new_items)*
#(#noop_handlers)*
}
const _: () = {
#[doc(hidden)]
#[export_name = "on-init"]
#[allow(non_snake_case)]
unsafe extern "C" fn __export_handlers_on_init() -> i32 {
handlers::call_on_init::<#struct_type>()
}
#[doc(hidden)]
#[export_name = "on-request"]
#[allow(non_snake_case)]
unsafe extern "C" fn __export_handlers_on_request() -> i32 {
handlers::call_on_request::<#struct_type>()
}
#[doc(hidden)]
#[export_name = "on-request-decision"]
#[allow(non_snake_case)]
unsafe extern "C" fn __export_handlers_on_request_decision() -> i32 {
handlers::call_on_request_decision::<#struct_type>()
}
#[doc(hidden)]
#[export_name = "on-response-decision"]
#[allow(non_snake_case)]
unsafe extern "C" fn __export_handlers_on_response_decision() -> i32 {
handlers::call_on_response_decision::<#struct_type>()
}
#[doc(hidden)]
#[export_name = "on-request-body-decision"]
#[allow(non_snake_case)]
unsafe extern "C" fn __export_handlers_on_request_body_decision() -> i32 {
handlers::call_on_request_body_decision::<#struct_type>()
}
#[doc(hidden)]
#[export_name = "on-response-body-decision"]
#[allow(non_snake_case)]
unsafe extern "C" fn __export_handlers_on_response_body_decision() -> i32 {
handlers::call_on_response_body_decision::<#struct_type>()
}
#[doc(hidden)]
#[export_name = "on-decision-feedback"]
#[allow(non_snake_case)]
unsafe extern "C" fn __export_handlers_on_decision_feedback() -> i32 {
handlers::call_on_decision_feedback::<#struct_type>()
}
};
};

output.into()
Expand Down
1 change: 1 addition & 0 deletions crates/wasm-sdk/examples/blank-slate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ maintenance = { status = "experimental" }

[dependencies]
bulwark-wasm-sdk = { path = "../..", version = "0.3.0" }
wit-bindgen = "0.14.0"

[lib]
crate-type = ["cdylib"]
Expand Down
1 change: 1 addition & 0 deletions crates/wasm-sdk/examples/blank-slate/wit/plugin.wit
1 change: 1 addition & 0 deletions crates/wasm-sdk/examples/evil-bit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ maintenance = { status = "experimental" }

[dependencies]
bulwark-wasm-sdk = { path = "../..", version = "0.3.0" }
wit-bindgen = "0.14.0"

[lib]
crate-type = ["cdylib"]
Expand Down
1 change: 1 addition & 0 deletions crates/wasm-sdk/examples/evil-bit/wit/plugin.wit
6 changes: 2 additions & 4 deletions crates/wasm-sdk/src/host_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ pub fn set_restricted(value: f64) {
#[inline]
pub fn set_tags<I: IntoIterator<Item = V>, V: Into<String>>(tags: I) {
let tags: Vec<String> = tags.into_iter().map(|s| s.into()).collect();
let tags: Vec<&str> = tags.iter().map(|s| s.as_str()).collect();
crate::bulwark_host::set_tags(tags.as_slice())
}

Expand All @@ -352,7 +351,6 @@ pub fn set_tags<I: IntoIterator<Item = V>, V: Into<String>>(tags: I) {
#[inline]
pub fn append_tags<I: IntoIterator<Item = V>, V: Into<String>>(tags: I) -> Vec<String> {
let tags: Vec<String> = tags.into_iter().map(|s| s.into()).collect();
let tags: Vec<&str> = tags.iter().map(|s| s.as_str()).collect();
crate::bulwark_host::append_tags(tags.as_slice())
}

Expand Down Expand Up @@ -532,10 +530,10 @@ pub fn check_rate_limit(key: &str) -> Result<Rate, crate::RemoteStateError> {
///
/// # Examples
///
/// ```no_run
/// ```ignore
/// use bulwark_wasm_sdk::*;
///
/// pub struct CircuitBreaker;
/// struct CircuitBreaker;
///
/// #[bulwark_plugin]
/// impl Handlers for CircuitBreaker {
Expand Down
15 changes: 3 additions & 12 deletions crates/wasm-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,9 @@ pub mod bulwark_host {
world: "bulwark:plugin/host-api"
});
}
#[allow(unused_macros)]
#[doc(hidden)]
pub mod handlers {
wit_bindgen::generate!({
world: "bulwark:plugin/handlers"
});
}

// Due to https://github.com/bytecodealliance/wit-bindgen/issues/674 we don't call `generate!` for
// the handlers and instead define the trait manually and do the bindings through our own macro.

mod errors;
mod from;
Expand All @@ -25,9 +21,4 @@ mod host_api;
pub use bulwark_decision::*;
pub use errors::*;
pub use from::*;
/// The handler functions a plugin needs to expose to process requests and generate decisions.
///
/// See the [`bulwark_plugin`](https://docs.rs/bulwark-wasm-sdk/latest/bulwark_wasm_sdk/attr.bulwark_plugin.html)
/// attribute for additional details on how to use this trait.
pub use handlers::Handlers;
pub use host_api::*;
15 changes: 15 additions & 0 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ fn install_wasm32_wasi_target() -> Result<(), BuildError> {
Ok(())
}

/// Replace the plugin's vendored plugin.wit file with the one embedded in this binary.
/// This ensures that the plugin.wit file is always in sync with the version of the CLI that's
/// performing the build.
///
/// Needed to make wit-bindgen work.
fn replace_plugin_wit_files(root_path: impl AsRef<Path>) -> Result<(), BuildError> {
let plugin_wit = include_bytes!("../wit/plugin.wit");
let plugin_wit_dest_path = root_path.as_ref().join(Path::new("wit/plugin.wit"));
std::fs::create_dir_all(root_path.as_ref().join(Path::new("wit/")))?;
std::fs::write(plugin_wit_dest_path, plugin_wit)?;
Ok(())
}

/// Builds a plugin.
///
/// Compiles the plugin with the `wasm32-wasi` target, and installs it if it is missing.
Expand All @@ -87,6 +100,8 @@ pub(crate) fn build_plugin(
let output = output.as_ref();
let output_dir = output.parent().ok_or(BuildError::MissingParent)?;

replace_plugin_wit_files(path)?;

let installed_targets = installed_targets()?;
let wasi_installed = installed_targets.get("wasm32-wasi");
if !wasi_installed.unwrap_or(&false) {
Expand Down
Loading

0 comments on commit 0d6b2ab

Please sign in to comment.