Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support making binds using mount #48

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions minion-tests/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
//! Worker: sets up sandbox, executes `minion-tests` in Test mode.
//! Test: just executes test selected by name
#![feature(asm, test)]

use minion::linux::Settings;
mod master;
mod tests;
mod worker;
Expand Down Expand Up @@ -98,3 +100,10 @@ enum Role {
Worker,
Test,
}

fn configurations() -> Vec<Settings> {
let s = Settings::new();
let mut s_ext_mount = s.clone();
s_ext_mount.use_mount_program = true;
vec![s, s_ext_mount]
}
18 changes: 14 additions & 4 deletions minion-tests/src/master.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Master source code

use crate::TestCase;

#[derive(Copy, Clone)]
Expand Down Expand Up @@ -58,25 +59,33 @@ struct ExecuteOptions {
}

fn execute_tests(test_cases: &[&dyn TestCase], exec_opts: ExecuteOptions) -> Outcome {
let configs_count = crate::configurations().len();
println!("will run:");
for &case in test_cases {
println!(" - {}", case.name());
}
println!("({} tests)", test_cases.len());
let mut outcome = Outcome::Success;
for &case in test_cases {
outcome = outcome.and(execute_single_test(case, exec_opts));
for idx in 0..configs_count {
outcome = outcome.and(execute_single_test(case, exec_opts, idx));
}
}
outcome
}

