building the void
This commit is contained in:
parent
87e873b1ff
commit
23f5761d05
51
Cargo.lock
generated
51
Cargo.lock
generated
@ -114,6 +114,7 @@ dependencies = [
|
||||
"nix",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@ -255,6 +256,15 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
@ -292,6 +302,15 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnetwork"
|
||||
version = "0.18.0"
|
||||
@ -485,6 +504,15 @@ dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
@ -508,6 +536,15 @@ version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
@ -602,6 +639,20 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
|
@ -20,6 +20,7 @@ libc = "0.2.117"
|
||||
nix = "0.23.1"
|
||||
|
||||
close_fds = "0.3.2"
|
||||
tempfile = "3.3"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
@ -4,6 +4,7 @@ pub mod clone;
|
||||
mod error;
|
||||
mod spawner;
|
||||
mod specification;
|
||||
mod void;
|
||||
|
||||
use error::Error;
|
||||
use spawner::Spawner;
|
||||
|
101
src/spawner.rs
101
src/spawner.rs
@ -1,8 +1,8 @@
|
||||
use log::{debug, error, info};
|
||||
|
||||
use super::specification::{Arg, Entrypoint, Permission, Pipe, Specification, Trigger};
|
||||
use super::specification::{Arg, Entrypoint, Pipe, Specification, Trigger};
|
||||
use super::PipePair;
|
||||
use crate::clone::{clone3, CloneArgs, CloneFlags};
|
||||
use crate::void::VoidBuilder;
|
||||
use crate::Error;
|
||||
|
||||
use std::collections::HashMap;
|
||||
@ -12,7 +12,7 @@ use std::io::Read;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use close_fds::CloseFdsBuilder;
|
||||
use nix::unistd::{self, Pid};
|
||||
use nix::unistd;
|
||||
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
|
||||
@ -30,34 +30,30 @@ impl<'a> Spawner<'a> {
|
||||
|
||||
match &entrypoint.trigger {
|
||||
Trigger::Startup => {
|
||||
if clone3(CloneArgs::new(Self::clone_flags(
|
||||
&mut entrypoint.permissions.iter(),
|
||||
)))
|
||||
.map_err(|e| Error::Nix {
|
||||
msg: "clone3",
|
||||
src: e,
|
||||
})? == Pid::from_raw(0)
|
||||
{
|
||||
let closure = || {
|
||||
let args = self.prepare_args(name, &entrypoint.args, None);
|
||||
|
||||
unistd::execv(&CString::new(self.binary).unwrap(), &args).map_err(|e| {
|
||||
Error::Nix {
|
||||
if let Err(e) = unistd::execv(&CString::new(self.binary).unwrap(), &args)
|
||||
.map_err(|e| Error::Nix {
|
||||
msg: "execv",
|
||||
src: e,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
})
|
||||
{
|
||||
error!("error: {}", e);
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
let mut builder = VoidBuilder::new();
|
||||
builder.spawn(closure)?;
|
||||
}
|
||||
|
||||
Trigger::Pipe(s) => {
|
||||
// take the pipe in the initiating thread so the File isn't dropped
|
||||
let pipe = self.pipes.get_mut(s).unwrap().take_read();
|
||||
|
||||
if clone3(CloneArgs::new(CloneFlags::empty())).map_err(|e| Error::Nix {
|
||||
msg: "clone3",
|
||||
src: e,
|
||||
})? == Pid::from_raw(0)
|
||||
{
|
||||
let closure = || {
|
||||
let mut closer = CloseFdsBuilder::new();
|
||||
let keep = [pipe.as_raw_fd()];
|
||||
closer.keep_fds(&keep);
|
||||
@ -72,7 +68,10 @@ impl<'a> Spawner<'a> {
|
||||
std::process::exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut builder = VoidBuilder::new();
|
||||
builder.spawn(closure)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,31 +84,32 @@ impl<'a> Spawner<'a> {
|
||||
|
||||
loop {
|
||||
let read_bytes = pipe.read(&mut buf)?;
|
||||
|
||||
if read_bytes == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
debug!("triggering from pipe read");
|
||||
|
||||
if clone3(CloneArgs::new(Self::clone_flags(
|
||||
&mut spec.permissions.iter(),
|
||||
)))
|
||||
.map_err(|e| Error::Nix {
|
||||
msg: "clone3",
|
||||
src: e,
|
||||
})? == Pid::from_raw(0)
|
||||
{
|
||||
let pipe_trigger = std::str::from_utf8(&buf[0..read_bytes]).unwrap();
|
||||
let args = self.prepare_args_ref(name, &spec.args, Some(pipe_trigger));
|
||||
let closure =
|
||||
|| {
|
||||
let pipe_trigger = std::str::from_utf8(&buf[0..read_bytes]).unwrap();
|
||||
let args = self.prepare_args_ref(name, &spec.args, Some(pipe_trigger));
|
||||
|
||||
unistd::execv(&CString::new(self.binary).unwrap(), &args).map_err(|e| {
|
||||
Error::Nix {
|
||||
msg: "execv",
|
||||
src: e,
|
||||
if let Err(e) = unistd::execv(&CString::new(self.binary).unwrap(), &args)
|
||||
.map_err(|e| Error::Nix {
|
||||
msg: "execv",
|
||||
src: e,
|
||||
})
|
||||
{
|
||||
error!("error: {}", e);
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})?;
|
||||
}
|
||||
};
|
||||
|
||||
let mut builder = VoidBuilder::new();
|
||||
builder.spawn(closure)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,25 +180,4 @@ impl<'a> Spawner<'a> {
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn clone_flags(perms: &mut dyn Iterator<Item = &Permission>) -> CloneFlags {
|
||||
let mut flags = CloneFlags::empty();
|
||||
|
||||
flags |= CloneFlags::CLONE_NEWCGROUP; // new cgroup namespace
|
||||
flags |= CloneFlags::CLONE_NEWIPC; // new IPC namespace
|
||||
flags |= CloneFlags::CLONE_NEWNET; // new empty network namespace
|
||||
flags |= CloneFlags::CLONE_NEWNS; // new separate mount namespace
|
||||
flags |= CloneFlags::CLONE_NEWPID; // new PID namespace
|
||||
flags |= CloneFlags::CLONE_NEWUSER; // new user namespace
|
||||
flags |= CloneFlags::CLONE_NEWUTS; // new UTS namespace
|
||||
|
||||
for perm in perms {
|
||||
match perm {
|
||||
Permission::PropagateFiles => flags |= CloneFlags::CLONE_FILES,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,6 @@ pub struct Entrypoint {
|
||||
|
||||
#[serde(default = "Arg::default_vec")]
|
||||
pub args: Vec<Arg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub permissions: HashSet<Permission>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
100
src/void.rs
Normal file
100
src/void.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use crate::clone::{clone3, CloneArgs, CloneFlags};
|
||||
use crate::Error;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use nix::mount::{mount, umount, MsFlags};
|
||||
use nix::sys::signal::Signal;
|
||||
use nix::unistd::{pivot_root, Pid};
|
||||
|
||||
pub struct VoidHandle {}
|
||||
|
||||
pub struct VoidBuilder {
|
||||
#[allow(dead_code)]
|
||||
mounts: HashMap<PathBuf, PathBuf>,
|
||||
}
|
||||
|
||||
impl VoidBuilder {
|
||||
pub fn new() -> VoidBuilder {
|
||||
VoidBuilder {
|
||||
mounts: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn mount(&mut self, src: PathBuf, dst: PathBuf) -> &mut Self {
|
||||
self.mounts.insert(src, dst);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn spawn(&mut self, child_fn: impl FnOnce() -> i32) -> Result<VoidHandle, Error> {
|
||||
let mut args = CloneArgs::new(
|
||||
CloneFlags::CLONE_NEWCGROUP
|
||||
| CloneFlags::CLONE_NEWIPC
|
||||
| CloneFlags::CLONE_NEWNET
|
||||
| CloneFlags::CLONE_NEWNS
|
||||
| CloneFlags::CLONE_NEWPID
|
||||
| CloneFlags::CLONE_NEWUSER
|
||||
| CloneFlags::CLONE_NEWUTS,
|
||||
);
|
||||
args.exit_signal = Some(Signal::SIGCHLD);
|
||||
|
||||
let child = clone3(args).map_err(|e| Error::Nix {
|
||||
msg: "clone3",
|
||||
src: e,
|
||||
})?;
|
||||
|
||||
if child == Pid::from_raw(0) {
|
||||
self.newns_post().unwrap();
|
||||
|
||||
std::process::exit(child_fn())
|
||||
}
|
||||
|
||||
// Leak the child function's resources in the parent process.
|
||||
// This avoids closing files that have been "moved" into the child.
|
||||
// It is also an over-approximation, and may cause actual memory leaks.
|
||||
// As the spawning process is normally short lived, this shouldn't
|
||||
// be a problem.
|
||||
std::mem::forget(child_fn);
|
||||
|
||||
Ok(VoidHandle {})
|
||||
}
|
||||
|
||||
// per-namespace void creation
|
||||
fn newns_post(&self) -> Result<(), Error> {
|
||||
// consume the TempDir so it doesn't get deleted
|
||||
let new_root = tempfile::tempdir()?.into_path();
|
||||
|
||||
mount(
|
||||
Option::<&str>::None,
|
||||
&new_root,
|
||||
Some("tmpfs"),
|
||||
MsFlags::empty(),
|
||||
Option::<&str>::None,
|
||||
)
|
||||
.map_err(|e| Error::Nix {
|
||||
msg: "mount",
|
||||
src: e,
|
||||
})?;
|
||||
|
||||
// TODO: Mount mounts
|
||||
|
||||
let old_root = new_root.join("old_root/");
|
||||
fs::create_dir(&old_root)?;
|
||||
|
||||
pivot_root(&new_root, &old_root).map_err(|e| Error::Nix {
|
||||
msg: "pivot_root",
|
||||
src: e,
|
||||
})?;
|
||||
std::env::set_current_dir("/")?;
|
||||
|
||||
umount("old_root/").map_err(|e| Error::Nix {
|
||||
msg: "umount",
|
||||
src: e,
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user