diff --git a/src/agent/src/main.rs b/src/agent/src/main.rs index 0d88851524..2c69f2f4c9 100644 --- a/src/agent/src/main.rs +++ b/src/agent/src/main.rs @@ -109,7 +109,7 @@ fn main() -> Result<()> { if unistd::getpid() == Pid::from_raw(1) { // Init a temporary logger used by init agent as init process // since before do the base mount, it wouldn't access "/proc/cmdline" - // to get the customzied debug level. + // to get the customzied debug level. let writer = io::stdout(); let logger = logging::create_logger(NAME, "agent", slog::Level::Debug, writer); init_agent_as_init(&logger)?; diff --git a/src/agent/src/namespace.rs b/src/agent/src/namespace.rs index f08b59933e..81db13fd11 100644 --- a/src/agent/src/namespace.rs +++ b/src/agent/src/namespace.rs @@ -6,37 +6,22 @@ use nix::mount::MsFlags; use nix::sched::{unshare, CloneFlags}; use nix::unistd::{getpid, gettid}; -use std::collections::HashMap; +use std::fmt; use std::fs; use std::fs::File; use std::os::unix::io::AsRawFd; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::thread; use crate::mount::{BareMount, FLAGS}; use slog::Logger; //use container::Process; - const PERSISTENT_NS_DIR: &'static str = "/var/run/sandbox-ns"; pub const NSTYPEIPC: &'static str = "ipc"; pub const NSTYPEUTS: &'static str = "uts"; pub const NSTYPEPID: &'static str = "pid"; -lazy_static! { - static ref CLONE_FLAG_TABLE: HashMap<&'static str, CloneFlags> = { - let mut m = HashMap::new(); - m.insert(NSTYPEIPC, CloneFlags::CLONE_NEWIPC); - m.insert(NSTYPEUTS, CloneFlags::CLONE_NEWUTS); - m - }; -} - -#[derive(Debug, Default)] -pub struct Namespace { - pub path: String, -} - pub fn get_current_thread_ns_path(ns_type: &str) -> String { format!( "/proc/{}/task/{}/ns/{}", @@ -46,74 +31,203 @@ pub fn get_current_thread_ns_path(ns_type: &str) -> String { ) } -// setup_persistent_ns creates persistent namespace without switchin to it. -// Note, pid namespaces cannot be persisted. -pub fn setup_persistent_ns(logger: Logger, ns_type: &'static str) -> Result { - if let Err(err) = fs::create_dir_all(PERSISTENT_NS_DIR) { - return Err(err.to_string()); +#[derive(Debug)] +pub struct Namespace { + logger: Logger, + pub path: String, + persistent_ns_dir: String, + ns_type: NamespaceType, +} + +impl Namespace { + pub fn new(logger: &Logger) -> Self { + Namespace { + logger: logger.clone(), + path: String::from(""), + persistent_ns_dir: String::from(PERSISTENT_NS_DIR), + ns_type: NamespaceType::IPC, + } } - let ns_path = Path::new(PERSISTENT_NS_DIR); - let new_ns_path = ns_path.join(ns_type); - - if let Err(err) = File::create(new_ns_path.as_path()) { - return Err(err.to_string()); + pub fn as_ipc(mut self) -> Self { + self.ns_type = NamespaceType::IPC; + self } - let new_thread = thread::spawn(move || { - let origin_ns_path = get_current_thread_ns_path(ns_type); - let _origin_ns_fd = match File::open(Path::new(&origin_ns_path)) { - Err(err) => return Err(err.to_string()), - Ok(file) => file.as_raw_fd(), - }; + pub fn as_uts(mut self) -> Self { + self.ns_type = NamespaceType::UTS; + self + } - // Create a new netns on the current thread. - let cf = match CLONE_FLAG_TABLE.get(ns_type) { - None => return Err(format!("Failed to get ns type {}", ns_type).to_string()), - Some(cf) => cf, - }; + pub fn set_root_dir(mut self, dir: &str) -> Self { + self.persistent_ns_dir = dir.to_string(); + self + } - if let Err(err) = unshare(*cf) { + // setup_persistent_ns creates persistent namespace without switchin to it. + // Note, pid namespaces cannot be persisted. + pub fn setup(mut self) -> Result { + if let Err(err) = fs::create_dir_all(&self.persistent_ns_dir) { return Err(err.to_string()); } - // Bind mount the new namespace from the current thread onto the mount point to persist it. - let source: &str = origin_ns_path.as_str(); - let destination: &str = new_ns_path.as_path().to_str().unwrap_or("none"); + let ns_path = PathBuf::from(&self.persistent_ns_dir); + let ns_type = self.ns_type.clone(); + let logger = self.logger.clone(); - let _recursive = true; - let _readonly = true; - let mut flags = MsFlags::empty(); + let new_ns_path = ns_path.join(&ns_type.get()); - match FLAGS.get("rbind") { - Some(x) => { - let (_, f) = *x; - flags = flags | f; - } - None => (), - }; - - let bare_mount = BareMount::new(source, destination, "none", flags, "", &logger); - - if let Err(err) = bare_mount.mount() { - return Err(format!( - "Failed to mount {} to {} with err:{:?}", - source, destination, err - )); + if let Err(err) = File::create(new_ns_path.as_path()) { + return Err(err.to_string()); } - Ok(()) - }); - match new_thread.join() { - Ok(t) => match t { - Err(err) => return Err(err), - Ok(()) => (), - }, - Err(err) => return Err(format!("Failed to join thread {:?}!", err)), + self.path = new_ns_path.into_os_string().into_string().unwrap(); + + let new_thread = thread::spawn(move || { + let ns_path = ns_path.clone(); + let ns_type = ns_type.clone(); + let logger = logger; + let new_ns_path = ns_path.join(&ns_type.get()); + + let origin_ns_path = get_current_thread_ns_path(&ns_type.get()); + + let _origin_ns_fd = match File::open(Path::new(&origin_ns_path)) { + Err(err) => return Err(err.to_string()), + Ok(file) => file.as_raw_fd(), + }; + + // Create a new netns on the current thread. + let cf = ns_type.get_flags().clone(); + + if let Err(err) = unshare(cf) { + return Err(err.to_string()); + } + + // Bind mount the new namespace from the current thread onto the mount point to persist it. + let source: &str = origin_ns_path.as_str(); + let destination: &str = new_ns_path.as_path().to_str().unwrap_or("none"); + + let _recursive = true; + let _readonly = true; + let mut flags = MsFlags::empty(); + + match FLAGS.get("rbind") { + Some(x) => { + let (_, f) = *x; + flags = flags | f; + } + None => (), + }; + + let bare_mount = BareMount::new(source, destination, "none", flags, "", &logger); + + if let Err(err) = bare_mount.mount() { + return Err(format!( + "Failed to mount {} to {} with err:{:?}", + source, destination, err + )); + } + Ok(()) + }); + + match new_thread.join() { + Ok(t) => match t { + Err(err) => return Err(err), + Ok(()) => (), + }, + Err(err) => return Err(format!("Failed to join thread {:?}!", err)), + } + + Ok(self) + } +} + +/// Represents the Namespace type. +#[derive(Clone, Copy)] +enum NamespaceType { + IPC, + UTS, + PID, +} + +impl NamespaceType { + /// Get the string representation of the namespace type. + pub fn get(&self) -> String { + match *self { + Self::IPC => String::from("ipc"), + Self::UTS => String::from("uts"), + Self::PID => String::from("pid"), + } } - let new_ns_path = ns_path.join(ns_type); - Ok(Namespace { - path: new_ns_path.into_os_string().into_string().unwrap(), - }) + /// Get the associate flags with the namespace type. + pub fn get_flags(&self) -> CloneFlags { + match *self { + Self::IPC => CloneFlags::CLONE_NEWIPC, + Self::UTS => CloneFlags::CLONE_NEWUTS, + Self::PID => CloneFlags::CLONE_NEWPID, + } + } +} + +impl fmt::Debug for NamespaceType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.get()) + } +} + +impl Default for NamespaceType { + fn default() -> Self { + NamespaceType::IPC + } +} + +#[cfg(test)] +mod tests { + use super::{Namespace, NamespaceType}; + use crate::{mount::remove_mounts, skip_if_not_root}; + use nix::sched::CloneFlags; + use tempfile::Builder; + + #[test] + fn test_setup_persistent_ns() { + skip_if_not_root!(); + // Create dummy logger and temp folder. + let logger = slog::Logger::root(slog::Discard, o!()); + let tmpdir = Builder::new().prefix("ipc").tempdir().unwrap(); + + let ns_ipc = Namespace::new(&logger) + .as_ipc() + .set_root_dir(tmpdir.path().to_str().unwrap()) + .setup(); + + assert!(ns_ipc.is_ok()); + assert!(remove_mounts(&vec![ns_ipc.unwrap().path]).is_ok()); + + let logger = slog::Logger::root(slog::Discard, o!()); + let tmpdir = Builder::new().prefix("ipc").tempdir().unwrap(); + + let ns_uts = Namespace::new(&logger) + .as_uts() + .set_root_dir(tmpdir.path().to_str().unwrap()) + .setup(); + + assert!(ns_uts.is_ok()); + assert!(remove_mounts(&vec![ns_uts.unwrap().path]).is_ok()); + } + + #[test] + fn test_namespace_type() { + let ipc = NamespaceType::IPC; + assert_eq!("ipc", ipc.get()); + assert_eq!(CloneFlags::CLONE_NEWIPC, ipc.get_flags()); + + let uts = NamespaceType::UTS; + assert_eq!("uts", uts.get()); + assert_eq!(CloneFlags::CLONE_NEWUTS, uts.get_flags()); + + let pid = NamespaceType::PID; + assert_eq!("pid", pid.get()); + assert_eq!(CloneFlags::CLONE_NEWPID, pid.get_flags()); + } } diff --git a/src/agent/src/sandbox.rs b/src/agent/src/sandbox.rs index 40b8da0037..a60aecc356 100644 --- a/src/agent/src/sandbox.rs +++ b/src/agent/src/sandbox.rs @@ -5,7 +5,7 @@ //use crate::container::Container; use crate::mount::{get_mount_fs_type, remove_mounts, TYPEROOTFS}; -use crate::namespace::{setup_persistent_ns, Namespace, NSTYPEIPC, NSTYPEUTS}; +use crate::namespace::Namespace; use crate::netlink::{RtnlHandle, NETLINK_ROUTE}; use crate::network::Network; use libc::pid_t; @@ -48,7 +48,7 @@ impl Sandbox { let logger = logger.new(o!("subsystem" => "sandbox")); Ok(Sandbox { - logger: logger, + logger: logger.clone(), id: "".to_string(), hostname: "".to_string(), network: Network::new(), @@ -56,12 +56,8 @@ impl Sandbox { mounts: Vec::new(), container_mounts: HashMap::new(), pci_device_map: HashMap::new(), - shared_utsns: Namespace { - path: "".to_string(), - }, - shared_ipcns: Namespace { - path: "".to_string(), - }, + shared_utsns: Namespace::new(&logger), + shared_ipcns: Namespace::new(&logger), storages: HashMap::new(), running: false, no_pivot_root: fs_type.eq(TYPEROOTFS), @@ -129,29 +125,28 @@ impl Sandbox { pub fn setup_shared_namespaces(&mut self) -> Result { // Set up shared IPC namespace - self.shared_ipcns = match setup_persistent_ns(self.logger.clone(), NSTYPEIPC) { + self.shared_ipcns = match Namespace::new(&self.logger).as_ipc().setup() { Ok(ns) => ns, Err(err) => { return Err(ErrorKind::ErrorCode(format!( - "Failed to setup persisten IPC namespace with error: {}", - &err + "Failed to setup persistent IPC namespace with error: {}", + err )) .into()) } }; - // Set up shared UTS namespace - self.shared_utsns = match setup_persistent_ns(self.logger.clone(), NSTYPEUTS) { + // // Set up shared UTS namespace + self.shared_utsns = match Namespace::new(&self.logger).as_uts().setup() { Ok(ns) => ns, Err(err) => { return Err(ErrorKind::ErrorCode(format!( - "Failed to setup persisten UTS namespace with error: {} ", - &err + "Failed to setup persistent UTS namespace with error: {}", + err )) .into()) } }; - Ok(true) }