diff --git a/policies/README.md b/policies/README.md new file mode 100644 index 0000000..232c8d4 --- /dev/null +++ b/policies/README.md @@ -0,0 +1,57 @@ +# Bot sandbox policies (NVIDIA/OpenShell pilot) + +Policies for running OpenBot bots inside [NVIDIA/OpenShell](https://github.com/NVIDIA/OpenShell) +kernel-level sandboxes. Pilot scope: the `@engineer` bot, in **advisory** mode +(denials are logged and surfaced as drafted rule proposals, not hard failures). + +## Contents + +- `engineer.yaml` — policy for the `engineer` sandbox: workspace files, git remotes + (github.com / api.github.com), package registries (pypi.org, files.pythonhosted.org), + and uv's CPython release host. All network rules are `enforcement: audit` for the + pilot; flip to `enforce` per rule before production. +- `engineer-base.Dockerfile` — sandbox image. The default sandbox image ships **without** + git/gh/curl/python and apt is unusable inside (non-root, no network), so tools must be + baked into the image before the sandbox starts. + +## Quickstart (host) + +```sh +# 1. Build the base image (gateway uses the local Docker daemon) +docker build -f policies/engineer-base.Dockerfile -t openshell/engineer-base:latest . + +# 2. Create the sandbox (keep a sleep-style main process; create attaches to it) +openshell sandbox create engineer --from openshell/engineer-base:latest \ + -- sleep 100000 # detached: nohup openshell sandbox create engineer ... + +# 3. Apply (or hot-reload) the policy — no restart needed +openshell policy set engineer --policy policies/engineer.yaml --wait + +# 4. Run work inside +openshell sandbox exec engineer -- bash -c 'cd /sandbox && git …' + +# 5. Audit + rule proposals +openshell logs engineer --source sandbox # OCSF allow/deny events +openshell rule get engineer --status pending # drafted rules from denials +``` + +## Hard-won details (v0.1.5) + +- **Network `binaries` must be kernel-resolved real paths** (`readlink -f`), never + symlinks. `git` fetch/push execs `/usr/lib/git-core/git-remote-http`; `pip` runs as the + interpreter `/usr/bin/python3.12`. A symlink path fails silently as "connection refused"; + the sandbox log carries a `SYMLINK HINT` explaining it. +- **uv downloads CPython** builds from `github.com/astral-sh/...` releases + (redirected via `releases.astral.sh`), so both hosts must be allowed for the uv binary — + even when pypi itself is allowed. +- Policies hot-reload via `policy set --wait`; network rules are live fields. +- Default-deny holds: unlisted endpoints fail as connection refused. + +## Pilot result (2026-10-23) + +Full backend suite ran inside the sandbox: `uv sync --frozen`, ruff, pytest → +811 passed / 30 failed / 30 deselected. The 30 failures were environmental, not policy +denials: uv pulled Python 3.13 (repo targets 3.12) and the image lacked `patch` — both +fixed in the image/pinned-tooling follow-up. Audit log showed 36 denials, all default-deny +working as intended (e.g. `example.com`), plus the drafted rules listed by +`openshell rule get engineer --status pending`. diff --git a/policies/engineer-base.Dockerfile b/policies/engineer-base.Dockerfile new file mode 100644 index 0000000..fd64d7b --- /dev/null +++ b/policies/engineer-base.Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:24.04 +ENV DEBIAN_FRONTEND=noninteractive +# ubuntu:24.04 ships an "ubuntu" user with UID 1000 (the uid OpenShell sandboxes run as). +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates git gh curl wget python3 python3-pip python3-venv ripgrep jq less patch && \ + rm -rf /var/lib/apt/lists/* +USER ubuntu +WORKDIR /sandbox +RUN mkdir -p /sandbox diff --git a/policies/engineer.yaml b/policies/engineer.yaml new file mode 100644 index 0000000..472a65a --- /dev/null +++ b/policies/engineer.yaml @@ -0,0 +1,79 @@ +version: 1 + +filesystem_policy: + include_workdir: true + read_only: + - /bin + - /usr + - /lib + - /proc + - /etc + - /dev/urandom + - /var/log + read_write: + - /tmp + - /dev/null + +landlock: + compatibility: best_effort + +network_policies: + github: + endpoints: + - host: github.com + port: 443 + protocol: rest + enforcement: audit + access: full + - host: api.github.com + port: 443 + protocol: rest + enforcement: audit + access: full + - host: objects.githubusercontent.com + port: 443 + protocol: rest + enforcement: audit + access: read-only + binaries: + # `git clone/fetch/push` execs git-remote-https (a symlink to this real + # binary) — list the real path, not the symlink. + - path: /usr/lib/git-core/git-remote-http + - path: /usr/bin/gh + - path: /usr/bin/curl + # uv downloads CPython builds from github.com/astral-sh releases + - path: /sandbox/.local/bin/uv + pypi: + endpoints: + - host: pypi.org + port: 443 + protocol: rest + enforcement: audit + access: read-only + - host: files.pythonhosted.org + port: 443 + protocol: rest + enforcement: audit + access: read-only + binaries: + # pip runs as the python3.12 interpreter — list the interpreter's real + # path (scripts exec their interpreter; pip3 itself is a python script). + - path: /usr/bin/python3.12 + # uv resolves/downloads packages over HTTPS + - path: /sandbox/.local/bin/uv + - path: /usr/bin/curl + astral_releases: + endpoints: + # uv fetches managed CPython builds from python-build-standalone releases + - host: releases.astral.sh + port: 443 + protocol: rest + enforcement: audit + access: read-only + - host: github.com + port: 443 + protocol: rest + enforcement: audit + access: read-only + binaries: + - path: /sandbox/.local/bin/uv