-
Notifications
You must be signed in to change notification settings - Fork 411
Description
Summary
Enable sandbox containers to operate without a pre-configured policy by supporting three policy resolution modes:
- Policy provided at create time — sandbox loads it from the gateway (current behavior, unchanged)
- Policy null at create time, found on disk — sandbox reads from
/etc/navigator/policy.yaml, syncs it to the gateway, reads it back - Policy null at create time, no disk policy — sandbox uses a hardcoded restrictive default policy, syncs it to the gateway
This allows policies to be shipped directly on container images (baked into /etc/navigator/policy.yaml) or to fall back to a safe restrictive default when no policy is configured at all.
Motivation
Currently, spec.policy is required at sandbox creation time. The CLI always provides one (falling back to the dev default from dev-sandbox-policy.yaml). This works but:
- Prevents container images from shipping their own policies
- Requires the CLI to always know the right policy upfront
- Has no safe fallback if policy is somehow missing
Implementation Plan
Task 1: Add restrictive default policy to navigator-policy
File: crates/navigator-policy/src/lib.rs
Add restrictive_default_policy() that returns a SandboxPolicy with:
- Filesystem: read-only system paths (
/usr,/lib,/proc,/dev/urandom,/app,/etc,/var/log), read-write (/sandbox,/tmp,/dev/null),include_workdir: true - Landlock:
best_effort - Process:
run_as_user: sandbox,run_as_group: sandbox - No network policies (all network blocked)
- No inference
Task 2: Make spec.policy optional in gateway create_sandbox
File: crates/navigator-server/src/grpc.rs
- Remove the
spec.policy.is_none()rejection - In
get_sandbox_policy(), whenspec.policyis None and no policy history exists, returnpolicy: None, version: 0
Task 3: Modify UpdateSandboxPolicy to handle no-baseline case
File: crates/navigator-server/src/grpc.rs
When spec.policy is None:
- Skip static field validation and network mode validation
- Backfill
spec.policyon the stored sandbox with the incoming policy - Proceed with normal version assignment and persistence
Task 4: Pass sandbox name to container via env var
Files:
crates/navigator-server/src/sandbox/mod.rs— AddNEMOCLAW_SANDBOX_NAMEtoapply_required_env()crates/navigator-sandbox/src/main.rs— Add--sandbox-name/NEMOCLAW_SANDBOX_NAMECLI arg
Task 5: Add policy sync capability to sandbox gRPC client
File: crates/navigator-sandbox/src/grpc_client.rs
Add sync_policy() method that calls UpdateSandboxPolicy with the sandbox name and locally-resolved policy.
Task 6: Modify sandbox load_policy() for disk discovery + restrictive default fallback
Files:
crates/navigator-sandbox/src/lib.rs— Modifyload_policy()gRPC branchcrates/navigator-sandbox/Cargo.toml— Addnavigator-policydependency
New gRPC mode flow:
1. Call GetSandboxPolicy
2. If policy returned → use it (unchanged)
3. If policy is None (version 0):
a. Try to read /etc/navigator/policy.yaml
b. If found → parse it, sync to gateway via UpdateSandboxPolicy
c. If not found → use restrictive_default_policy(), sync to gateway
d. Re-fetch from gateway (read-back to get version/hash)
e. Proceed with the fetched policy
Task 7: Tests
navigator-policy tests:
restrictive_default_policy()returns valid policy with no network policiesrestrictive_default_policy()has expected filesystem pathsparse_sandbox_policy()round-trips correctly
navigator-sandbox tests:
- Policy disk discovery reads from
/etc/navigator/policy.yamlwhen present - Falls back to restrictive default when file not found
- Restrictive default produces
NetworkMode::Block
navigator-server tests:
create_sandboxaccepts None policyget_sandbox_policyreturns None/version 0 when no policy configuredupdate_sandbox_policyworks when spec.policy is None (sets baseline)update_sandbox_policywith established baseline still validates static fields
Design Decisions
- Disk path:
/etc/navigator/policy.yaml(standard Linux config location) - Sync flow: Both disk-found and restrictive default get synced to gateway, then re-read for consistency
- Sandbox name: Passed via
NEMOCLAW_SANDBOX_NAMEenv var (needed forUpdateSandboxPolicyRPC) - UpdateSandboxPolicy modification: Skip validation when no baseline exists, backfill spec.policy