agent: sandbox shared pid namespace support

Add support shareProcessNamespace.
BTW, this commit only support shared pid namespace by
sharing the infrastructure pause container's pid namespace
with other containers, instead of creating a new pid
namespace different from pause container.

Fixes: #342

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
This commit is contained in:
fupan.lfp 2020-07-13 15:45:52 +08:00
parent afcf269c9b
commit c6e4d092d6
3 changed files with 68 additions and 14 deletions

View File

@ -65,6 +65,11 @@ impl Namespace {
self self
} }
pub fn as_pid(mut self) -> Self {
self.ns_type = NamespaceType::PID;
self
}
pub fn set_root_dir(mut self, dir: &str) -> Self { pub fn set_root_dir(mut self, dir: &str) -> Self {
self.persistent_ns_dir = dir.to_string(); self.persistent_ns_dir = dir.to_string();
self self

View File

@ -79,6 +79,7 @@ impl agentService {
let cid = req.container_id.clone(); let cid = req.container_id.clone();
let mut oci_spec = req.OCI.clone(); let mut oci_spec = req.OCI.clone();
let use_sandbox_pidns = req.get_sandbox_pidns();
let sandbox; let sandbox;
let mut s; let mut s;
@ -121,7 +122,7 @@ impl agentService {
s.container_mounts.insert(cid.clone(), m); s.container_mounts.insert(cid.clone(), m);
} }
update_container_namespaces(&s, &mut oci)?; update_container_namespaces(&s, &mut oci, use_sandbox_pidns)?;
// Add the root partition to the device cgroup to prevent access // Add the root partition to the device cgroup to prevent access
update_device_cgroup(&mut oci)?; update_device_cgroup(&mut oci)?;
@ -162,6 +163,7 @@ impl agentService {
ctr.start(p)?; ctr.start(p)?;
s.update_shared_pidns(&ctr)?;
s.add_container(ctr); s.add_container(ctr);
info!(sl!(), "created container!"); info!(sl!(), "created container!");
@ -1478,7 +1480,11 @@ pub fn start<S: Into<String>>(s: Arc<Mutex<Sandbox>>, host: S, port: u16) -> ttr
// path set by the spec, since we will always ignore it. Indeed, it makes no // path set by the spec, since we will always ignore it. Indeed, it makes no
// sense to rely on the namespace path provided by the host since namespaces // sense to rely on the namespace path provided by the host since namespaces
// are different inside the guest. // are different inside the guest.
fn update_container_namespaces(sandbox: &Sandbox, spec: &mut Spec) -> Result<()> { fn update_container_namespaces(
sandbox: &Sandbox,
spec: &mut Spec,
sandbox_pidns: bool,
) -> Result<()> {
let linux = match spec.linux.as_mut() { let linux = match spec.linux.as_mut() {
None => { None => {
return Err( return Err(
@ -1488,14 +1494,8 @@ fn update_container_namespaces(sandbox: &Sandbox, spec: &mut Spec) -> Result<()>
Some(l) => l, Some(l) => l,
}; };
let mut pidNs = false;
let namespaces = linux.namespaces.as_mut_slice(); let namespaces = linux.namespaces.as_mut_slice();
for namespace in namespaces.iter_mut() { for namespace in namespaces.iter_mut() {
if namespace.r#type == NSTYPEPID {
pidNs = true;
continue;
}
if namespace.r#type == NSTYPEIPC { if namespace.r#type == NSTYPEIPC {
namespace.path = sandbox.shared_ipcns.path.clone(); namespace.path = sandbox.shared_ipcns.path.clone();
continue; continue;
@ -1505,13 +1505,19 @@ fn update_container_namespaces(sandbox: &Sandbox, spec: &mut Spec) -> Result<()>
continue; continue;
} }
} }
// update pid namespace
let mut pid_ns = LinuxNamespace::default();
pid_ns.r#type = NSTYPEPID.to_string();
if !pidNs && !sandbox.sandbox_pid_ns { // Use shared pid ns if useSandboxPidns has been set in either
let mut pid_ns = LinuxNamespace::default(); // the create_sandbox request or create_container request.
pid_ns.r#type = NSTYPEPID.to_string(); // Else set this to empty string so that a new pid namespace is
linux.namespaces.push(pid_ns); // created for the container.
if sandbox_pidns && sandbox.sandbox_pidns.is_some() {
pid_ns.path = String::from(sandbox.sandbox_pidns.as_ref().unwrap().path.as_str());
} }
linux.namespaces.push(pid_ns);
Ok(()) Ok(())
} }

View File

@ -7,9 +7,11 @@
use crate::linux_abi::*; use crate::linux_abi::*;
use crate::mount::{get_mount_fs_type, remove_mounts, TYPEROOTFS}; use crate::mount::{get_mount_fs_type, remove_mounts, TYPEROOTFS};
use crate::namespace::Namespace; use crate::namespace::Namespace;
use crate::namespace::NSTYPEPID;
use crate::network::Network; use crate::network::Network;
use libc::pid_t; use libc::pid_t;
use netlink::{RtnlHandle, NETLINK_ROUTE}; use netlink::{RtnlHandle, NETLINK_ROUTE};
use oci::LinuxNamespace;
use protocols::agent::OnlineCPUMemRequest; use protocols::agent::OnlineCPUMemRequest;
use regex::Regex; use regex::Regex;
use rustjail::cgroups; use rustjail::cgroups;
@ -34,10 +36,10 @@ pub struct Sandbox {
pub pci_device_map: HashMap<String, String>, pub pci_device_map: HashMap<String, String>,
pub shared_utsns: Namespace, pub shared_utsns: Namespace,
pub shared_ipcns: Namespace, pub shared_ipcns: Namespace,
pub sandbox_pidns: Option<Namespace>,
pub storages: HashMap<String, u32>, pub storages: HashMap<String, u32>,
pub running: bool, pub running: bool,
pub no_pivot_root: bool, pub no_pivot_root: bool,
pub sandbox_pid_ns: bool,
pub sender: Option<Sender<i32>>, pub sender: Option<Sender<i32>>,
pub rtnl: Option<RtnlHandle>, pub rtnl: Option<RtnlHandle>,
} }
@ -58,10 +60,10 @@ impl Sandbox {
pci_device_map: HashMap::new(), pci_device_map: HashMap::new(),
shared_utsns: Namespace::new(&logger), shared_utsns: Namespace::new(&logger),
shared_ipcns: Namespace::new(&logger), shared_ipcns: Namespace::new(&logger),
sandbox_pidns: None,
storages: HashMap::new(), storages: HashMap::new(),
running: false, running: false,
no_pivot_root: fs_type.eq(TYPEROOTFS), no_pivot_root: fs_type.eq(TYPEROOTFS),
sandbox_pid_ns: false,
sender: None, sender: None,
rtnl: Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()), rtnl: Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()),
}) })
@ -191,6 +193,30 @@ impl Sandbox {
self.containers.insert(c.id.clone(), c); self.containers.insert(c.id.clone(), c);
} }
pub fn update_shared_pidns(&mut self, c: &LinuxContainer) -> Result<()> {
// Populate the shared pid path only if this is an infra container and
// sandbox_pidns has not been passed in the create_sandbox request.
// This means a separate pause process has not been created. We treat the
// first container created as the infra container in that case
// and use its pid namespace in case pid namespace needs to be shared.
if self.sandbox_pidns.is_none() && self.containers.len() == 0 {
let init_pid = c.init_process_pid;
if init_pid == -1 {
return Err(ErrorKind::ErrorCode(String::from(
"Failed to setup pid namespace: init container pid is -1",
))
.into());
}
let mut pid_ns = Namespace::new(&self.logger).as_pid();
pid_ns.path = format!("/proc/{}/ns/pid", init_pid);
self.sandbox_pidns = Some(pid_ns);
}
Ok(())
}
pub fn get_container(&mut self, id: &str) -> Option<&mut LinuxContainer> { pub fn get_container(&mut self, id: &str) -> Option<&mut LinuxContainer> {
self.containers.get_mut(id) self.containers.get_mut(id)
} }
@ -553,4 +579,21 @@ mod tests {
s.add_container(linux_container); s.add_container(linux_container);
assert!(s.get_container("some_id").is_some()); assert!(s.get_container("some_id").is_some());
} }
#[test]
fn update_shared_pidns() {
skip_if_not_root!();
let logger = slog::Logger::root(slog::Discard, o!());
let mut s = Sandbox::new(&logger).unwrap();
let test_pid = 9999;
let mut linux_container = create_linuxcontainer();
linux_container.init_process_pid = test_pid;
s.update_shared_pidns(&linux_container).unwrap();
assert!(s.sandbox_pidns.is_some());
let ns_path = format!("/proc/{}/ns/pid", test_pid);
assert_eq!(s.sandbox_pidns.unwrap().path, ns_path);
}
} }