summaryrefslogtreecommitdiffstats
path: root/src/runner/container/task.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/runner/container/task.rs')
-rw-r--r--src/runner/container/task.rs124
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
)));
}