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

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.

sh $ 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:

```rust 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
    // .ro_bind("/etc", "/myetc")? // --ro-bind /etc:/myetc
    // .rw_bind("/data", "/data")? // --rw-bind /data
    // .limit_cpu(Some(2)) // --limit-cpu 2
    // .limit_walltime(Some(5)) // --limit-walltime 5
    .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.