diff options
Diffstat (limited to 'src/runner/container/task.rs')
-rw-r--r-- | src/runner/container/task.rs | 124 |
1 files changed, 87 insertions, 37 deletions
diff --git a/src/runner/container/task.rs b/src/runner/container/task.rs index 73d0d64..923f15b 100644 --- a/src/runner/container/task.rs +++ b/src/runner/container/task.rs @@ -1,22 +1,30 @@ use std::{ collections::HashMap, + fs::File, io::BufWriter, + os::unix::prelude::CommandExt, path::{Path, PathBuf}, - process, + process::{self, Command, Stdio}, time::Instant, }; +use capctl::prctl; use indoc::formatdoc; -use nix::mount::MsFlags; +use nix::{ + mount::{self, MsFlags}, + sched::CloneFlags, + sys::wait, + unistd, +}; use tee_readwrite::TeeWriter; use crate::{ paths, runner, types::*, - util::{cjson, error::*, fs}, + util::{cjson, error::*, fs, Checkable}, }; -use super::{spec, tar}; +use super::{ns, tar}; pub const BUILD_UID: u32 = 1000; pub const BUILD_GID: u32 = 1000; @@ -204,6 +212,10 @@ fn run_task(input_hash: &InputHash, task: &runner::Task) -> Result<()> { let _rootfs_mount = init_task_rootfs(input_hash).context("Failed to initialize task rootfs")?; let task_tmp_dir = paths::task_tmp_dir(input_hash); + let rootfs = paths::join(&[&task_tmp_dir, paths::TASK_TMP_ROOTFS_SUBDIR]); + + let builddir_source = paths::join(&[&task_tmp_dir, paths::TASK_BUILDDIR]); + let builddir_target = paths::join(&[&rootfs, paths::TASK_BUILDDIR]); let command = formatdoc! {" INPUT_HASH={input_hash} @@ -213,46 +225,84 @@ fn run_task(input_hash: &InputHash, task: &runner::Task) -> Result<()> { command = task.input.command, }; - spec::generate_spec(&[ - "unshare", - "--user", - &format!("--map-user={}", BUILD_UID), - &format!("--map-group={}", BUILD_GID), - "sh", - "-exc", - &command, - ]) - .save(paths::join(&[&task_tmp_dir, "config.json"])) - .map_err(Error::new) - .context("Failed to save runtime config")?; - let log_filename = paths::task_log_filename(input_hash); let log = fs::create(&log_filename)?; - let log_stdout = log - .try_clone() - .context("Failed to duplicate log file descriptor")?; - let log_stderr = log - .try_clone() - .context("Failed to duplicate log file descriptor")?; - - let status = process::Command::new("crun") - .arg("--root") - .arg(paths::TASK_TMP_CONTAINERS_ROOT_SUBDIR) - .arg("run") - .arg(input_hash.to_string()) - .current_dir(task_tmp_dir) - .stdin(process::Stdio::null()) - .stdout(log_stdout) - .stderr(log_stderr) - .status() - .context("Failed to start container runtime")?; + + let exec_cmd = |log: File| -> Result<()> { + mount::mount::<_, _, str, str>( + Some(paths::join(&[paths::ROOTFS_DIR, "dev"]).as_str()), + paths::join(&[&rootfs, "dev"]).as_str(), + None, + MsFlags::MS_BIND | MsFlags::MS_REC, + None, + ) + .expect("Failed to bind mount /dev directory"); + mount::mount::<_, _, str, str>( + Some(builddir_source.as_str()), + builddir_target.as_str(), + None, + MsFlags::MS_BIND | MsFlags::MS_REC, + None, + ) + .expect("Failed to bind mount build directory"); + + ns::pivot_root(&rootfs); + ns::container_mounts().context("Failed to set up container mounts")?; + + unistd::sethostname("rebel-builder").context("Failed to set hostname")?; + + prctl::set_no_new_privs().context("set_no_new_privs()")?; + + let log_stdout = log + .try_clone() + .context("Failed to duplicate log file descriptor")?; + let log_stderr = log + .try_clone() + .context("Failed to duplicate log file descriptor")?; + + let mut cmd = Command::new("unshare"); + let err = cmd + .args(&[ + "--user", + &format!("--map-user={}", BUILD_UID), + &format!("--map-group={}", BUILD_GID), + "sh", + "-exc", + &command, + ]) + .stdin(Stdio::null()) + .stdout(log_stdout) + .stderr(log_stderr) + .current_dir(paths::TASK_WORKDIR) + .env_clear() + .env("PATH", "/usr/sbin:/usr/bin:/sbin:/bin") + .env("HOME", "/build") + .exec(); + eprintln!("{}", err); + process::exit(127); + }; + + let (pid, log) = unsafe { + ns::spawn( + CloneFlags::CLONE_NEWNS + | CloneFlags::CLONE_NEWPID + | CloneFlags::CLONE_NEWIPC + | CloneFlags::CLONE_NEWNET + | CloneFlags::CLONE_NEWUTS, + log, + |log| exec_cmd(log).unwrap(), + ) + } + .context("Failed to run task container")?; + + let status = wait::waitpid(pid, None)?; log.sync_all().context("Failed to write log output")?; - if !status.success() { + if let Err(err) = status.check() { return Err(Error::new(format!( "Task failed: {}\nOutput: {}", - status, log_filename + err, log_filename ))); } |