Skip to content

Latest commit

 

History

History
223 lines (173 loc) · 8.43 KB

File metadata and controls

223 lines (173 loc) · 8.43 KB

CLI Guide

sandlock run confines one command. Filesystem access is default-deny: -r PATH grants read access, -w PATH grants read and write access. Every other option layers a further restriction on top. The full field-by-field reference, shared with profiles and the Python SDK, is in sandbox-reference.md.

Examples

# Basic confinement
sandlock run -r /usr -r /lib -w /tmp -- ls /tmp

# Interactive shell (the sandboxed command inherits the terminal)
sandlock run -r /usr -r /lib -r /lib64 -r /bin -r /etc -w /tmp -- /bin/sh

# Resource limits + timeout
sandlock run -m 512M -P 20 -t 30 -- ./compute.sh

# GPU access (NVIDIA): `--gpu all` for every GPU, or indices like `0,2`.
# Selection is a hard Landlock boundary: only the chosen /dev/nvidiaN nodes
# are openable, so a sandbox given `--gpu 0` cannot touch other GPUs. The
# NVIDIA userspace also needs the driver libraries readable and writes thread
# names under /proc/self/task.
sandlock run --gpu 0 \
  -r /usr -r /lib -r /lib64 -r /etc -r /sys -r /proc -w /proc/self/task \
  -- python3 train.py

# Outbound allowlist: restrict to one host on one port
sandlock run --net-allow api.openai.com:443 -r /usr -r /lib -r /etc -- python3 agent.py

# Multiple ports for one host, plus a separate any-IP port
sandlock run --net-allow github.com:22,443 --net-allow :8080 \
  -r /usr -r /lib -r /etc -- python3 agent.py

# Wildcard port (optional): a bare `host` (or `host:*`) permits every port
sandlock run --net-allow github.com -r /usr -r /lib -r /etc -- ssh user@github.com

# IP, CIDR range, or IPv6 literal as the target (matched by containment,
# no DNS); same grammar as --net-deny
sandlock run --net-allow 10.0.0.0/8:443 --net-allow '[2606:4700::/32]:443' \
  -r /usr -r /lib -r /etc -- python3 agent.py

# Unrestricted outbound: `*` opens any host and any port over both TCP
# and UDP (`:*` / `*:*` are equivalent). ICMP still needs `icmp://`.
sandlock run --net-allow '*' \
  -r /usr -r /lib -r /etc -- ./client

# Scheme prefix: a spec with no scheme covers TCP and UDP; `tcp://` or
# `udp://` pins one protocol (e.g. UDP DNS to 1.1.1.1 only, plus
# TCP-only HTTPS to anywhere)
sandlock run --net-allow udp://1.1.1.1:53 --net-allow tcp://:443 \
  -r /usr -r /lib -r /etc -- ./client

# Ping: kernel ping socket (SOCK_DGRAM) gated by net.ipv4.ping_group_range
sandlock run --net-allow icmp://github.com -r /usr -r /lib -r /etc -- ping github.com

# Denylist: default-allow networking, block specific IPs/CIDRs/ports
# (inverse of --net-allow; mutually exclusive with it). Port is optional.
sandlock run --net-deny 169.254.169.254 --net-deny 10.0.0.0/8 \
  -r /usr -r /lib -r /etc -- python3 agent.py

# HTTP-level ACL (method + host + path rules via transparent proxy)
# HTTP rules with concrete hosts auto-extend --net-allow with host:80,443
sandlock run \
  --http-allow "GET docs.python.org/*" \
  --http-allow "POST api.openai.com/v1/chat/completions" \
  --http-deny "* */admin/*" \
  -r /usr -r /lib -r /etc -- python3 agent.py

# HTTPS MITM, zero-config: sandlock generates an ephemeral CA and splices it
# into the trust bundle(s) you name. No openssl, no manual install.
sandlock run \
  --http-allow "POST api.openai.com/v1/*" \
  --http-inject-ca /etc/ssl/certs/ca-certificates.crt \
  -r /usr -r /lib -r /etc -- python3 agent.py

# Node and other runtimes with a compiled-in CA list: export the cert and
# wire the runtime's own env var.
sandlock run \
  --http-allow "POST api.example.com/*" \
  --http-inject-ca /etc/ssl/certs/ca-certificates.crt \
  --http-ca-out /tmp/sandlock-ca.pem \
  --env NODE_EXTRA_CA_CERTS=/tmp/sandlock-ca.pem \
  -r /usr -r /lib -r /etc -- node agent.js

# HTTPS MITM with your own CA (still supported)
sandlock run \
  --http-allow "POST api.openai.com/v1/*" \
  --http-ca ca.pem --http-key ca-key.pem \
  -r /usr -r /lib -r /etc -- python3 agent.py

