Cheat sheets

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

Source

The profile ladder#

Factorprocess-fixturefilesystem-fixturecontainer-fixturemicrovm-fixture
Cost1235
Filesystem controlnoyesyesyes
Network denialnonoyesyes
Separate kernelnononoyes
Residualshared-host-kernel, network-not-deniedshared-host-kernel, network-not-deniedshared-host-kernelnone

From solution/stage2.rs, solution/stage4.rs

Run it#

CommandWhat it does
python3 projects/sandbox-ladder/solution/cli.py untrusted=true,secrets=true,network=true,host_kernel=false --budget 3Policy simulation, JSON out
... --docker-image <image>Adds a docker run preview
... --docker-image <image> --executeRuns two probes on a local image
python3 scripts/project_test.py sandbox-ladder --stage 1 --path learning-artifacts/sandbox-ladderGrades stage 1 of your version
python3 scripts/project_test.py sandbox-ladder --all --solution --strictGrades the reference
python3 code/main.pyLesson demo, allow and deny battery
python3 -m pytest code/tests/ -vLesson tests

From sandbox-ladder/README.md, solution/cli.py, Lesson 19.26

Planner steps#

1needsparses key=true and key=false pairs. A repeatedor unknown key fails.2satisfiesuntrusted or secrets need filesystem.network=true needs network denial.3satisfieshost_kernel=true needs a separate kernel.4selectkeeps profiles with cost at most the budget,then takes the lowest cost.5planprints the profile and each residual risk itleaves.6cli.pywraps it as schema_version: 1 JSON withruntime_verified false.

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=false
residual=shared-host-kernelno separate kernel
verify=runtime-configuration-and-escape-testsalways printed
JSON wrapper keys from cli.py
"mode": "policy_simulation", "runtime_verified": false

From solution/stage4.rs, solution/cli.py

The 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_route

From solution/cli.py, sandbox-ladder/README.md

How sbx decides#

Deny wins
a deny beats an allow for the same hostname or CIDR.
deny_kind
implicit means no rule matched. explicit names 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-managed or sbx-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#

Factorstrictbalancedrestrictedautonomous
safe labelaskallowallowallow
destructive labelaskaskdenyallow
unknown labelaskaskdenyallow

From docker-sandboxes-101 §5.5

Lesson runner checks#

all passcaptureclientSandbox.run()argv, shell=Falseprocess_check_executable_denylistbasename ondenylistprocess_check_argv_interpreter-c, -e, --evalprocess_check_shell_metacharsshell metachars,shell offprocess_check_path_jailrealpath under rootprocesssubprocess.runenv allowlist,timeoutstoreSandboxResultdenied, timed_out,truncatedall passcaptureclientSandbox.run()argv, shell=Falseprocess_check_executable_denylistbasename on denylistprocess_check_argv_interpreter-c, -e, --evalprocess_check_shell_metacharsshell metachars, shelloffprocess_check_path_jailrealpath under rootprocesssubprocess.runenv allowlist, timeoutstoreSandboxResultdenied, timed_out,truncated

From 19.26 code/main.py, Lesson 19.26

Lesson defaults#

SettingDefaultNote
max_output_bytes64 * 1024Then the truncation marker
timeout_seconds30.0Demo uses 2.0
DENIED_EXIT_CODE-100Any refusal
TIMED_OUT_EXIT_CODE-101Wall-clock timeout
env_allowlist("PATH", "HOME", "LANG", "LC_ALL", "TERM")All other variables are dropped
shellFalseMetachars refused while off
--budget3Planner cost ceiling

From 19.26 code/main.py, solution/cli.py

sbx network policy tasks#

TaskCode
Set the global preset oncesbx policy init balanced
List rules with their idssbx policy ls --wide --json
Allow one host for one sandboxsbx policy allow network --sandbox <name> example.com
Deny a host at creation--deny-network <host> on create or run
Predict one decisionsbx policy check network --sandbox <name> <host:port> --json
Read what the proxy decidedsbx policy log with --json
Remove an allow rulesbx policy rm network --sandbox <name> --id <uuid> --force
Look for organization rulessbx policy ls --source org

From docker-sandboxes-101 §3.3, docker-sandboxes-101 §3.4, docker-sandboxes-101 §7.2

Refusals and fixes#

SymptomCauseFix
is on the denylistBasename of argv[0] is in DEFAULT_DENYLISTUse another tool or change denylist
use a script file instead of -c/-eInterpreter with an inline code flagWrite the code to a script file
set shell=True to opt inMetachar in argv with shell offSplit the command into argv
resolves outside project root../, an absolute path or a symlink outKeep paths under project_root
Limit error from the plannerNo sufficient profile within the budgetRaise --budget. A kernel needs 5
Docker probe cannot establish independent guest kernel--docker-image with host_kernel=trueUse the policy simulation only
conflicts with existing allow ruleDeny over an allow in one scopesbx policy rm network first
403 Forbidden, 90-byte bodyNo rule matched, approval requiredAdd 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=true grant network?
No. It means network denial is required.
All four needs at budget 4?
Limit. Only microvm-fixture has 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.agentForwardingEnabled for 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