fn execute_single_test(case: &dyn TestCase, exec_opts: ExecuteOptions) -> Outcome {
println!("------ {} ------", case.name());
fn execute_single_test(
case: &dyn TestCase,
exec_opts: ExecuteOptions,
config_idx: usize,
) -> Outcome {
println!("------ {} (config #{}) ------", case.name(), config_idx);
let self_exe = std::env::current_exe().unwrap();
let mut cmd = if exec_opts.trace {
let mut cmd = std::process::Command::new("strace");
cmd.arg("-f"); // follow forks
cmd.arg("-o").arg(format!("strace-log-{}.txt", case.name()));
cmd.arg("-o")
.arg(format!("strace-log-{}-{}.txt", case.name(), config_idx));
cmd.arg(self_exe);
cmd
} else {
Expand All @@ -91,6 +100,7 @@ fn execute_single_test(case: &dyn TestCase, exec_opts: ExecuteOptions) -> Outcom
}
cmd.env(crate::WORKER_ENV_NAME, "1");
cmd.env("TEST", case.name());
cmd.env("CONFIG_INDEX", config_idx.to_string());
let status = cmd.status().unwrap();
if status.success() {
Outcome::Success
Expand Down
6 changes: 5 additions & 1 deletion minion-tests/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ async fn inner_main(test_cases: &[&'static dyn TestCase]) {
.find(|&tc| tc.name() == test_case_name)
.unwrap();

let config_idx = std::env::var("CONFIG_INDEX").unwrap().parse().unwrap();
let config = crate::configurations().into_iter().nth(config_idx).unwrap();

let tempdir = tempfile::TempDir::new().expect("cannot create temporary dir");
let backend = minion::erased::setup().expect("backend creation failed");
let backend = minion::LinuxBackend::new(config).expect("backend creation failed");
let backend: Box<dyn minion::erased::Backend> = Box::new(backend);
let opts = minion::SandboxOptions {
cpu_time_limit: test_case.time_limit(),
real_time_limit: test_case.real_time_limit(),
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub use command::Command;
/// Anyway, SUID-bit will be disabled.
///
/// Warning: this type is __unstable__ (i.e. not covered by SemVer) and __non-portable__
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum SharedDirKind {
Readonly,
Full,
Expand Down
5 changes: 5 additions & 0 deletions src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ pub struct Settings {
/// If enabled, minion will ignore clone(MOUNT_NEWNS) error.
/// This flag has to be enabled for gVisor support.
pub allow_unsupported_mount_namespace: bool,

/// If enabled, minion will use `mount` CLI utility
/// for creating bind-mounts.
pub use_mount_program: bool,
}

impl Default for Settings {
Expand All @@ -232,6 +236,7 @@ impl Default for Settings {
cgroupfs: std::env::var_os("MINION_CGROUPFS")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("/sys/fs/cgroup")),
use_mount_program: false,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/linux/jail_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub(crate) struct JailOptions {
pub(crate) real_time_limit: Duration,
pub(crate) isolation_root: PathBuf,
pub(crate) exposed_paths: Vec<SharedDir>,
pub(crate) use_mount_for_binds: bool,
pub(crate) jail_id: String,
pub(crate) watchdog_chan: RawFd,
pub(crate) allow_mount_ns_failure: bool,
Expand Down
1 change: 1 addition & 0 deletions src/linux/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ impl LinuxSandbox {
real_time_limit: options.real_time_limit,
isolation_root: options.isolation_root.clone(),
exposed_paths: options.exposed_paths.clone(),
use_mount_for_binds: settings.use_mount_program,
jail_id: jail_id.clone(),
watchdog_chan: write_end,
allow_mount_ns_failure: settings.allow_unsupported_mount_namespace,
Expand Down
66 changes: 52 additions & 14 deletions src/linux/zygote/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use std::{
ffi::CString,
fs, io,
io::Write,
os::unix::{ffi::OsStrExt, io::RawFd},
os::unix::{ffi::OsStrExt, io::RawFd, prelude::ExitStatusExt},
path::Path,
process::Command,
ptr, time,
};
use tiny_nix_ipc::Socket;
Expand Down Expand Up @@ -49,15 +50,7 @@ fn configure_dir(dir_path: &Path) -> Result<(), Error> {
Ok(())
}

fn expose_dir(jail_root: &Path, system_path: &Path, alias_path: &Path, kind: SharedDirKind) {
let bind_target = jail_root.join(alias_path);
fs::create_dir_all(&bind_target).unwrap();
let stat = fs::metadata(&system_path)
.unwrap_or_else(|err| panic!("failed to stat {}: {}", system_path.display(), err));
if stat.is_file() {
fs::remove_dir(&bind_target).unwrap();
fs::write(&bind_target, &"").unwrap();
}
fn expose_dir_via_syscall(bind_target: &Path, system_path: &Path, kind: SharedDirKind) {
let bind_target = CString::new(bind_target.as_os_str().as_bytes()).unwrap();
let bind_src = CString::new(system_path.as_os_str().as_bytes()).unwrap();
unsafe {
Expand Down Expand Up @@ -87,10 +80,51 @@ fn expose_dir(jail_root: &Path, system_path: &Path, alias_path: &Path, kind: Sha
}
}

pub(crate) fn expose_dirs(expose: &[SharedDir], jail_root: &Path) {
fn expose_dir_via_mount(bind_target: &Path, system_path: &Path, kind: SharedDirKind) {
let mut cmd = Command::new("mount");
match kind {
SharedDirKind::Full => {
cmd.arg("--bind");
}
SharedDirKind::Readonly => {
cmd.arg("-o").arg("bind,ro");
}
}
cmd.arg(system_path);
cmd.arg(bind_target);
match cmd.status() {
Ok(st) => {
if !st.success() {
panic!(
"mount program failed: exit code {:?}, signal {:?}",
st.code(),
st.signal()
);
}
}
Err(err) => {
panic!("failed to spawn mount: {:#}", err);
}
}
}

pub(crate) fn expose_dirs(expose: &[SharedDir], jail_root: &Path, use_mount: bool) {
// mount --bind
for x in expose {
expose_dir(jail_root, &x.src, &x.dest, x.kind.clone())
let bind_target = jail_root.join(&x.dest);
fs::create_dir_all(&bind_target).unwrap();
let stat = fs::metadata(&x.src)
.unwrap_or_else(|err| panic!("failed to stat {}: {}", x.src.display(), err));
if stat.is_file() {
fs::remove_dir(&bind_target).unwrap();
fs::write(&bind_target, &"").unwrap();
}
// configure_dir(&bind_target).expect("failed to change access rights on the mount point");
if use_mount {
expose_dir_via_mount(&bind_target, &x.src, x.kind)
} else {
expose_dir_via_syscall(&bind_target, &x.src, x.kind)
}
}
}

Expand Down Expand Up @@ -183,7 +217,11 @@ fn setup_time_watch(
}

fn setup_expositions(options: &JailOptions) {
expose_dirs(&options.exposed_paths, &options.isolation_root);
expose_dirs(
&options.exposed_paths,
&options.isolation_root,
options.use_mount_for_binds,
);
}

fn setup_panic_hook() {
Expand Down Expand Up @@ -212,10 +250,10 @@ pub(in crate::linux) fn setup(
) -> Result<SetupData, Error> {
setup_panic_hook();
setup_sighandler();
setup_expositions(&jail_params);
// must be done before `configure_dir`.
setup_uid_mapping(sock)?;
configure_dir(&jail_params.isolation_root)?;
setup_expositions(&jail_params);
setup_procfs(&jail_params)?;
let cgroup_join_handle = cgroup_driver.create_group(
&jail_params.jail_id,
Expand Down