//! The container's executor. It installs the seccomp filter, then execs the job. //! //! This replaces libcontainer's `DefaultExecutor::exec`, which does the same exec without a //! filter. libcontainer runs the executor in the init process after the namespaces, mounts and //! `no_new_privileges` are set up, and right before the job starts. `seccompiler::apply_filter` //! also sets `PR_SET_NO_NEW_PRIVS` itself, so an unprivileged install works. use std::ffi::CString; use libcontainer::workload::default::DefaultExecutor; use libcontainer::workload::{Executor, ExecutorError, ExecutorValidationError}; use nix::unistd; use oci_spec::runtime::Spec; use seccompiler::BpfProgram; /// Installs `filter` and execs the spec's process. The filter is built by the helper before the /// container forks, so this only installs it. #[derive(Clone)] pub struct FilteredExecutor { filter: BpfProgram, } impl FilteredExecutor { pub fn new(filter: BpfProgram) -> Self { Self { filter } } } impl Executor for FilteredExecutor { fn exec(&self, spec: &Spec) -> Result<(), ExecutorError> { seccompiler::apply_filter(&self.filter) .map_err(|e| ExecutorError::Other(format!("cannot install seccomp filter: {e}")))?; exec_process(spec) } fn validate(&self, spec: &Spec) -> Result<(), ExecutorValidationError> { // The same checks as libcontainer's default executor: the process exists in PATH. DefaultExecutor {}.validate(spec) } } /// Replaces the process with `args[0]`, as libcontainer's default executor does. It returns only /// on failure. fn exec_process(spec: &Spec) -> Result<(), ExecutorError> { let args = spec .process() .as_ref() .and_then(|p| p.args().as_ref()) .ok_or(ExecutorError::InvalidArg)?; let path = args.first().ok_or(ExecutorError::InvalidArg)?; let path = CString::new(path.as_bytes()).map_err(|_| ExecutorError::InvalidArg)?; let argv = args .iter() .map(|s| CString::new(s.as_bytes()).map_err(|_| ExecutorError::InvalidArg)) .collect::, ExecutorError>>()?; match unistd::execvp(&path, &argv) { Ok(never) => match never {}, Err(err) => Err(ExecutorError::Other(format!( "cannot exec {}: {err}", path.to_string_lossy() ))), } }