Last active
August 30, 2021 01:59
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} | |
} |
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
Depends on