Derived from Podman's default profile, stripped of capability-conditional rules (we never grant capabilities), argument filters, and the explicit EPERM block. Dangerous syscalls (mount, unshare, ptrace, bpf, perf_event_open, io_uring_*, keyctl, kexec_*, ...) fall through to the default ENOSYS action, which also keeps glibc's clone3 -> clone fallback working. x86_64 and aarch64 are supported; other archs error out. Toggle with --seccomp / --no-seccomp or seccomp = <bool> in config.
93 lines
3.1 KiB
Rust
93 lines
3.1 KiB
Rust
use std::ffi::OsString;
|
|
use std::path::PathBuf;
|
|
|
|
use clap::Parser;
|
|
|
|
#[derive(Parser, Debug, Default)]
|
|
#[command(
|
|
name = "agent-sandbox",
|
|
version,
|
|
about = "Sandbox agentic coding assistants with bubblewrap"
|
|
)]
|
|
pub struct Args {
|
|
/// Blacklist mode: bind / read-only, overlay sensitive paths (default)
|
|
#[arg(long, conflicts_with = "whitelist")]
|
|
pub blacklist: bool,
|
|
|
|
/// Whitelist mode: only explicitly listed minimal paths visible
|
|
#[arg(long)]
|
|
pub whitelist: bool,
|
|
|
|
/// Harden: unshare IPC, PID, UTS; private /tmp, /dev, /run
|
|
#[arg(long, overrides_with = "no_hardened")]
|
|
pub hardened: bool,
|
|
|
|
/// Disable hardening (overrides config-file `hardened = true`)
|
|
#[arg(long, overrides_with = "hardened")]
|
|
pub no_hardened: bool,
|
|
|
|
/// Unshare the network namespace
|
|
#[arg(long, overrides_with = "share_net")]
|
|
pub unshare_net: bool,
|
|
|
|
/// Share the host network namespace (overrides config-file `unshare-net = true`)
|
|
#[arg(long, overrides_with = "unshare_net")]
|
|
pub share_net: bool,
|
|
|
|
/// Enable seccomp syscall filtering (on by default; overrides config-file `seccomp = false`)
|
|
#[arg(long, overrides_with = "no_seccomp")]
|
|
pub seccomp: bool,
|
|
|
|
/// Disable seccomp syscall filtering (overrides config-file `seccomp = true`)
|
|
#[arg(long, overrides_with = "seccomp")]
|
|
pub no_seccomp: bool,
|
|
|
|
/// Bind an extra path read-write (repeatable)
|
|
#[arg(long = "rw", value_name = "PATH", action = clap::ArgAction::Append)]
|
|
pub extra_rw: Vec<PathBuf>,
|
|
|
|
/// Bind an extra path read-only (repeatable)
|
|
#[arg(long = "ro", value_name = "PATH", action = clap::ArgAction::Append)]
|
|
pub extra_ro: Vec<PathBuf>,
|
|
|
|
/// Print the bwrap command without executing
|
|
#[arg(long, overrides_with = "no_dry_run")]
|
|
pub dry_run: bool,
|
|
|
|
/// Disable dry-run (overrides config-file `dry-run = true`)
|
|
#[arg(long, overrides_with = "dry_run")]
|
|
pub no_dry_run: bool,
|
|
|
|
/// Working directory inside the sandbox (default: current directory)
|
|
#[arg(long, value_name = "PATH")]
|
|
pub chdir: Option<PathBuf>,
|
|
|
|
/// Use a named profile from the config file
|
|
#[arg(long, conflicts_with = "no_config")]
|
|
pub profile: Option<String>,
|
|
|
|
/// Path to config file (default: $XDG_CONFIG_HOME/agent-sandbox/config.toml)
|
|
#[arg(long = "config", value_name = "PATH", conflicts_with = "no_config")]
|
|
pub config_path: Option<PathBuf>,
|
|
|
|
/// Skip loading the config file entirely
|
|
#[arg(long)]
|
|
pub no_config: bool,
|
|
|
|
/// Hide a path inside the sandbox with a tmpfs overlay (repeatable)
|
|
#[arg(long = "mask", value_name = "PATH", action = clap::ArgAction::Append)]
|
|
pub mask: Vec<PathBuf>,
|
|
|
|
/// Pass an arbitrary argument directly to bwrap (repeatable)
|
|
#[arg(long = "bwrap-arg", value_name = "ARG", action = clap::ArgAction::Append)]
|
|
pub bwrap_args: Vec<String>,
|
|
|
|
/// Run this binary with the trailing args as its arguments
|
|
#[arg(long, value_name = "CMD")]
|
|
pub entrypoint: Option<String>,
|
|
|
|
/// Command and arguments to run inside the sandbox
|
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
|
pub command_and_args: Vec<OsString>,
|
|
}
|