diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index 590e1e71e5..a81f4f3ab5 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -30,6 +30,18 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + [[package]] name = "autocfg" version = "1.0.0" @@ -49,12 +61,29 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + [[package]] name = "byteorder" version = "1.3.4" @@ -95,6 +124,12 @@ dependencies = [ "time", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "crc32fast" version = "1.2.0" @@ -125,6 +160,26 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "dirs" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "errno" version = "0.2.5" @@ -531,6 +586,17 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + [[package]] name = "regex" version = "1.3.7" @@ -564,6 +630,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rustc-demangle" version = "0.1.16" @@ -575,6 +653,7 @@ name = "rustjail" version = "0.1.0" dependencies = [ "caps", + "dirs", "error-chain", "lazy_static", "libc", diff --git a/src/agent/rustjail/src/container.rs b/src/agent/rustjail/src/container.rs index 24299a2c42..7607a32b61 100644 --- a/src/agent/rustjail/src/container.rs +++ b/src/agent/rustjail/src/container.rs @@ -38,6 +38,7 @@ use protocols::agent::StatsContainerResponse; use nix::errno::Errno; use nix::fcntl::{self, OFlag}; use nix::fcntl::{FcntlArg, FdFlag}; +use nix::mount::MntFlags; use nix::pty; use nix::sched::{self, CloneFlags}; use nix::sys::signal::{self, Signal}; @@ -972,6 +973,10 @@ impl BaseContainer for LinuxContainer { } self.status.transition(Status::STOPPED); + nix::mount::umount2( + spec.root.as_ref().unwrap().path.as_str(), + MntFlags::MNT_DETACH, + )?; fs::remove_dir_all(&self.root)?; Ok(()) } @@ -1510,12 +1515,16 @@ fn execute_hook(logger: &Logger, h: &Hook, st: &OCIState) -> Result<()> { }); match unistd::fork()? { - ForkResult::Parent { child: _ch } => { + ForkResult::Parent { child } => { let buf = read_sync(rfd)?; - let buf_array: [u8; 4] = [buf[0], buf[1], buf[2], buf[3]]; - let status: i32 = i32::from_be_bytes(buf_array); + let status = if buf.len() == 4 { + let buf_array: [u8; 4] = [buf[0], buf[1], buf[2], buf[3]]; + i32::from_be_bytes(buf_array) + } else { + -libc::EPIPE + }; - info!(logger, "hook child: {}", _ch); + info!(logger, "hook child: {} status: {}", child, status); // let _ = wait::waitpid(_ch, // Some(WaitPidFlag::WEXITED | WaitPidFlag::__WALL)); @@ -1644,7 +1653,11 @@ fn execute_hook(logger: &Logger, h: &Hook, st: &OCIState) -> Result<()> { }; handle.join().unwrap(); - let _ = write_sync(wfd, status, ""); + let _ = write_sync( + wfd, + SYNC_DATA, + std::str::from_utf8(&status.to_be_bytes()).unwrap_or_default(), + ); // let _ = wait::waitpid(Pid::from_raw(pid), // Some(WaitPidFlag::WEXITED | WaitPidFlag::__WALL)); std::process::exit(0); diff --git a/src/agent/src/rpc.rs b/src/agent/src/rpc.rs index 5e704be158..5f09906942 100644 --- a/src/agent/src/rpc.rs +++ b/src/agent/src/rpc.rs @@ -3,10 +3,11 @@ // SPDX-License-Identifier: Apache-2.0 // +use std::path::Path; use std::sync::{Arc, Mutex}; use ttrpc; -use oci::{LinuxNamespace, Spec}; +use oci::{LinuxNamespace, Root, Spec}; use protobuf::{RepeatedField, SingularPtrField}; use protocols::agent::{ AgentDetails, CopyFileRequest, GuestDetailsResponse, Interfaces, ListProcessesResponse, @@ -25,6 +26,7 @@ use rustjail::process::Process; use rustjail::specconv::CreateOpts; use nix::errno::Errno; +use nix::mount::MsFlags; use nix::sys::signal::Signal; use nix::sys::stat; use nix::unistd::{self, Pid}; @@ -33,7 +35,7 @@ use rustjail::process::ProcessOperations; use crate::device::{add_devices, rescan_pci_bus, update_device_cgroup}; use crate::linux_abi::*; use crate::metrics::get_metrics; -use crate::mount::{add_storages, remove_mounts, STORAGEHANDLERLIST}; +use crate::mount::{add_storages, remove_mounts, BareMount, STORAGEHANDLERLIST}; use crate::namespace::{NSTYPEIPC, NSTYPEPID, NSTYPEUTS}; use crate::random; use crate::sandbox::Sandbox; @@ -127,9 +129,12 @@ impl agentService { // Add the root partition to the device cgroup to prevent access update_device_cgroup(&mut oci)?; + // Append guest hooks + append_guest_hooks(&s, &mut oci); + // write spec to bundle path, hooks might // read ocispec - let olddir = setup_bundle(&oci)?; + let olddir = setup_bundle(&cid, &mut oci)?; // restore the cwd for kata-agent process. defer!(unistd::chdir(&olddir).unwrap()); @@ -1082,6 +1087,15 @@ impl protocols::agent_ttrpc::AgentService for agentService { s.hostname = req.hostname.clone(); s.running = true; + if !req.guest_hook_path.is_empty() { + if let Err(e) = s.add_hooks(&req.guest_hook_path) { + error!( + sl!(), + "add guest hook {} failed: {:?}", req.guest_hook_path, e + ); + } + } + if req.sandbox_id.len() > 0 { s.id = req.sandbox_id.clone(); } @@ -1521,6 +1535,18 @@ fn update_container_namespaces( Ok(()) } +fn append_guest_hooks(s: &Sandbox, oci: &mut Spec) { + if s.hooks.is_none() { + return; + } + let guest_hooks = s.hooks.as_ref().unwrap(); + let mut hooks = oci.hooks.take().unwrap_or_default(); + hooks.prestart.append(&mut guest_hooks.prestart.clone()); + hooks.poststart.append(&mut guest_hooks.poststart.clone()); + hooks.poststop.append(&mut guest_hooks.poststop.clone()); + oci.hooks = Some(hooks); +} + // Check is the container process installed the // handler for specific signal. fn is_signal_handled(pid: pid_t, signum: u32) -> bool { @@ -1644,26 +1670,46 @@ fn do_copy_file(req: &CopyFileRequest) -> Result<()> { Ok(()) } -fn setup_bundle(spec: &Spec) -> Result { +// Setup container bundle under CONTAINER_BASE, which is cleaned up +// before removing a container. +// - bundle path is /// +// - config.json at ///config.json +// - container rootfs bind mounted at ///rootfs +// - modify container spec root to point to ///rootfs +fn setup_bundle(cid: &str, spec: &mut Spec) -> Result { if spec.root.is_none() { return Err(nix::Error::Sys(Errno::EINVAL).into()); } - let root = spec.root.as_ref().unwrap().path.as_str(); + let spec_root = spec.root.as_ref().unwrap(); - let rootfs = fs::canonicalize(root)?; - let bundle_path = rootfs.parent().unwrap().to_str().unwrap(); + let bundle_path = Path::new(CONTAINER_BASE).join(cid); + let config_path = bundle_path.clone().join("config.json"); + let rootfs_path = bundle_path.clone().join("rootfs"); - let config = format!("{}/{}", bundle_path, "config.json"); + fs::create_dir_all(&rootfs_path)?; + BareMount::new( + &spec_root.path, + rootfs_path.to_str().unwrap(), + "bind", + MsFlags::MS_BIND, + "", + &sl!(), + ) + .mount()?; + spec.root = Some(Root { + path: rootfs_path.to_str().unwrap().to_owned(), + readonly: spec_root.readonly, + }); info!( sl!(), "{:?}", spec.process.as_ref().unwrap().console_size.as_ref() ); - let _ = spec.save(config.as_str()); + let _ = spec.save(config_path.to_str().unwrap()); let olddir = unistd::getcwd().chain_err(|| "cannot getcwd")?; - unistd::chdir(bundle_path)?; + unistd::chdir(bundle_path.to_str().unwrap())?; Ok(olddir) } @@ -1713,6 +1759,7 @@ fn load_kernel_module(module: &protocols::agent::KernelModule) -> Result<()> { #[cfg(test)] mod tests { use super::*; + use oci::{Hook, Hooks}; #[test] fn test_load_kernel_module() { @@ -1734,4 +1781,22 @@ mod tests { let result = load_kernel_module(&m); assert!(result.is_ok(), "load module should success"); } + + #[test] + fn test_append_guest_hooks() { + let logger = slog::Logger::root(slog::Discard, o!()); + let mut s = Sandbox::new(&logger).unwrap(); + s.hooks = Some(Hooks { + prestart: vec![Hook { + path: "foo".to_string(), + ..Default::default() + }], + ..Default::default() + }); + let mut oci = Spec { + ..Default::default() + }; + append_guest_hooks(&s, &mut oci); + assert_eq!(s.hooks, oci.hooks); + } } diff --git a/src/agent/src/sandbox.rs b/src/agent/src/sandbox.rs index 5d7a993042..a80b0d9198 100644 --- a/src/agent/src/sandbox.rs +++ b/src/agent/src/sandbox.rs @@ -11,7 +11,7 @@ use crate::namespace::NSTYPEPID; use crate::network::Network; use libc::pid_t; use netlink::{RtnlHandle, NETLINK_ROUTE}; -use oci::LinuxNamespace; +use oci::{Hook, Hooks}; use protocols::agent::OnlineCPUMemRequest; use regex::Regex; use rustjail::cgroups; @@ -22,6 +22,8 @@ use rustjail::process::Process; use slog::Logger; use std::collections::HashMap; use std::fs; +use std::os::unix::fs::PermissionsExt; +use std::path::Path; use std::sync::mpsc::Sender; #[derive(Debug)] @@ -42,6 +44,7 @@ pub struct Sandbox { pub no_pivot_root: bool, pub sender: Option>, pub rtnl: Option, + pub hooks: Option, } impl Sandbox { @@ -66,6 +69,7 @@ impl Sandbox { no_pivot_root: fs_type.eq(TYPEROOTFS), sender: None, rtnl: Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()), + hooks: None, }) } @@ -261,6 +265,57 @@ impl Sandbox { Ok(()) } + + pub fn add_hooks(&mut self, dir: &str) -> Result<()> { + let mut hooks = Hooks::default(); + if let Ok(hook) = self.find_hooks(dir, "prestart") { + hooks.prestart = hook; + } + if let Ok(hook) = self.find_hooks(dir, "poststart") { + hooks.poststart = hook; + } + if let Ok(hook) = self.find_hooks(dir, "poststop") { + hooks.poststop = hook; + } + self.hooks = Some(hooks); + Ok(()) + } + + fn find_hooks(&self, hook_path: &str, hook_type: &str) -> Result> { + let mut hooks = Vec::new(); + for entry in fs::read_dir(Path::new(hook_path).join(hook_type))? { + let entry = entry?; + // Reject non-file, symlinks and non-executable files + if !entry.file_type()?.is_file() + || entry.file_type()?.is_symlink() + || entry.metadata()?.permissions().mode() & 0o777 & 0o111 == 0 + { + continue; + } + + let name = entry.file_name(); + let hook = Hook { + path: Path::new(hook_path) + .join(hook_type) + .join(&name) + .to_str() + .unwrap() + .to_owned(), + args: vec![name.to_str().unwrap().to_owned(), hook_type.to_owned()], + ..Default::default() + }; + info!( + self.logger, + "found {} hook {:?} mode {:o}", + hook_type, + hook, + entry.metadata()?.permissions().mode() + ); + hooks.push(hook); + } + + Ok(hooks) + } } fn online_resources(logger: &Logger, path: &str, pattern: &str, num: i32) -> Result { @@ -315,6 +370,8 @@ mod tests { use rustjail::container::LinuxContainer; use rustjail::specconv::CreateOpts; use slog::Logger; + use std::fs::{self, File}; + use std::os::unix::fs::PermissionsExt; use tempfile::Builder; fn bind_mount(src: &str, dst: &str, logger: &Logger) -> Result<(), rustjail::errors::Error> { @@ -596,4 +653,26 @@ mod tests { let ns_path = format!("/proc/{}/ns/pid", test_pid); assert_eq!(s.sandbox_pidns.unwrap().path, ns_path); } + #[test] + fn add_guest_hooks() { + let logger = slog::Logger::root(slog::Discard, o!()); + let mut s = Sandbox::new(&logger).unwrap(); + let tmpdir = Builder::new().tempdir().unwrap(); + let tmpdir_path = tmpdir.path().to_str().unwrap(); + + assert!(fs::create_dir_all(tmpdir.path().join("prestart")).is_ok()); + assert!(fs::create_dir_all(tmpdir.path().join("poststop")).is_ok()); + + let file = File::create(tmpdir.path().join("prestart").join("prestart.sh")).unwrap(); + let mut perm = file.metadata().unwrap().permissions(); + perm.set_mode(0o777); + assert!(file.set_permissions(perm).is_ok()); + assert!(File::create(tmpdir.path().join("poststop").join("poststop.sh")).is_ok()); + + assert!(s.add_hooks(tmpdir_path).is_ok()); + assert!(s.hooks.is_some()); + assert!(s.hooks.as_ref().unwrap().prestart.len() == 1); + assert!(s.hooks.as_ref().unwrap().poststart.is_empty()); + assert!(s.hooks.as_ref().unwrap().poststop.is_empty()); + } } diff --git a/tools/agent-ctl/Cargo.lock b/tools/agent-ctl/Cargo.lock index 8cbc0b9b22..276379c065 100644 --- a/tools/agent-ctl/Cargo.lock +++ b/tools/agent-ctl/Cargo.lock @@ -45,6 +45,18 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + [[package]] name = "atty" version = "0.2.14" @@ -76,12 +88,29 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + [[package]] name = "byteorder" version = "1.3.4" @@ -137,6 +166,12 @@ dependencies = [ "vec_map", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "crossbeam-channel" version = "0.4.2" @@ -158,6 +193,26 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "dirs" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "errno" version = "0.2.5" @@ -504,6 +559,23 @@ dependencies = [ "rand_core", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + [[package]] name = "regex" version = "1.3.9" @@ -522,6 +594,18 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rustc-demangle" version = "0.1.16" @@ -533,6 +617,7 @@ name = "rustjail" version = "0.1.0" dependencies = [ "caps", + "dirs", "error-chain", "lazy_static", "libc",