From 51fd624f3e782aabaf6d95bfe9194551238ceb06 Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Wed, 23 Dec 2020 16:24:51 +0800 Subject: [PATCH 1/4] rustjail: add more context info for errors Fixes: #1214 Signed-off-by: Liu Jiang --- src/agent/rustjail/src/validator.rs | 45 +++++++++++++++++------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/agent/rustjail/src/validator.rs b/src/agent/rustjail/src/validator.rs index 9bdfc5c886..d652fd8e60 100644 --- a/src/agent/rustjail/src/validator.rs +++ b/src/agent/rustjail/src/validator.rs @@ -4,7 +4,7 @@ // use crate::container::Config; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use nix::errno::Errno; use oci::{LinuxIDMapping, LinuxNamespace, Spec}; use std::collections::HashMap; @@ -57,7 +57,7 @@ fn rootfs(root: &str) -> Result<()> { cleaned.push(e); } - let canon = path.canonicalize()?; + let canon = path.canonicalize().context("canonicalize")?; if cleaned != canon { // There is symbolic in path return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); @@ -120,8 +120,8 @@ fn usernamespace(oci: &Spec) -> Result<()> { } // check if idmappings is correct, at least I saw idmaps // with zero size was passed to agent - idmapping(&linux.uid_mappings)?; - idmapping(&linux.gid_mappings)?; + idmapping(&linux.uid_mappings).context("idmapping uid")?; + idmapping(&linux.gid_mappings).context("idmapping gid")?; } else { // no user namespace but idmap if !linux.uid_mappings.is_empty() || !linux.gid_mappings.is_empty() { @@ -162,14 +162,20 @@ fn check_host_ns(path: &str) -> Result<()> { let cpath = PathBuf::from(path); let hpath = PathBuf::from("/proc/self/ns/net"); - let real_hpath = hpath.read_link()?; - let meta = cpath.symlink_metadata()?; + let real_hpath = hpath + .read_link() + .context(format!("read link {:?}", hpath))?; + let meta = cpath + .symlink_metadata() + .context(format!("symlink metadata {:?}", cpath))?; let file_type = meta.file_type(); if !file_type.is_symlink() { return Ok(()); } - let real_cpath = cpath.read_link()?; + let real_cpath = cpath + .read_link() + .context(format!("read link {:?}", cpath))?; if real_cpath == real_hpath { return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); } @@ -238,7 +244,10 @@ fn rootless_euid_mount(oci: &Spec) -> Result<()> { return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); } - let id = fields[1].trim().parse::()?; + let id = fields[1] + .trim() + .parse::() + .context(format!("parse field {}", &fields[1]))?; if opt.starts_with("uid=") && !has_idmapping(&linux.uid_mappings, id) { return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); @@ -254,8 +263,8 @@ fn rootless_euid_mount(oci: &Spec) -> Result<()> { } fn rootless_euid(oci: &Spec) -> Result<()> { - rootless_euid_mapping(oci)?; - rootless_euid_mount(oci)?; + rootless_euid_mapping(oci).context("rootless euid mapping")?; + rootless_euid_mount(oci).context("rotless euid mount")?; Ok(()) } @@ -272,16 +281,16 @@ pub fn validate(conf: &Config) -> Result<()> { } let root = oci.root.as_ref().unwrap().path.as_str(); - rootfs(root)?; - network(oci)?; - hostname(oci)?; - security(oci)?; - usernamespace(oci)?; - cgroupnamespace(oci)?; - sysctl(&oci)?; + rootfs(root).context("rootfs")?; + network(oci).context("network")?; + hostname(oci).context("hostname")?; + security(oci).context("security")?; + usernamespace(oci).context("usernamespace")?; + cgroupnamespace(oci).context("cgroupnamespace")?; + sysctl(&oci).context("sysctl")?; if conf.rootless_euid { - rootless_euid(oci)?; + rootless_euid(oci).context("rootless euid")?; } Ok(()) From 76ad32136f82c6fb7b400e5c0d12ea3b249d1518 Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Thu, 24 Dec 2020 09:47:10 +0800 Subject: [PATCH 2/4] jail/validator: avoid unwrap() for safety Explicitly return error codes instead of unwrap(). Fixes: #1214 Signed-off-by: Liu Jiang --- src/agent/rustjail/src/validator.rs | 57 +++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/src/agent/rustjail/src/validator.rs b/src/agent/rustjail/src/validator.rs index d652fd8e60..33f9b0da87 100644 --- a/src/agent/rustjail/src/validator.rs +++ b/src/agent/rustjail/src/validator.rs @@ -49,7 +49,11 @@ fn rootfs(root: &str) -> Result<()> { continue; } - stack.push(c.as_os_str().to_str().unwrap().to_string()); + if let Some(v) = c.as_os_str().to_str() { + stack.push(v.to_string()); + } else { + return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + } } let mut cleaned = PathBuf::from("/"); @@ -75,10 +79,10 @@ fn hostname(oci: &Spec) -> Result<()> { return Ok(()); } - if oci.linux.is_none() { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); - } - let linux = oci.linux.as_ref().unwrap(); + let linux = oci + .linux + .as_ref() + .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; if !contain_namespace(&linux.namespaces, "uts") { return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); } @@ -87,7 +91,10 @@ fn hostname(oci: &Spec) -> Result<()> { } fn security(oci: &Spec) -> Result<()> { - let linux = oci.linux.as_ref().unwrap(); + let linux = oci + .linux + .as_ref() + .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; if linux.masked_paths.is_empty() && linux.readonly_paths.is_empty() { return Ok(()); } @@ -112,7 +119,10 @@ fn idmapping(maps: &[LinuxIDMapping]) -> Result<()> { } fn usernamespace(oci: &Spec) -> Result<()> { - let linux = oci.linux.as_ref().unwrap(); + let linux = oci + .linux + .as_ref() + .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; if contain_namespace(&linux.namespaces, "user") { let user_ns = PathBuf::from("/proc/self/ns/user"); if !user_ns.exists() { @@ -133,7 +143,10 @@ fn usernamespace(oci: &Spec) -> Result<()> { } fn cgroupnamespace(oci: &Spec) -> Result<()> { - let linux = oci.linux.as_ref().unwrap(); + let linux = oci + .linux + .as_ref() + .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; if contain_namespace(&linux.namespaces, "cgroup") { let path = PathBuf::from("/proc/self/ns/cgroup"); if !path.exists() { @@ -184,7 +197,10 @@ fn check_host_ns(path: &str) -> Result<()> { } fn sysctl(oci: &Spec) -> Result<()> { - let linux = oci.linux.as_ref().unwrap(); + let linux = oci + .linux + .as_ref() + .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; for (key, _) in linux.sysctl.iter() { if SYSCTLS.contains_key(key.as_str()) || key.starts_with("fs.mqueue.") { if contain_namespace(&linux.namespaces, "ipc") { @@ -210,7 +226,10 @@ fn sysctl(oci: &Spec) -> Result<()> { } fn rootless_euid_mapping(oci: &Spec) -> Result<()> { - let linux = oci.linux.as_ref().unwrap(); + let linux = oci + .linux + .as_ref() + .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; if !contain_namespace(&linux.namespaces, "user") { return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); } @@ -233,7 +252,10 @@ fn has_idmapping(maps: &[LinuxIDMapping], id: u32) -> bool { } fn rootless_euid_mount(oci: &Spec) -> Result<()> { - let linux = oci.linux.as_ref().unwrap(); + let linux = oci + .linux + .as_ref() + .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; for mnt in oci.mounts.iter() { for opt in mnt.options.iter() { @@ -270,16 +292,19 @@ fn rootless_euid(oci: &Spec) -> Result<()> { pub fn validate(conf: &Config) -> Result<()> { lazy_static::initialize(&SYSCTLS); - let oci = conf.spec.as_ref().unwrap(); + let oci = conf + .spec + .as_ref() + .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; if oci.linux.is_none() { return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); } - if oci.root.is_none() { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); - } - let root = oci.root.as_ref().unwrap().path.as_str(); + let root = match oci.root.as_ref() { + Some(v) => v.path.as_str(), + None => return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))), + }; rootfs(root).context("rootfs")?; network(oci).context("network")?; From d38a5d3fcf710a745a75839dde7e59b9b6061500 Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Thu, 24 Dec 2020 09:49:19 +0800 Subject: [PATCH 3/4] jail/validator: introduce helpers to reduce duplicated code Fixes: #1214 Signed-off-by: Liu Jiang --- src/agent/rustjail/src/validator.rs | 91 +++++++++++++---------------- 1 file changed, 40 insertions(+), 51 deletions(-) diff --git a/src/agent/rustjail/src/validator.rs b/src/agent/rustjail/src/validator.rs index 33f9b0da87..567b3b16f3 100644 --- a/src/agent/rustjail/src/validator.rs +++ b/src/agent/rustjail/src/validator.rs @@ -10,6 +10,14 @@ use oci::{LinuxIDMapping, LinuxNamespace, Spec}; use std::collections::HashMap; use std::path::{Component, PathBuf}; +fn einval() { + anyhow!(nix::Error::from_errno(Errno::EINVAL)) +} + +fn get_linux(oci: &Spec) -> Result<&Linux> { + oci.linux.as_ref().ok_or_else(einval) +} + fn contain_namespace(nses: &[LinuxNamespace], key: &str) -> bool { for ns in nses { if ns.r#type.as_str() == key { @@ -27,14 +35,14 @@ fn get_namespace_path(nses: &[LinuxNamespace], key: &str) -> Result { } } - Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))) + Err(einval()) } fn rootfs(root: &str) -> Result<()> { let path = PathBuf::from(root); // not absolute path or not exists if !path.exists() || !path.is_absolute() { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } // symbolic link? ..? @@ -52,7 +60,7 @@ fn rootfs(root: &str) -> Result<()> { if let Some(v) = c.as_os_str().to_str() { stack.push(v.to_string()); } else { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } } @@ -64,7 +72,7 @@ fn rootfs(root: &str) -> Result<()> { let canon = path.canonicalize().context("canonicalize")?; if cleaned != canon { // There is symbolic in path - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } Ok(()) @@ -79,28 +87,23 @@ fn hostname(oci: &Spec) -> Result<()> { return Ok(()); } - let linux = oci - .linux - .as_ref() - .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; + let linux = get_linux(oci)?; if !contain_namespace(&linux.namespaces, "uts") { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } Ok(()) } fn security(oci: &Spec) -> Result<()> { - let linux = oci - .linux - .as_ref() - .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; + let linux = get_linux(oci)?; + if linux.masked_paths.is_empty() && linux.readonly_paths.is_empty() { return Ok(()); } if !contain_namespace(&linux.namespaces, "mount") { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } // don't care about selinux at present @@ -115,14 +118,12 @@ fn idmapping(maps: &[LinuxIDMapping]) -> Result<()> { } } - Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))) + Err(einval()) } fn usernamespace(oci: &Spec) -> Result<()> { - let linux = oci - .linux - .as_ref() - .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; + let linux = get_linux(oci)?; + if contain_namespace(&linux.namespaces, "user") { let user_ns = PathBuf::from("/proc/self/ns/user"); if !user_ns.exists() { @@ -135,7 +136,7 @@ fn usernamespace(oci: &Spec) -> Result<()> { } else { // no user namespace but idmap if !linux.uid_mappings.is_empty() || !linux.gid_mappings.is_empty() { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } } @@ -143,10 +144,8 @@ fn usernamespace(oci: &Spec) -> Result<()> { } fn cgroupnamespace(oci: &Spec) -> Result<()> { - let linux = oci - .linux - .as_ref() - .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; + let linux = get_linux(oci)?; + if contain_namespace(&linux.namespaces, "cgroup") { let path = PathBuf::from("/proc/self/ns/cgroup"); if !path.exists() { @@ -190,23 +189,21 @@ fn check_host_ns(path: &str) -> Result<()> { .read_link() .context(format!("read link {:?}", cpath))?; if real_cpath == real_hpath { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } Ok(()) } fn sysctl(oci: &Spec) -> Result<()> { - let linux = oci - .linux - .as_ref() - .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; + let linux = get_linux(oci)?; + for (key, _) in linux.sysctl.iter() { if SYSCTLS.contains_key(key.as_str()) || key.starts_with("fs.mqueue.") { if contain_namespace(&linux.namespaces, "ipc") { continue; } else { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } } @@ -216,27 +213,25 @@ fn sysctl(oci: &Spec) -> Result<()> { } if key == "kernel.hostname" { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } } - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } Ok(()) } fn rootless_euid_mapping(oci: &Spec) -> Result<()> { - let linux = oci - .linux - .as_ref() - .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; + let linux = get_linux(oci)?; + if !contain_namespace(&linux.namespaces, "user") { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } if linux.uid_mappings.is_empty() || linux.gid_mappings.is_empty() { // rootless containers requires at least one UID/GID mapping - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } Ok(()) @@ -252,10 +247,7 @@ fn has_idmapping(maps: &[LinuxIDMapping], id: u32) -> bool { } fn rootless_euid_mount(oci: &Spec) -> Result<()> { - let linux = oci - .linux - .as_ref() - .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; + let linux = get_linux(oci)?; for mnt in oci.mounts.iter() { for opt in mnt.options.iter() { @@ -263,7 +255,7 @@ fn rootless_euid_mount(oci: &Spec) -> Result<()> { let fields: Vec<&str> = opt.split('=').collect(); if fields.len() != 2 { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } let id = fields[1] @@ -272,11 +264,11 @@ fn rootless_euid_mount(oci: &Spec) -> Result<()> { .context(format!("parse field {}", &fields[1]))?; if opt.starts_with("uid=") && !has_idmapping(&linux.uid_mappings, id) { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } if opt.starts_with("gid=") && !has_idmapping(&linux.gid_mappings, id) { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } } } @@ -292,18 +284,15 @@ fn rootless_euid(oci: &Spec) -> Result<()> { pub fn validate(conf: &Config) -> Result<()> { lazy_static::initialize(&SYSCTLS); - let oci = conf - .spec - .as_ref() - .ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?; + let oci = conf.spec.as_ref().ok_or_else(einval)?; if oci.linux.is_none() { - return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); + return Err(einval()); } let root = match oci.root.as_ref() { Some(v) => v.path.as_str(), - None => return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))), + None => return Err(einval()), }; rootfs(root).context("rootfs")?; From b366af9358f212e91f55645d3c2e3ea92f6ba2cc Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Wed, 23 Dec 2020 16:27:54 +0800 Subject: [PATCH 4/4] jail: add more test cases for validator Fixes: #1214 Signed-off-by: Liu Jiang --- src/agent/rustjail/src/validator.rs | 277 +++++++++++++++++++++++++++- 1 file changed, 274 insertions(+), 3 deletions(-) diff --git a/src/agent/rustjail/src/validator.rs b/src/agent/rustjail/src/validator.rs index 567b3b16f3..86e04830d9 100644 --- a/src/agent/rustjail/src/validator.rs +++ b/src/agent/rustjail/src/validator.rs @@ -4,13 +4,13 @@ // use crate::container::Config; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Context, Error, Result}; use nix::errno::Errno; -use oci::{LinuxIDMapping, LinuxNamespace, Spec}; +use oci::{Linux, LinuxIDMapping, LinuxNamespace, Spec}; use std::collections::HashMap; use std::path::{Component, PathBuf}; -fn einval() { +fn einval() -> Error { anyhow!(nix::Error::from_errno(Errno::EINVAL)) } @@ -309,3 +309,274 @@ pub fn validate(conf: &Config) -> Result<()> { Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use oci::Mount; + + #[test] + fn test_namespace() { + let namespaces = [ + LinuxNamespace { + r#type: "net".to_owned(), + path: "/sys/cgroups/net".to_owned(), + }, + LinuxNamespace { + r#type: "uts".to_owned(), + path: "/sys/cgroups/uts".to_owned(), + }, + ]; + + assert_eq!(contain_namespace(&namespaces, "net"), true); + assert_eq!(contain_namespace(&namespaces, "uts"), true); + + assert_eq!(contain_namespace(&namespaces, ""), false); + assert_eq!(contain_namespace(&namespaces, "Net"), false); + assert_eq!(contain_namespace(&namespaces, "ipc"), false); + + assert_eq!( + get_namespace_path(&namespaces, "net").unwrap(), + "/sys/cgroups/net" + ); + assert_eq!( + get_namespace_path(&namespaces, "uts").unwrap(), + "/sys/cgroups/uts" + ); + + get_namespace_path(&namespaces, "").unwrap_err(); + get_namespace_path(&namespaces, "Uts").unwrap_err(); + get_namespace_path(&namespaces, "ipc").unwrap_err(); + } + + #[test] + fn test_rootfs() { + rootfs("/_no_exit_fs_xxxxxxxxxxx").unwrap_err(); + rootfs("sys").unwrap_err(); + rootfs("/proc/self/root").unwrap_err(); + rootfs("/proc/self/root/sys").unwrap_err(); + + rootfs("/proc/self").unwrap_err(); + rootfs("/./proc/self").unwrap_err(); + rootfs("/proc/././self").unwrap_err(); + rootfs("/proc/.././self").unwrap_err(); + + rootfs("/proc/uptime").unwrap(); + rootfs("/../proc/uptime").unwrap(); + rootfs("/../../proc/uptime").unwrap(); + rootfs("/proc/../proc/uptime").unwrap(); + rootfs("/proc/../../proc/uptime").unwrap(); + } + + #[test] + fn test_hostname() { + let mut spec = Spec::default(); + + hostname(&spec).unwrap(); + + spec.hostname = "a.test.com".to_owned(); + hostname(&spec).unwrap_err(); + + let mut linux = Linux::default(); + linux.namespaces = vec![ + LinuxNamespace { + r#type: "net".to_owned(), + path: "/sys/cgroups/net".to_owned(), + }, + LinuxNamespace { + r#type: "uts".to_owned(), + path: "/sys/cgroups/uts".to_owned(), + }, + ]; + spec.linux = Some(linux); + hostname(&spec).unwrap(); + } + + #[test] + fn test_security() { + let mut spec = Spec::default(); + + let linux = Linux::default(); + spec.linux = Some(linux); + security(&spec).unwrap(); + + let mut linux = Linux::default(); + linux.masked_paths.push("/test".to_owned()); + linux.namespaces = vec![ + LinuxNamespace { + r#type: "net".to_owned(), + path: "/sys/cgroups/net".to_owned(), + }, + LinuxNamespace { + r#type: "uts".to_owned(), + path: "/sys/cgroups/uts".to_owned(), + }, + ]; + spec.linux = Some(linux); + security(&spec).unwrap_err(); + + let mut linux = Linux::default(); + linux.masked_paths.push("/test".to_owned()); + linux.namespaces = vec![ + LinuxNamespace { + r#type: "net".to_owned(), + path: "/sys/cgroups/net".to_owned(), + }, + LinuxNamespace { + r#type: "mount".to_owned(), + path: "/sys/cgroups/mount".to_owned(), + }, + ]; + spec.linux = Some(linux); + security(&spec).unwrap(); + } + + #[test] + fn test_usernamespace() { + let mut spec = Spec::default(); + usernamespace(&spec).unwrap_err(); + + let linux = Linux::default(); + spec.linux = Some(linux); + usernamespace(&spec).unwrap(); + + let mut linux = Linux::default(); + linux.uid_mappings = vec![LinuxIDMapping { + container_id: 0, + host_id: 1000, + size: 0, + }]; + spec.linux = Some(linux); + usernamespace(&spec).unwrap_err(); + + let mut linux = Linux::default(); + linux.uid_mappings = vec![LinuxIDMapping { + container_id: 0, + host_id: 1000, + size: 100, + }]; + spec.linux = Some(linux); + usernamespace(&spec).unwrap_err(); + } + + #[test] + fn test_rootless_euid() { + let mut spec = Spec::default(); + + // Test case: without linux + rootless_euid_mapping(&spec).unwrap_err(); + rootless_euid_mount(&spec).unwrap_err(); + + // Test case: without user namespace + let linux = Linux::default(); + spec.linux = Some(linux); + rootless_euid_mapping(&spec).unwrap_err(); + + // Test case: without user namespace + let linux = spec.linux.as_mut().unwrap(); + linux.namespaces = vec![ + LinuxNamespace { + r#type: "net".to_owned(), + path: "/sys/cgroups/net".to_owned(), + }, + LinuxNamespace { + r#type: "uts".to_owned(), + path: "/sys/cgroups/uts".to_owned(), + }, + ]; + rootless_euid_mapping(&spec).unwrap_err(); + + let linux = spec.linux.as_mut().unwrap(); + linux.namespaces = vec![ + LinuxNamespace { + r#type: "net".to_owned(), + path: "/sys/cgroups/net".to_owned(), + }, + LinuxNamespace { + r#type: "user".to_owned(), + path: "/sys/cgroups/user".to_owned(), + }, + ]; + linux.uid_mappings = vec![LinuxIDMapping { + container_id: 0, + host_id: 1000, + size: 1000, + }]; + linux.gid_mappings = vec![LinuxIDMapping { + container_id: 0, + host_id: 1000, + size: 1000, + }]; + rootless_euid_mapping(&spec).unwrap(); + + spec.mounts.push(Mount { + destination: "/app".to_owned(), + r#type: "tmpfs".to_owned(), + source: "".to_owned(), + options: vec!["uid=10000".to_owned()], + }); + rootless_euid_mount(&spec).unwrap_err(); + + spec.mounts = vec![ + (Mount { + destination: "/app".to_owned(), + r#type: "tmpfs".to_owned(), + source: "".to_owned(), + options: vec!["uid=500".to_owned(), "gid=500".to_owned()], + }), + ]; + rootless_euid(&spec).unwrap(); + } + + #[test] + fn test_check_host_ns() { + check_host_ns("/proc/self/ns/net").unwrap_err(); + check_host_ns("/proc/sys/net/ipv4/tcp_sack").unwrap(); + } + + #[test] + fn test_sysctl() { + let mut spec = Spec::default(); + + let mut linux = Linux::default(); + linux.namespaces = vec![LinuxNamespace { + r#type: "net".to_owned(), + path: "/sys/cgroups/net".to_owned(), + }]; + linux + .sysctl + .insert("kernel.domainname".to_owned(), "test.com".to_owned()); + spec.linux = Some(linux); + sysctl(&spec).unwrap_err(); + + spec.linux + .as_mut() + .unwrap() + .namespaces + .push(LinuxNamespace { + r#type: "uts".to_owned(), + path: "/sys/cgroups/uts".to_owned(), + }); + sysctl(&spec).unwrap(); + } + + #[test] + fn test_validate() { + let spec = Spec::default(); + let mut config = Config { + cgroup_name: "container1".to_owned(), + use_systemd_cgroup: false, + no_pivot_root: true, + no_new_keyring: true, + rootless_euid: false, + rootless_cgroup: false, + spec: Some(spec), + }; + + validate(&config).unwrap_err(); + + let linux = Linux::default(); + config.spec.as_mut().unwrap().linux = Some(linux); + validate(&config).unwrap_err(); + } +}