Skip to content

Instantly share code, notes, and snippets.

@sighingnow
Last active August 30, 2021 01:59
Show Gist options
  • Save sighingnow/460987887ef2c1bb809e47b25b8981ff to your computer and use it in GitHub Desktop.
Save sighingnow/460987887ef2c1bb809e47b25b8981ff to your computer and use it in GitHub Desktop.
Rust version of "Rootless Containers From Scratch" by Liz Rice, https://www.youtube.com/watch?v=jeTKgAEyhsA
use std;
use std::env;
use std::fs;
use std::io::Write;
use nix::mount;
use nix::sched;
use nix::sched::CloneFlags;
use nix::sys::signal::Signal;
use nix::sys::wait::waitpid;
use nix::unistd;
fn run(args: &[String]) {
println!(
"Running run {:?} as {}, user is {}",
args,
unistd::getpid(),
unistd::geteuid()
);
let clone_flags = CloneFlags::CLONE_NEWUSER
| CloneFlags::CLONE_NEWUTS
| CloneFlags::CLONE_NEWPID
| CloneFlags::CLONE_NEWNS;
const STACK_SIZE: usize = 1024 * 1024;
let stack: &mut [u8; STACK_SIZE] = &mut [0; STACK_SIZE];
let (r, w) = unistd::pipe().unwrap();
let proc = sched::clone(
Box::new(|| {
{
let mut buffer = [0u8; std::mem::size_of::<i32>()];
unistd::read(r, &mut buffer).unwrap();
println!(
"container process id from parent: {}",
i32::from_ne_bytes(buffer)
);
}
child(args);
return 0;
}),
stack,
clone_flags,
Some(Signal::SIGCHLD as i32),
)
.expect("Failed to create container process");
// uid, gid mapping
{
println!("container process created: {}", proc);
append_user_mapping(format!("{}", proc));
unistd::write(w, &proc.as_raw().to_ne_bytes()).unwrap();
}
waitpid(proc, None).unwrap();
println!("Finishes run ...");
}
fn mount_sys_fs() {
const NONE: Option<&'static [u8]> = None;
mount::mount(
Some("proc"),
"/proc",
Some("proc"),
mount::MsFlags::empty(),
NONE,
)
.expect("Failed to mount the /proc");
}
fn append_user_mapping(pid: String) {
println!(
"update user mapping: write to {} from process {}",
pid,
unistd::getpid()
);
let mut uid_mapping = fs::OpenOptions::new()
.create(true)
.write(true)
.append(false)
.open(format!("/proc/{}/uid_map", pid))
.unwrap();
if let Err(e) = write!(&mut uid_mapping, "0 1000 1\n") {
panic!("Could not update the uid mapping: {}", e);
}
}
fn child(args: &[String]) {
println!(
"Running child {:?} as {}, user is {}",
args,
unistd::getpid(),
unistd::geteuid()
);
// unshare
let unshare_flags = CloneFlags::CLONE_NEWNS;
sched::unshare(unshare_flags).unwrap();
// mount sys fs: proc, etc.
mount_sys_fs();
unistd::sethostname("container").unwrap();
let exitcode = std::process::Command::new(args[0].as_str())
.args(args.get(1..).unwrap_or_default())
.spawn()
.expect("Failed to execute container command")
.wait()
.unwrap();
match exitcode.code() {
Some(code) => {
if code != 0 {
panic!("Exit with code {}", code);
}
}
None => (),
}
println!("Finishes child ...");
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
panic!("Require command to run")
}
match args[1].as_str() {
"run" => run(args.get(2..).unwrap_or_default()),
"child" => child(args.get(2..).unwrap_or_default()),
cmd => {
panic!("Unsupported command: {}", cmd)
}
}
}
@sighingnow
Copy link
Author

sighingnow commented Aug 29, 2021

Depends on

[dependencies]
nix = "0.22.1"

@sighingnow
Copy link
Author

See also "Containers From Scratch" in Rust: https://gist.github.com/sighingnow/4988a0100bc5030d301926f79254133a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment