Sandbox Policy Ladder
Pick the least costly isolation profile, refuse bad calls in a runner, and set sbx network, secret and safety policy.
- Concepts and terms
- Tasks and commands
- Annotated code
- Diagrams
- Comparisons
- Defaults and numbers
- Troubleshooting
- Questions and tips
The profile ladder#
| Factor | process-fixture | filesystem-fixture | container-fixture | microvm-fixture |
|---|---|---|---|---|
| Cost | 1 | 2 | 3 | 5 |
| Filesystem control | no | yes | yes | yes |
| Network denial | no | no | yes | yes |
| Separate kernel | no | no | no | yes |
| Residual | shared-host-kernel, network-not-denied | shared-host-kernel, network-not-denied | shared-host-kernel | none |
Run it#
| Command | What it does |
|---|---|
python3 projects/ | Policy simulation, JSON out |
... --docker-image <image> | Adds a docker run preview |
... --docker-image <image> --execute | Runs two probes on a local image |
python3 scripts/ | Grades stage 1 of your version |
python3 scripts/ | Grades the reference |
python3 code/ | Lesson demo, allow and deny battery |
python3 -m pytest code/ | Lesson tests |
From sandbox-ladder/README.md, solution/cli.py, Lesson 19.26
Planner steps#
From solution/stage1.rs, solution/stage2.rs, solution/stage3.rs, solution/stage4.rs, solution/cli.py, sandbox-ladder/README.md
Planner output for the sample#
profile=container-fixture mode=policy-simulation os_isolation=falseresidual=shared-host-kernelno separate kernelverify=runtime-configuration-and-escape-testsalways printed"mode": "policy_simulation", "runtime_verified": falseThe Docker probe command#
docker run --rm --pull=never \local image only --network none --read-only \no route, read-only root --cap-drop ALL --security-opt no-new-privileges \ --pids-limit 32 --memory 64m --cpus 0.5 \ --user 65534:65534 <image> sh -c '<probes>'root_write, default_routeHow sbx decides#
- Deny wins
- a deny beats an allow for the same hostname or CIDR.
- deny_kind
implicitmeans no rule matched.explicitnames the rule.- check network
- host and port only, port 443 by default. It exits 1 on a denial.
- forward-bypass
- a tunnel with no inspection and no credential injection.
- Secrets
- the agent sees
proxy-managedorsbx-cs-<rand>, never the value. - SSH agent
- forwarding is on by default. Keys stay on the host.
- Governance
- only organization allow rules grant. Local deny rules still apply.
- Boundary
- the hypervisor, not in-VM privilege separation.
From docker-sandboxes-101 §3.3, docker-sandboxes-101 §3.4, docker-sandboxes-101 §3.5, docker-sandboxes-101 §7.2, docker-sandboxes-101 §3.1
Safety modes for unmatched calls#
| Factor | strict | balanced | restricted | autonomous |
|---|---|---|---|---|
safe label | ask | allow | allow | allow |
destructive label | ask | ask | deny | allow |
unknown label | ask | ask | deny | allow |
Lesson runner checks#
Lesson defaults#
| Setting | Default | Note |
|---|---|---|
max_output_bytes | 64 * 1024 | Then the truncation marker |
timeout_seconds | 30.0 | Demo uses 2.0 |
DENIED_EXIT_CODE | -100 | Any refusal |
TIMED_OUT_EXIT_CODE | -101 | Wall-clock timeout |
env_allowlist | ("PATH", | All other variables are dropped |
shell | False | Metachars refused while off |
--budget | 3 | Planner cost ceiling |
sbx network policy tasks#
| Task | Code |
|---|---|
| Set the global preset once | sbx policy init balanced |
| List rules with their ids | sbx policy ls --wide --json |
| Allow one host for one sandbox | sbx policy allow network --sandbox <name> example.com |
| Deny a host at creation | --deny-network <host> on create or run |
| Predict one decision | sbx policy check network --sandbox <name> <host:port> --json |
| Read what the proxy decided | sbx policy log with --json |
| Remove an allow rule | sbx policy rm network --sandbox <name> --id <uuid> --force |
| Look for organization rules | sbx policy ls --source org |
From docker-sandboxes-101 §3.3, docker-sandboxes-101 §3.4, docker-sandboxes-101 §7.2
Refusals and fixes#
| Symptom | Cause | Fix |
|---|---|---|
is on the denylist | Basename of argv[0] is in DEFAULT_DENYLIST | Use another tool or change denylist |
use a script file instead of -c/ | Interpreter with an inline code flag | Write the code to a script file |
set shell= | Metachar in argv with shell off | Split the command into argv |
resolves outside project root | ../, an absolute path or a symlink out | Keep paths under project_root |
Limit error from the planner | No sufficient profile within the budget | Raise --budget. A kernel needs 5 |
Docker probe cannot establish independent guest kernel | --docker-image with host_kernel=true | Use the policy simulation only |
conflicts with existing allow rule | Deny over an allow in one scope | sbx policy rm network first |
403 Forbidden, 90-byte body | No rule matched, approval required | Add an allow rule. It is not a network fault |
From 19.26 code/main.py, solution/cli.py, solution/stage3.rs, solution/main.rs, docker-sandboxes-101 §3.3, docker-sandboxes-101 §3.4
Q&A#
- Does
network=truegrant network? - No. It means network denial is required.
- All four needs at budget 4?
Limit. Onlymicrovm-fixturehas a kernel, at cost 5.- Does the Docker probe prove a microVM?
- No. It tests root write and the default route only.
- Is the lesson sandbox an OS boundary?
- No. It uses no namespaces, cgroups or seccomp.
- Are agent permissions a boundary?
- No. They run client-side. Use a sandbox for isolation.
From solution/main.rs, solution/stage2.rs, sandbox-ladder/README.md, solution/cli.py, Lesson 19.26, docker-sandboxes-101 §5.5
Tips#
- Run unattended agents with
--safety restricted. - Put destructive commands in
deny. No mode or hook allow overrides it. - Store keys with
sbx secret set, never with-e KEY. - Turn off
ssh.agentForwardingEnabledfor an agent you would not let sign. - A host allow passes any body. Narrow an API with a kit HTTP rule.
- In production, add a container or microVM, drop capabilities, mount the root read-only.
From Lesson 19.26, docker-sandboxes-101 §5.5, docker-sandboxes-101 §3.5, docker-sandboxes-101 §3.4