Hakoniwa
Process isolation for Linux using namespaces, resource limits and seccomp. It
works by creating a new, completely empty, mount namespace where the root is
on a tmpfs that is invisible from the host, and will be automatically cleaned
up when the last process exits. You can then use a policy configuration file or
commandline options to construct the root filesystem and process environment
and command to run in the namespace.
Installation
Cargo
- Install libseccomp by following this guide.
- Install the rust toolchain in order to have cargo installed by following
this guide.
- Run
cargo install hakoniwa-cli.
Usage
CLI
When use commandline, hakoniwa-run will load a default policy configuration named
KISS-policy.toml to ensure a minimal mount namespace created, use --policy-file
to use your custom version.
$ hakoniwa run --verbose -- /bin/bash
[2022-08-21T09:14:11Z INFO hakoniwa::cli::run] Configuration: "KISS-policy.toml"
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "/tmp/hakoniwa-EJemcsRL", container_path: "/"
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "", container_path: "/proc", fstype: "proc"
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "/usr/bin", container_path: "/bin", fstype: "", rw: false
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "/usr/lib", container_path: "/lib", fstype: "", rw: false
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "/usr/lib", container_path: "/lib64", fstype: "", rw: false
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "/usr", container_path: "/usr", fstype: "", rw: false
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "/dev/null", container_path: "/dev/null", fstype: "", rw: true
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "/dev/random", container_path: "/dev/random", fstype: "", rw: false
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "/dev/urandom", container_path: "/dev/urandom", fstype: "", rw: false
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Mount point: host_path: "/dev/zero", container_path: "/dev/zero", fstype: "", rw: false
[2022-08-21T09:14:11Z INFO hakoniwa::executor] UID map: host_id: 5001, container_id: 5001
[2022-08-21T09:14:11Z INFO hakoniwa::executor] GID map: host_id: 1000, container_id: 1000
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Seccomp: disabled
[2022-08-21T09:14:11Z INFO hakoniwa::executor] Execve: /bin/bash ["/bin/bash"]
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.1$ pwd
/
bash-5.1$ ls
bin dev lib lib64 proc usr
bash-5.1$ ls /dev
null random urandom zero
bash-5.1$ ls /proc
1 bus crypto execdomains ioports kmsg locks mtrr scsi sys uptime
4 cgroups devices fb irq kpagecgroup meminfo net self sysrq-trigger version
acpi cmdline diskstats filesystems kallsyms kpagecount misc pagetypeinfo slabinfo sysvipc vmallocinfo
asound config.gz dma fs kcore kpageflags modules partitions softirqs thread-self vmstat
bootconfig consoles driver interrupts key-users latency_stats mounts pressure stat timer_list zoneinfo
buddyinfo cpuinfo dynamic_debug iomem keys loadavg mtd schedstat swaps tty
bash-5.1$ exit
exit
[2022-08-21T09:14:27Z INFO hakoniwa::executor] Result: {"status":"OK","reason":"","exit_code":0,"start_time":"2022-08-21T09:14:11.058546277Z","real_time":{"secs":16,"nanos":460452556},"system_time":{"secs":0,"nanos":8744000},"user_time":{"secs":0,"nanos":3149000},"max_rss":3780}
More examples can be found in hakoniwa-cli/examples.
Rust Library
The code below is almost eq to hakoniwa run --policy-file KISS-policy.toml -- /bin/bash:
use hakoniwa::{Error, Sandbox, SandboxPolicy, Stdio};
fn main() -> Result<(), Error> {
let policy = SandboxPolicy::from_str(
r#"
mounts = [
{ source = "/bin" , target = "/bin" },
{ source = "/lib" , target = "/lib" },
{ source = "/lib64" , target = "/lib64" },
{ source = "/usr" , target = "/usr" },
{ source = "/dev/null" , target = "/dev/null" , rw = true },
{ source = "/dev/random" , target = "/dev/random" },
{ source = "/dev/urandom", target = "/dev/urandom" },
{ source = "/dev/zero" , target = "/dev/zero" },
]
[env]
TERM = {{ os_env "TERM" }}
"#,
)?;
let mut sandbox = Sandbox::new();
sandbox.with_policy(policy);
let prog = std::env::var("SHELL").unwrap_or_else(|_| String::from("/bin/sh"));
let argv = vec![&prog];
let mut executor = sandbox.command(&prog, &argv);
let result = executor
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.stdin(Stdio::inherit())
.run();
dbg!(result);
Ok(())
}
More examples can be found in hakoniwa/examples.
Howto
Acknowledgements
License
Licensed under either of
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.