Files
Johnny Greco 854b2370b8 fix(policy): align quickstart, policy skills, and pypi profile with current behavior (#3695)
* fix(examples): add quickstart rule with policy update and show OCSF logs

The quickstart applied policy.yaml with `openshell policy set`, which replaces
the whole policy. The file omitted /bin from the restrictive default, so the
live filesystem additivity check could reject it. Add the rule with
`openshell policy update` instead, and keep policy.yaml as a complete policy
for `sandbox create --policy` that covers the default read-only paths.

The demo and README filtered logs with `--level warn`, but the server ranks
OCSF events as INFO, which hid the policy decisions the demo shows. Query
`--source sandbox` without a level filter and match the OCSF shorthand
(DENIED/ALLOWED) instead of the retired key=value format.

Signed-off-by: Johnny Greco <jogreco@nvidia.com>

* docs(skills): align generate-sandbox-policy with proxy behavior

Remove the `protocol: sql` validation check and the SQL `command` matcher,
which the published policy docs no longer describe.

Correct the private IP guidance: exact user-declared hostnames may reach
private addresses without allowed_ips. Wildcard, hostless, and
advisor-proposed endpoints still need allowed_ips, and loopback, link-local,
unspecified, and cloud metadata addresses stay blocked.

Stop describing an omitted protocol as pure L4 or uninspected. The proxy
still terminates TLS, parses HTTP strictly, and enforces request authority;
it only skips method and path rules.

Signed-off-by: Johnny Greco <jogreco@nvidia.com>

* fix(providers): drop pip script paths from pypi profile binaries

OpenShell identifies a process by /proc/<pid>/exe, so a pip script runs as
its Python interpreter and the .venv/bin/pip entries could never match. The
venv interpreters that run those scripts are already listed, so remove the
script paths and explain in the header that users must list interpreters.

Signed-off-by: Johnny Greco <jogreco@nvidia.com>

* docs(skills): fix openshell-cli policy iteration steps

Monitoring denials with `--level warn` hides them, because the server ranks
OCSF policy events as INFO. Drop the level filter and describe the OCSF
shorthand DENIED lines instead of the retired `action: deny` format.

`policy get --full > file` produced input that `policy set` cannot parse: the
output starts with revision details before the `---` separator, and --full
adds provider-composed rules. Export `--base` and keep only the YAML after the
separator. Also stop recommending full replacement for filesystem, Landlock,
or process changes, which require recreating the sandbox, and drop the SQL
mention that generate-sandbox-policy no longer covers.

Signed-off-by: Johnny Greco <jogreco@nvidia.com>

* docs(skills): qualify authority checks for omitted-protocol endpoints

An endpoint without `protocol` only receives authority checks on HTTP requests
the proxy parses after default TLS handling. Without an L7 route or required
middleware, other CONNECT payloads such as HTTP/2 prior knowledge can use the
raw relay, and `tls: skip` bypasses termination and parsing. Stop describing
omitted-protocol endpoints as always authority-checked.

Signed-off-by: Johnny Greco <jogreco@nvidia.com>

---------

Signed-off-by: Johnny Greco <jogreco@nvidia.com>
2026-09-25 16:34:43 +00:00
..

Sandbox Policy Quickstart

See how OpenShell's network policy system works in under five minutes. You'll create a sandbox, watch a request get blocked by the default-deny policy, apply a fine-grained L7 rule, and verify that reads are allowed while writes are blocked — all without restarting anything.

Prerequisites

  • A running OpenShell gateway (mise run gateway:docker for local development)
  • Docker daemon running

What's in this example

File Description
policy.yaml Complete policy with the same rule, for sandbox create --policy
demo.sh Automated script that runs the full walkthrough non-interactively

Walkthrough

1. Create a sandbox

openshell sandbox create --name demo --no-auto-providers

--no-auto-providers skips the provider setup prompt since this demo doesn't use an AI agent.

You'll land in an interactive shell inside the sandbox:

sandbox@demo:~$

2. Try to reach the GitHub API — blocked

curl -s https://api.github.com/zen

The request fails. By default, all outbound network traffic is denied. The sandbox proxy intercepted the HTTPS CONNECT request to api.github.com:443 and rejected it because no network policy authorizes curl to reach that host.

curl: (56) Received HTTP code 403 from proxy after CONNECT

Exit the sandbox (sandboxes are kept running by default; pass --no-keep at creation time to delete on exit):

exit

3. Check the deny log

openshell logs demo --since 5m --source sandbox

You'll see a line like:

[1775014132.690] [sandbox] [OCSF ] [ocsf] NET:OPEN [MED] DENIED /usr/bin/curl(64) -> api.github.com:443 [policy:- engine:opa] [reason:network connections not allowed by policy]

Every denied connection is logged with the destination, the binary that attempted it, and the reason. Nothing gets out silently.

4. Add a read-only GitHub API rule

openshell policy update demo \
  --rule-name github_api \
  --binary /usr/bin/curl \
  --add-endpoint api.github.com:443:read-only:rest:enforce \
  --wait

The endpoint specification lists the host, port, access preset, protocol, and enforcement mode. curl may make GET, HEAD, and OPTIONS requests to api.github.com over HTTPS. Everything else is denied. rest tells the proxy to terminate TLS and inspect each HTTP request, read-only permits GET, HEAD, and OPTIONS, and enforce blocks every other request. policy update changes only the network rules and keeps the rest of the sandbox's policy.

The command adds a rule equivalent to this YAML in the policy's network_policies section:

network_policies:
  github_api:
    endpoints:
      - host: api.github.com
        port: 443
        protocol: rest
        enforcement: enforce
        access: read-only
    binaries:
      - path: /usr/bin/curl

--wait blocks until the sandbox reports a result for the new policy revision. No restart required — network rules reload while the sandbox runs.

policy.yaml contains the same rule in a complete policy. Use it to start a new sandbox with the rule in place: openshell sandbox create --name demo --policy examples/sandbox-policy-quickstart/policy.yaml. Do not apply it to a running sandbox with openshell policy set, which replaces the entire policy and is rejected if the file drops a filesystem path the sandbox already has.

5. Connect and verify: GET works

openshell sandbox connect demo
curl -s https://api.github.com/zen
Anything added dilutes everything else.

It works. Try a more visual endpoint:

curl -s https://api.github.com/octocat
               MMM.           .MMM
               MMMMMMMMMMMMMMMMMMM
               MMMMMMMMMMMMMMMMMMM      ____________________________
              MMMMMMMMMMMMMMMMMMMMM    |                            |
             MMMMMMMMMMMMMMMMMMMMMMM   | Speak like a human.       |
            MMMMMMMMMMMMMMMMMMMMMMMM   |_   ________________________|
            MMMM::- -:::::::- -::MMMM    |/
             MM~:~ 00~:::::~ 00~:~MM
        .. MMMMM::.00:::+:::.00teleMMM ..
              .MM::::: ._. :::::MM.
                 MMMM;:::::;MMMM
          -MM        MMMMMMM
          ^  M+     MMMMMMMMM
              MMMMMMM MM MM MM
                   MM MM MM MM
                   MM MM MM MM
                .~~MM~MM~MM~MM~~.
             ~~~~MM:~MM~~~MM~:MM~~~~
            ~~~~~~==googler======~~~~~~
             ~~~~~~==googler======
                 :MMMMMMMMMMM:
                 '=googler=='

6. Try a write — blocked by L7

curl -s -X POST https://api.github.com/repos/octocat/hello-world/issues \
  -H "Content-Type: application/json" \
  -d '{"title":"oops"}'

The proxy returns a 403 response with a JSON body that includes fields like these:

{...,"error":"policy_denied",...,"policy":"github_api",...,"rule":"POST /repos/octocat/hello-world/issues",...}

The CONNECT request succeeded (api.github.com is allowed), but the L7 proxy inspected the HTTP method and returned 403. POST is not in the read-only preset. Your agent can read code from GitHub but cannot create issues, push commits, or modify anything.

Exit the sandbox:

exit

7. Check the L7 deny log

openshell logs demo --since 5m --source sandbox
[1775014140.412] [sandbox] [OCSF ] [ocsf] HTTP:POST [MED] DENIED POST http://api.github.com:443/repos/octocat/hello-world/issues [policy:github_api engine:l7] [reason:L7_REQUEST deny POST api.github.com:443/repos/octocat/hello-world/issues reason=POST /repos/octocat/hello-world/issues not permitted by policy]

The log captures the exact HTTP method, path, and matching rule. Policy events are INFO-level log records regardless of their severity, so do not filter them out with --level warn. In production, export these events to your SIEM for a complete audit trail of every request your agent makes.

8. Clean up

openshell sandbox delete demo

What you just saw

State What happens
Default deny All outbound traffic blocked — nothing gets out
L7 read-only GET to api.github.com allowed, POST blocked
Audit trail Every request logged with method, path, decision

The policy hot-reloads in seconds and gives you verifiable, fine-grained control over what your agent can access — without --dangerously-skip-permissions.

Next steps

  • Customize the policy: Change access: read-only to read-write or add explicit rules for specific paths. See the security policy reference.
  • Scope to an agent: Replace the binaries section with your agent's binary (e.g., /usr/local/bin/claude) instead of curl.
  • Add more endpoints: Stack multiple policies in the same file to allow PyPI, npm, or your internal APIs.
  • Try audit mode: Set enforcement: audit to log violations without blocking, useful for building a policy iteratively.