# Credential injection: the secret lives in the supervisor, the child never sees
# it. sandlock attaches it in the proxy AFTER the ACL check. Over HTTPS the value
# is encrypted to the upstream; over cleartext HTTP sandlock warns (the secret
# would be exposed on the wire).
sandlock run \
  --http-allow "POST api.openai.com/v1/*" \
  --http-inject-ca /etc/ssl/certs/ca-certificates.crt \
  --credential openai=env:OPENAI_API_KEY \
  --http-auth "POST api.openai.com/* bearer openai" \
  -r /usr -r /lib -r /etc -- python3 agent.py

# Server listening on ports (Landlock --net-allow-bind, separate from --net-allow;
# accepts comma-separated ports and lo-hi ranges, repeatable)
sandlock run --net-allow-bind 8080,9000-9005 -r /usr -r /lib -r /etc -- python3 server.py

# Clean environment
sandlock run --clean-env --env CC=gcc \
  -r /usr -r /lib -w /tmp -- make

# Deterministic execution (frozen time + seeded randomness)
sandlock run --time-start "2000-01-01T00:00:00Z" --random-seed 42 -- ./build.sh

# Port virtualization (multiple sandboxes can bind the same port)
sandlock run --port-remap --net-allow-bind 6379 -r /usr -r /lib -r /etc -- redis-server --port 6379

# Port virtualization with named sandboxes (enables network discovery)
sandlock run --name api.local --port-remap --net-allow-bind 8080 -r /usr -r /lib -r /etc -- python3 server.py
sandlock run --name web.local --port-remap --net-allow-bind 8080 -r /usr -r /lib -r /etc -- python3 server.py

# List all running sandboxes (with uptime and command)
sandlock ps

# Show effective policy for a running sandbox (JSON or TOML)
sandlock inspect api.local
sandlock inspect api.local --toml

# Kill a running sandbox by name
sandlock kill web.local

# Chroot with per-sandbox mount (no kernel bind mount needed)
sandlock run --chroot ./rootfs --fs-mount /work:/tmp/sandbox/work -- /bin/sh

# COW filesystem (writes captured, committed on success)
sandlock run --workdir /opt/project -r /usr -r /lib -- python3 task.py

# Dry-run (show what files would change, then discard)
sandlock run --dry-run --workdir . -w . -r /usr -r /lib -r /bin -r /etc -- make build

# Use a saved profile
sandlock run -p build -- make -j4

# No-supervisor mode (Landlock + deny-only seccomp, no supervisor process)
sandlock run --no-supervisor -r /usr -r /lib -r /lib64 -r /bin -w /tmp -- ./script.sh

# Nested sandboxing: confine sandlock's own supervisor
sandlock run --no-supervisor -r /proc -r /usr -r /lib -r /lib64 -r /bin -r /etc -w /tmp -- \
  sandlock run -r /usr -w /tmp -- untrusted-command

Profiles

Save reusable sandbox profiles as TOML files in ~/.config/sandlock/profiles/. Profiles use a sectioned schema; top-level flat keys such as fs_readable = [...] are rejected. Pass a sandbox instance name with --name when you need a stable virtual hostname.

# ~/.config/sandlock/profiles/build.toml
[program]
exec = "make"
args = ["-j4"]
clean_env = true
env = { CC = "gcc", LANG = "C.UTF-8" }

[filesystem]
read = ["/usr", "/lib", "/lib64", "/bin", "/etc", "${HOME}/.cargo"]
write = ["/tmp/work"]

[limits]
memory = "512M"
processes = 50

[syscalls]
extra_deny = []

Path fields expand ${HOME}, so the same profile works on machines with different usernames. ${HOME} is the only variable; anything else, including a leading ~, is a load error. See sandbox-reference.md for the full grammar.

sandlock profile list
sandlock profile show build
sandlock run -p build        # uses [program].exec + args
sandlock run -p build -- make test  # trailing command overrides [program]

Managing running sandboxes

sandlock ps lists every running sandbox with its uptime, port mappings, and command. sandlock inspect NAME prints the effective policy as JSON (--toml prints it in profile syntax). sandlock kill NAME stops a sandbox and its supervisor.

$ sandlock ps
NAME                                  PID        UPTIME  STATUS      PORTS                     CMD
api.local                           12345            5m  running     8080→41235                python3 server.py
web.local                           12346            3m  running     8080→41236                python3 server.py

$ sandlock inspect api.local --toml | head -10
[config]
http_inject_ca = []

[determinism]
...

$ sandlock kill web.local
Killed sandbox 'web.local' (child PID 12346, supervisor PID 12340)

Ports show as virtual→real when --port-remap is active; see network.md.

Generating a profile

sandlock learn runs a workload under observation and emits a profile covering the paths, network endpoints, and resource peaks it actually used. See learn.md.