#[macro_use]
extern crate log;
use std::path::PathBuf;
use anyhow::{bail, Context};
use serde::{Deserialize, Serialize};
use structopt::StructOpt;
use tabox::configuration::SandboxConfiguration;
use tabox::syscall_filter::SyscallFilter;
use tabox::Result;
use tabox::{Sandbox, SandboxImplementation};
#[derive(Debug, Clone, Serialize, Deserialize, StructOpt)]
#[structopt(
name = "tabox",
about = "Execute code in a secure sandbox",
setting = structopt::clap::AppSettings::ColoredHelp)
]
struct Args {
#[structopt(long, short)]
time_limit: Option<u64>,
#[structopt(long, short)]
memory_limit: Option<u64>,
executable: PathBuf,
args: Vec<String>,
#[structopt(long)]
env: Vec<String>,
#[structopt(long = "mount")]
mount: Vec<String>,
#[structopt(long)]
working_directory: Option<PathBuf>,
#[structopt(long)]
allow_chmod: bool,
#[structopt(long)]
allow_multiprocess: bool,
#[structopt(long, short = "i")]
stdin: Option<PathBuf>,
#[structopt(long, short = "o")]
stdout: Option<PathBuf>,
#[structopt(long, short = "e")]
stderr: Option<PathBuf>,
#[structopt(long)]
allow_insecure: bool,
#[structopt(long, short)]
json: bool,
#[structopt(long)]
mount_tmpfs: bool,
#[structopt(long)]
wall_limit: Option<u64>,
#[structopt(long)]
cpu_core: Option<usize>,
#[structopt(long, default_value = "0")]
pub uid: usize,
#[structopt(long, default_value = "0")]
pub gid: usize,
#[structopt(long)]
pub mount_proc: bool,
}
fn main() -> Result<()> {
env_logger::init();
let args = Args::from_args();
if !SandboxImplementation::is_secure() && !args.allow_insecure {
eprintln!("Your platform doesn't support a secure sandbox!");
eprintln!("Run with --allow-insecure if you really want to execute it anyway");
std::process::exit(1);
}
let mut config = SandboxConfiguration::default();
config
.executable(args.executable)
.mount_tmpfs(args.mount_tmpfs)
.uid(args.uid)
.gid(args.gid)
.mount_proc(args.mount_proc);
if let Some(time_limit) = args.time_limit {
config.time_limit(time_limit);
}
if let Some(memory_limit) = args.memory_limit {
config.memory_limit(memory_limit * 1_000_000);
}
if let Some(wall_limit) = args.wall_limit {
config.wall_time_limit(wall_limit);
}
if let Some(stdin) = args.stdin {
config.stdin(stdin);
}
if let Some(stdout) = args.stdout {
config.stdout(stdout);
}
if let Some(stderr) = args.stderr {
config.stderr(stderr);
}
if let Some(working_directory) = args.working_directory {
config.working_directory(working_directory);
}
if let Some(core) = args.cpu_core {
config.run_on_core(core);
}
for arg in args.args {
config.arg(arg);
}
for el in args.env {
let parts: Vec<&str> = el.splitn(2, '=').collect();
match parts.len() {
1 => {
let name = parts[0];
let value = std::env::var(name).with_context(|| {
format!("Variable {} not present in the environment", parts[0])
})?;
config.env(name, value);
}
2 => {
config.env(parts[0], parts[1]);
}
_ => bail!("Invalid env argument: {}", el),
}
}
for path in args.mount {
let parts: Vec<&str> = path.split(',').collect();
let (local, sandbox, writable) = match parts[..] {
[local] => (local, local, false),
[local, "rw"] => (local, local, true),
[local, sandbox] => (local, sandbox, false),
[local, sandbox, "rw"] => (local, sandbox, true),
[local, sandbox, "ro"] => (local, sandbox, false),
_ => bail!("Invalid mount point: {}", path),
};
debug!(
"Mount {} into {} ({})",
local,
sandbox,
if writable { "rw" } else { "ro" }
);
config.mount(PathBuf::from(local), PathBuf::from(sandbox), writable);
}
config.syscall_filter(SyscallFilter::build(
args.allow_multiprocess,
args.allow_chmod,
));
trace!("Sandbox config {:#?}", config);
let sandbox =
SandboxImplementation::run(config.build()).context("Error running the sandbox")?;
let result = sandbox.wait().context("Error waiting for sandbox result")?;
if args.json {
eprintln!("{}", serde_json::to_string(&result).unwrap());
} else {
eprintln!("{:#?}", result);
}
Ok(())
}