From bfcae2d40c991971b4f6e1736fa27acf678c9019 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Thu, 8 Oct 2020 21:15:06 +0200 Subject: [PATCH] Add spec and capabilities to sandbox run Signed-off-by: Sascha Grunert --- src/capability.rs | 44 +++++++++++++-- src/oci_spec/runtime.rs | 8 +-- src/sandbox/mod.rs | 4 +- src/sandbox/pinned.rs | 116 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 160 insertions(+), 12 deletions(-) diff --git a/src/capability.rs b/src/capability.rs index 88dc2827..e8134fa8 100644 --- a/src/capability.rs +++ b/src/capability.rs @@ -1,9 +1,11 @@ //! Linux capability handling +use lazy_static::lazy_static; +use std::string::ToString; use std::{collections::HashSet, ops::Deref}; -use strum::{AsRefStr, EnumIter, EnumString, IntoEnumIterator, IntoStaticStr}; +use strum::{AsRefStr, Display, EnumIter, EnumString, IntoEnumIterator, IntoStaticStr}; -#[derive(Debug)] +#[derive(Clone, Debug)] /// A set of capabilities. pub struct Capabilities(HashSet); @@ -23,7 +25,43 @@ impl Deref for Capabilities { } } -#[derive(AsRefStr, IntoStaticStr, Copy, Clone, Debug, EnumIter, EnumString, Eq, Hash, PartialEq)] +impl Default for Capabilities { + fn default() -> Self { + DEFAULT_CAPABILITIES.clone() + } +} + +impl Into> for Capabilities { + fn into(self) -> Vec { + (&self).into() + } +} + +impl Into> for &Capabilities { + fn into(self) -> Vec { + self.iter().map(ToString::to_string).collect() + } +} + +lazy_static! { + static ref DEFAULT_CAPABILITIES: Capabilities = { + let mut s = HashSet::new(); + s.insert(Capability::CapChown); + s.insert(Capability::CapDacOverride); + s.insert(Capability::CapFsetid); + s.insert(Capability::CapFowner); + s.insert(Capability::CapSetgid); + s.insert(Capability::CapSetuid); + s.insert(Capability::CapSetpcap); + s.insert(Capability::CapNetBindService); + s.insert(Capability::CapKill); + Capabilities(s) + }; +} + +#[derive( + AsRefStr, IntoStaticStr, Copy, Clone, Debug, Display, EnumIter, EnumString, Eq, Hash, PartialEq, +)] #[strum(serialize_all = "shouty_snake_case")] /// All available capabilities. pub enum Capability { diff --git a/src/oci_spec/runtime.rs b/src/oci_spec/runtime.rs index ff9fc9a5..a835a160 100644 --- a/src/oci_spec/runtime.rs +++ b/src/oci_spec/runtime.rs @@ -108,8 +108,8 @@ impl Spec { } } -#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Builder, Getters)] -#[builder(pattern = "owned", setter(into, strip_option))] +#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Default, Builder, Getters)] +#[builder(default, pattern = "owned", setter(into, strip_option))] /// Process contains information to start a specific application inside the container. pub struct Process { #[getset(get = "pub")] @@ -243,8 +243,8 @@ pub struct Box { } /// User specifies specific user (and group) information for the container process. -#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Builder, Getters)] -#[builder(pattern = "owned", setter(into, strip_option))] +#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Default, Builder, Getters)] +#[builder(default, pattern = "owned", setter(into, strip_option))] pub struct User { #[getset(get_copy = "pub")] /// UID is the user id. diff --git a/src/sandbox/mod.rs b/src/sandbox/mod.rs index efb6a3de..b2d309fb 100644 --- a/src/sandbox/mod.rs +++ b/src/sandbox/mod.rs @@ -38,7 +38,7 @@ bitflags! { } } -#[derive(Builder, Getters)] +#[derive(Builder, Debug, Getters)] #[builder(pattern = "owned", setter(into))] /// SandboxData holds all the data which will be passed around to the `Pod` trait, too. pub struct SandboxData { @@ -77,7 +77,7 @@ pub struct SandboxData { } pub trait Pod { - /// Run a previously created sandbox. + /// Run a new sandbox. fn run(&mut self, _: &SandboxData) -> Result<()> { Ok(()) } diff --git a/src/sandbox/pinned.rs b/src/sandbox/pinned.rs index f46c45f4..317992f9 100644 --- a/src/sandbox/pinned.rs +++ b/src/sandbox/pinned.rs @@ -1,8 +1,118 @@ //! A pod sandbox implementation which does pin it's namespaces to file descriptors. use crate::sandbox::Pod; +use crate::{ + capability::Capabilities, + oci_spec::runtime::{ + LinuxCapabilities, LinuxCapabilitiesBuilder, Process, ProcessBuilder, Spec, SpecBuilder, + }, + sandbox::SandboxData, +}; +use anyhow::{Context, Result}; +use getset::Getters; +use log::{debug, trace}; -#[derive(Default)] -pub struct PinnedSandbox {} +#[derive(Default, Getters)] +pub struct PinnedSandbox { + #[get] + runtime_spec: Spec, +} -impl Pod for PinnedSandbox {} +impl Pod for PinnedSandbox { + /// Run a new sandbox. + fn run(&mut self, sandbox_data: &SandboxData) -> Result<()> { + debug!("Running pod sandbox: {:?}", sandbox_data); + + // Build the OCI runtime specification + let runtime_spec = self.build_runtime_spec()?; + debug!( + "Built OCI runtime spec for sandbox {}: {:?}", + sandbox_data.id(), + runtime_spec + ); + + // Update the sandbox state + self.runtime_spec = runtime_spec; + Ok(()) + } +} + +impl PinnedSandbox { + /// Build the runtime spec. + pub fn build_runtime_spec(&self) -> Result { + trace!("Building OCI runtime spec"); + let spec = SpecBuilder::default() + .process(self.build_runtime_spec_process()?) + .build() + .context("build OCI runtime spec")?; + Ok(spec) + } + + /// Build the runtime spec process. + fn build_runtime_spec_process(&self) -> Result { + trace!("Building OCI runtime spec process"); + Ok(ProcessBuilder::default() + .capabilities(self.build_runtime_spec_capabilities()?) + .build() + .context("build process")?) + } + + /// Build the runtime spec process Linux capabilities. + fn build_runtime_spec_capabilities(&self) -> Result { + trace!("Building OCI runtime spec capabilities"); + let default_capabilities = Capabilities::default(); + Ok(LinuxCapabilitiesBuilder::default() + .bounding(&default_capabilities) + .effective(&default_capabilities) + .inheritable(&default_capabilities) + .permitted(&default_capabilities) + .build() + .context("build capabilities")?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::sandbox::tests::new_sandbox_data; + + #[test] + fn run_success() -> Result<()> { + let sandbox_data = new_sandbox_data()?; + let mut sandbox = PinnedSandbox::default(); + + sandbox.run(&sandbox_data)?; + + let capabilities = sandbox + .runtime_spec() + .process() + .as_ref() + .context("no process")? + .capabilities() + .as_ref() + .context("no capabilities")?; + let default_capabilities: Vec = Capabilities::default().into(); + assert_eq!( + capabilities.bounding().as_ref().context("no boundings")?, + &default_capabilities + ); + assert_eq!( + capabilities.effective().as_ref().context("no effective")?, + &default_capabilities + ); + assert_eq!( + capabilities + .inheritable() + .as_ref() + .context("no effective")?, + &default_capabilities + ); + assert_eq!( + capabilities.permitted().as_ref().context("no effective")?, + &default_capabilities + ); + assert!(capabilities.ambient().is_none()); + + Ok(()) + } +}