Merge pull request #563 from bergwolf/guest-hooks

agent: support guest hooks
This commit is contained in:
Bin Liu 2020-08-26 17:36:32 +08:00 committed by GitHub
commit b4c105336e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 337 additions and 16 deletions

79
src/agent/Cargo.lock generated
View File

@ -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",

View File

@ -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);

View File

@ -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<PathBuf> {
// Setup container bundle under CONTAINER_BASE, which is cleaned up
// before removing a container.
// - bundle path is /<CONTAINER_BASE>/<cid>/
// - config.json at /<CONTAINER_BASE>/<cid>/config.json
// - container rootfs bind mounted at /<CONTAINER_BASE>/<cid>/rootfs
// - modify container spec root to point to /<CONTAINER_BASE>/<cid>/rootfs
fn setup_bundle(cid: &str, spec: &mut Spec) -> Result<PathBuf> {
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);
}
}

View File

@ -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<Sender<i32>>,
pub rtnl: Option<RtnlHandle>,
pub hooks: Option<Hooks>,
}
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<Vec<Hook>> {
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<i32> {
@ -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());
}
}

View File

@ -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",