jail/validator: introduce helpers to reduce duplicated code

Fixes: #1214

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
This commit is contained in:
Liu Jiang 2020-12-24 09:49:19 +08:00
parent 76ad32136f
commit d38a5d3fcf

View File

@ -10,6 +10,14 @@ use oci::{LinuxIDMapping, LinuxNamespace, Spec};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::{Component, PathBuf}; 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 { fn contain_namespace(nses: &[LinuxNamespace], key: &str) -> bool {
for ns in nses { for ns in nses {
if ns.r#type.as_str() == key { if ns.r#type.as_str() == key {
@ -27,14 +35,14 @@ fn get_namespace_path(nses: &[LinuxNamespace], key: &str) -> Result<String> {
} }
} }
Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))) Err(einval())
} }
fn rootfs(root: &str) -> Result<()> { fn rootfs(root: &str) -> Result<()> {
let path = PathBuf::from(root); let path = PathBuf::from(root);
// not absolute path or not exists // not absolute path or not exists
if !path.exists() || !path.is_absolute() { if !path.exists() || !path.is_absolute() {
return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); return Err(einval());
} }
// symbolic link? ..? // symbolic link? ..?
@ -52,7 +60,7 @@ fn rootfs(root: &str) -> Result<()> {
if let Some(v) = c.as_os_str().to_str() { if let Some(v) = c.as_os_str().to_str() {
stack.push(v.to_string()); stack.push(v.to_string());
} else { } 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")?; let canon = path.canonicalize().context("canonicalize")?;
if cleaned != canon { if cleaned != canon {
// There is symbolic in path // There is symbolic in path
return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); return Err(einval());
} }
Ok(()) Ok(())
@ -79,28 +87,23 @@ fn hostname(oci: &Spec) -> Result<()> {
return Ok(()); return Ok(());
} }
let linux = oci let linux = get_linux(oci)?;
.linux
.as_ref()
.ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?;
if !contain_namespace(&linux.namespaces, "uts") { if !contain_namespace(&linux.namespaces, "uts") {
return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); return Err(einval());
} }
Ok(()) Ok(())
} }
fn security(oci: &Spec) -> Result<()> { fn security(oci: &Spec) -> Result<()> {
let linux = oci let linux = get_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() { if linux.masked_paths.is_empty() && linux.readonly_paths.is_empty() {
return Ok(()); return Ok(());
} }
if !contain_namespace(&linux.namespaces, "mount") { 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 // 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<()> { fn usernamespace(oci: &Spec) -> Result<()> {
let linux = oci let linux = get_linux(oci)?;
.linux
.as_ref()
.ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?;
if contain_namespace(&linux.namespaces, "user") { if contain_namespace(&linux.namespaces, "user") {
let user_ns = PathBuf::from("/proc/self/ns/user"); let user_ns = PathBuf::from("/proc/self/ns/user");
if !user_ns.exists() { if !user_ns.exists() {
@ -135,7 +136,7 @@ fn usernamespace(oci: &Spec) -> Result<()> {
} else { } else {
// no user namespace but idmap // no user namespace but idmap
if !linux.uid_mappings.is_empty() || !linux.gid_mappings.is_empty() { 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<()> { fn cgroupnamespace(oci: &Spec) -> Result<()> {
let linux = oci let linux = get_linux(oci)?;
.linux
.as_ref()
.ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?;
if contain_namespace(&linux.namespaces, "cgroup") { if contain_namespace(&linux.namespaces, "cgroup") {
let path = PathBuf::from("/proc/self/ns/cgroup"); let path = PathBuf::from("/proc/self/ns/cgroup");
if !path.exists() { if !path.exists() {
@ -190,23 +189,21 @@ fn check_host_ns(path: &str) -> Result<()> {
.read_link() .read_link()
.context(format!("read link {:?}", cpath))?; .context(format!("read link {:?}", cpath))?;
if real_cpath == real_hpath { if real_cpath == real_hpath {
return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); return Err(einval());
} }
Ok(()) Ok(())
} }
fn sysctl(oci: &Spec) -> Result<()> { fn sysctl(oci: &Spec) -> Result<()> {
let linux = oci let linux = get_linux(oci)?;
.linux
.as_ref()
.ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?;
for (key, _) in linux.sysctl.iter() { for (key, _) in linux.sysctl.iter() {
if SYSCTLS.contains_key(key.as_str()) || key.starts_with("fs.mqueue.") { if SYSCTLS.contains_key(key.as_str()) || key.starts_with("fs.mqueue.") {
if contain_namespace(&linux.namespaces, "ipc") { if contain_namespace(&linux.namespaces, "ipc") {
continue; continue;
} else { } 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" { 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(()) Ok(())
} }
fn rootless_euid_mapping(oci: &Spec) -> Result<()> { fn rootless_euid_mapping(oci: &Spec) -> Result<()> {
let linux = oci let linux = get_linux(oci)?;
.linux
.as_ref()
.ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?;
if !contain_namespace(&linux.namespaces, "user") { 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() { if linux.uid_mappings.is_empty() || linux.gid_mappings.is_empty() {
// rootless containers requires at least one UID/GID mapping // rootless containers requires at least one UID/GID mapping
return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); return Err(einval());
} }
Ok(()) Ok(())
@ -252,10 +247,7 @@ fn has_idmapping(maps: &[LinuxIDMapping], id: u32) -> bool {
} }
fn rootless_euid_mount(oci: &Spec) -> Result<()> { fn rootless_euid_mount(oci: &Spec) -> Result<()> {
let linux = oci let linux = get_linux(oci)?;
.linux
.as_ref()
.ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?;
for mnt in oci.mounts.iter() { for mnt in oci.mounts.iter() {
for opt in mnt.options.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(); let fields: Vec<&str> = opt.split('=').collect();
if fields.len() != 2 { if fields.len() != 2 {
return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); return Err(einval());
} }
let id = fields[1] let id = fields[1]
@ -272,11 +264,11 @@ fn rootless_euid_mount(oci: &Spec) -> Result<()> {
.context(format!("parse field {}", &fields[1]))?; .context(format!("parse field {}", &fields[1]))?;
if opt.starts_with("uid=") && !has_idmapping(&linux.uid_mappings, id) { 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) { 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<()> { pub fn validate(conf: &Config) -> Result<()> {
lazy_static::initialize(&SYSCTLS); lazy_static::initialize(&SYSCTLS);
let oci = conf let oci = conf.spec.as_ref().ok_or_else(einval)?;
.spec
.as_ref()
.ok_or(anyhow!(nix::Error::from_errno(Errno::EINVAL)))?;
if oci.linux.is_none() { if oci.linux.is_none() {
return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))); return Err(einval());
} }
let root = match oci.root.as_ref() { let root = match oci.root.as_ref() {
Some(v) => v.path.as_str(), Some(v) => v.path.as_str(),
None => return Err(anyhow!(nix::Error::from_errno(Errno::EINVAL))), None => return Err(einval()),
}; };
rootfs(root).context("rootfs")?; rootfs(root).context("rootfs")?;