From d8bc651bbad1409fb06db8b94cb019e3e72ff640 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 11 Nov 2025 17:01:53 +0000 Subject: [PATCH] Add a check for required binaries - We require bwrap in the target container currently, make that clear. That needs to be done in the entrypoint shell script. - Once we're past that we're in Rust, so add infrastructure to verify that we have systemctl and objcopy for the UKI extraction. Signed-off-by: Colin Walters --- Cargo.lock | 25 +++++++++++++++++++++++++ crates/kit/Cargo.toml | 1 + crates/kit/scripts/entrypoint.sh | 6 ++++++ crates/kit/src/run_ephemeral.rs | 26 ++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 2cc908e..6ee4150 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -194,6 +194,7 @@ dependencies = [ "tracing-subscriber", "uuid", "vsock", + "which", "xshell", "yaml-rust2", ] @@ -716,6 +717,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "equivalent" version = "1.0.2" @@ -2996,6 +3003,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" +dependencies = [ + "either", + "env_home", + "rustix", + "winsafe", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3361,6 +3380,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "winx" version = "0.36.4" diff --git a/crates/kit/Cargo.toml b/crates/kit/Cargo.toml index b4fa694..2373104 100644 --- a/crates/kit/Cargo.toml +++ b/crates/kit/Cargo.toml @@ -49,6 +49,7 @@ strum = { version = "0.26", features = ["derive"] } quick-xml = "0.36" oci-spec = "0.8.2" sha2 = "0.10" +which = "7.0" [dev-dependencies] similar-asserts = "1.5" diff --git a/crates/kit/scripts/entrypoint.sh b/crates/kit/scripts/entrypoint.sh index a277811..4626f4e 100644 --- a/crates/kit/scripts/entrypoint.sh +++ b/crates/kit/scripts/entrypoint.sh @@ -3,6 +3,12 @@ set -euo pipefail SELFEXE=/run/selfexe +# Check for required binaries early +if ! command -v bwrap &>/dev/null; then + echo "Error: bwrap (bubblewrap) is currently required in the target container image" >&2 + exit 1 +fi + # Shell script library init_tmproot() { if test -d /run/tmproot; then return 0; fi diff --git a/crates/kit/src/run_ephemeral.rs b/crates/kit/src/run_ephemeral.rs index 4e01fbc..28576c5 100644 --- a/crates/kit/src/run_ephemeral.rs +++ b/crates/kit/src/run_ephemeral.rs @@ -700,6 +700,29 @@ fn parse_service_exit_code(status_content: &str) -> Result { Ok(0) } +/// Check for required binaries in the target container image +/// +/// These binaries must be present in the container image being run as an ephemeral VM. +fn check_required_container_binaries() -> Result<()> { + // We use systemctl in a few places. objcopy is for UKI extraction. + let required_binaries = ["systemctl", "objcopy"]; + + let mut missing = Vec::new(); + + for binary in &required_binaries { + if which::which(binary).is_err() { + missing.push(format!("Missing required executable: {}", binary)); + } + } + + if !missing.is_empty() { + return Err(eyre!("{}", missing.join("\n"))); + } + + debug!("All required container binaries found"); + Ok(()) +} + /// VM execution inside container: extracts kernel/initramfs, starts virtiofsd processes, /// generates systemd mount units, sets up command execution, launches QEMU. pub(crate) async fn run_impl(opts: RunEphemeralOpts) -> Result<()> { @@ -708,6 +731,9 @@ pub(crate) async fn run_impl(opts: RunEphemeralOpts) -> Result<()> { debug!("Running QEMU implementation inside container"); + // Check for required binaries in the target container image early + check_required_container_binaries()?; + // Initialize status writer for supervisor monitoring let status_writer = StatusWriter::new("/run/supervisor-status.json"); status_writer.update_state(SupervisorState::WaitingForSystemd)?;