//! # Demo: Chroot //! //! This demo runs the child in a mount namespace, and pivots root into a small chroot. //! //! - We're running `busybox sh` from the chroot, not `sh` from the host. //! - Running `ls / /home` will show the filesystem in the chroot. //! - Running `id` will show `root`. //! - Running `cat /proc/$$/cgroup` will fail, because we don't have `/proc` //! - Touching a file will create it in the chroot. use c_str_macro::c_str; use cordon::{IdMap, MountTable}; use std::ptr; use tracing::info; mod common; pub fn main() -> eyre::Result<()> { common::configure_logging(); let executable_path = c_str!("/bin/sh"); // A `Context` describes the behavior of Cordon's sandboxing. let ctx = cordon::spawn::Context { // The binary to run, arguments, and environment. command: executable_path.as_ptr(), args: vec![executable_path.as_ptr(), ptr::null()], envp: vec![c_str!("ENVVAR=test").as_ptr(), ptr::null()], // Note: We must allocate memory up-front, because we cannot allocate in the children. // Standard IO. stdin_fd: None, stdout_fd: None, stderr_fd: None, // Namespace configuration. namespaces: cordon::spawn::NamespaceSet { user: true, // Unshared at the first fork. mount: true, // Unshared at the second fork, because we need root privileges to do so. ..Default::default() }, // User and group IDs and mappings. set_uid: None, set_gid: None, uid_map: Some(IdMap::self_to_inner_uid(0)), gid_map: Some(IdMap::self_to_inner_gid(0)), // Mounts and chroot. // The `./_root` dir contains a small busybox-based root filesystem. pivot_root_to: Some(c_str!("./_root").as_ptr()), set_working_dir: None, mounts: MountTable::default(), // Systemd integration and cgroup parameters. scope: None, forward_spawn_logs: true, }; // Spawn the child process. let child = unsafe { cordon::spawn::spawn(ctx) }?; // Wait for the child to exit. let exit = child.wait()?; info!(?exit); Ok(()) }