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",
|
"nix",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -255,6 +256,15 @@ version = "1.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
|
checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "half"
|
name = "half"
|
||||||
version = "1.8.2"
|
version = "1.8.2"
|
||||||
@ -292,6 +302,15 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnetwork"
|
name = "ipnetwork"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
@ -485,6 +504,15 @@ dependencies = [
|
|||||||
"num_cpus",
|
"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]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.4"
|
version = "1.5.4"
|
||||||
@ -508,6 +536,15 @@ version = "0.6.25"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
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]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -602,6 +639,20 @@ dependencies = [
|
|||||||
"unicode-xid",
|
"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]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -20,6 +20,7 @@ libc = "0.2.117"
|
|||||||
nix = "0.23.1"
|
nix = "0.23.1"
|
||||||
|
|
||||||
close_fds = "0.3.2"
|
close_fds = "0.3.2"
|
||||||
|
tempfile = "3.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
@ -4,6 +4,7 @@ pub mod clone;
|
|||||||
mod error;
|
mod error;
|
||||||
mod spawner;
|
mod spawner;
|
||||||
mod specification;
|
mod specification;
|
||||||
|
mod void;
|
||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use spawner::Spawner;
|
use spawner::Spawner;
|
||||||
|
101
src/spawner.rs
101
src/spawner.rs
@ -1,8 +1,8 @@
|
|||||||
use log::{debug, error, info};
|
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 super::PipePair;
|
||||||
use crate::clone::{clone3, CloneArgs, CloneFlags};
|
use crate::void::VoidBuilder;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -12,7 +12,7 @@ use std::io::Read;
|
|||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
use close_fds::CloseFdsBuilder;
|
use close_fds::CloseFdsBuilder;
|
||||||
use nix::unistd::{self, Pid};
|
use nix::unistd;
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = 1024;
|
const BUFFER_SIZE: usize = 1024;
|
||||||
|
|
||||||
@ -30,34 +30,30 @@ impl<'a> Spawner<'a> {
|
|||||||
|
|
||||||
match &entrypoint.trigger {
|
match &entrypoint.trigger {
|
||||||
Trigger::Startup => {
|
Trigger::Startup => {
|
||||||
if clone3(CloneArgs::new(Self::clone_flags(
|
let closure = || {
|
||||||
&mut entrypoint.permissions.iter(),
|
|
||||||
)))
|
|
||||||
.map_err(|e| Error::Nix {
|
|
||||||
msg: "clone3",
|
|
||||||
src: e,
|
|
||||||
})? == Pid::from_raw(0)
|
|
||||||
{
|
|
||||||
let args = self.prepare_args(name, &entrypoint.args, None);
|
let args = self.prepare_args(name, &entrypoint.args, None);
|
||||||
|
|
||||||
unistd::execv(&CString::new(self.binary).unwrap(), &args).map_err(|e| {
|
if let Err(e) = unistd::execv(&CString::new(self.binary).unwrap(), &args)
|
||||||
Error::Nix {
|
.map_err(|e| Error::Nix {
|
||||||
msg: "execv",
|
msg: "execv",
|
||||||
src: e,
|
src: e,
|
||||||
}
|
})
|
||||||
})?;
|
{
|
||||||
}
|
error!("error: {}", e);
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut builder = VoidBuilder::new();
|
||||||
|
builder.spawn(closure)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Trigger::Pipe(s) => {
|
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();
|
let pipe = self.pipes.get_mut(s).unwrap().take_read();
|
||||||
|
|
||||||
if clone3(CloneArgs::new(CloneFlags::empty())).map_err(|e| Error::Nix {
|
let closure = || {
|
||||||
msg: "clone3",
|
|
||||||
src: e,
|
|
||||||
})? == Pid::from_raw(0)
|
|
||||||
{
|
|
||||||
let mut closer = CloseFdsBuilder::new();
|
let mut closer = CloseFdsBuilder::new();
|
||||||
let keep = [pipe.as_raw_fd()];
|
let keep = [pipe.as_raw_fd()];
|
||||||
closer.keep_fds(&keep);
|
closer.keep_fds(&keep);
|
||||||
@ -72,7 +68,10 @@ impl<'a> Spawner<'a> {
|
|||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
let mut builder = VoidBuilder::new();
|
||||||
|
builder.spawn(closure)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,31 +84,32 @@ impl<'a> Spawner<'a> {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
let read_bytes = pipe.read(&mut buf)?;
|
let read_bytes = pipe.read(&mut buf)?;
|
||||||
|
|
||||||
if read_bytes == 0 {
|
if read_bytes == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("triggering from pipe read");
|
debug!("triggering from pipe read");
|
||||||
|
|
||||||
if clone3(CloneArgs::new(Self::clone_flags(
|
let closure =
|
||||||
&mut spec.permissions.iter(),
|
|| {
|
||||||
)))
|
let pipe_trigger = std::str::from_utf8(&buf[0..read_bytes]).unwrap();
|
||||||
.map_err(|e| Error::Nix {
|
let args = self.prepare_args_ref(name, &spec.args, Some(pipe_trigger));
|
||||||
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));
|
|
||||||
|
|
||||||
unistd::execv(&CString::new(self.binary).unwrap(), &args).map_err(|e| {
|
if let Err(e) = unistd::execv(&CString::new(self.binary).unwrap(), &args)
|
||||||
Error::Nix {
|
.map_err(|e| Error::Nix {
|
||||||
msg: "execv",
|
msg: "execv",
|
||||||
src: e,
|
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
|
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")]
|
#[serde(default = "Arg::default_vec")]
|
||||||
pub args: Vec<Arg>,
|
pub args: Vec<Arg>,
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub permissions: HashSet<Permission>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[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