Files
John T. Myers d3e1b31db9 feat(sandbox): log connection attempts that bypass proxy path (#326)
* feat(sandbox): log connection attempts that bypass proxy path

Add iptables LOG + REJECT rules inside the sandbox network namespace to
detect and diagnose direct connection attempts that bypass the HTTP
CONNECT proxy. This provides two improvements:

1. Fast-fail UX: applications get immediate ECONNREFUSED instead of a
   30-second timeout when they bypass the proxy
2. Diagnostics: a /dev/kmsg monitor emits structured BYPASS_DETECT
   tracing events with destination, protocol, process identity, and
   actionable hints

Both TCP and UDP bypass attempts are covered (UDP catches DNS bypass).
The feature degrades gracefully if iptables or /dev/kmsg are unavailable.

Closes #268

* chore: track .python-version to pin Python 3.13.12 for uv

The sandbox base image runs Python 3.13. A stale venv on 3.12 causes
all exec_python E2E tests to fail because cloudpickle bytecode is not
compatible across minor versions.

* fix(cluster): preserve hostGatewayIP across fast deploys

The fast deploy's helm upgrade was missing the hostGatewayIP value that
the bootstrap entrypoint injects into the HelmChart CR. This caused
host.openshell.internal hostAliases to be lost from the gateway pod
and sandbox pods after any fast deploy, breaking host gateway routing.

Read the IP from the HelmChart CR and pass it through to helm upgrade.

* wip: fix iptables path resolution, use dmesg for kmsg, add CAP_SYSLOG

* fix(sandbox): restore NetworkNamespace Drop impl, remove dead kmsg code

The Drop impl for NetworkNamespace was accidentally deleted during the
bypass detection refactor, which would cause network namespaces and
veth interfaces to leak on every sandbox shutdown.

Also removes dead kmsg volume/mount code (bypass monitor uses dmesg
instead of direct /dev/kmsg access) and removes an accidentally
committed session transcript file.
2026-03-16 00:32:13 -07:00
..

Bring Your Own Container

Run a sandbox with a custom container image. This example includes a ready-to-use Python REST API that you can build, deploy, and reach from your local machine through port forwarding.

Prerequisites

  • A running OpenShell gateway (openshell gateway start)
  • Docker daemon running

What's in this example

File Description
Dockerfile Builds a Python 3.12 image that starts a REST API
app.py Minimal HTTP server with /hello and /health routes

Quick start

1. Create a sandbox from the Dockerfile with port forwarding

openshell sandbox create \
    --from examples/bring-your-own-container/Dockerfile \
    --forward 8080 \
    -- python /sandbox/app.py

The --from flag accepts a Dockerfile path. The CLI builds the image, pushes it into the cluster, and creates the sandbox in one step.

The --forward 8080 flag opens an SSH tunnel so localhost:8080 on your machine reaches the REST API inside the sandbox.

Important: The image's CMD / ENTRYPOINT does not run automatically. OpenShell replaces it with the sandbox supervisor (which manages SSH access, network policy, etc.). You must pass your application's start command after -- so it is executed via SSH once the sandbox is ready.

2. Hit the API

curl http://localhost:8080/hello
# {"message": "hello from OpenShell sandbox!"}

curl http://localhost:8080/hello/world
# {"message": "hello, world!"}

curl http://localhost:8080/health
# {"status": "ok"}

Running your own app

Replace app.py and the Dockerfile with your own application. The key requirements are:

  • Pass your start command explicitly — use -- <command> on the CLI. The image's CMD / ENTRYPOINT is replaced by the sandbox supervisor at runtime.
  • Create a sandbox user (uid/gid 1000) for non-root execution.
  • Install iproute2 for full network namespace isolation.
  • Use a standard Linux base image — distroless and FROM scratch images are not supported.

TODO(#70): Remove the sandbox user note once custom images are secure by default without requiring manual setup.

How it works

OpenShell handles all the wiring automatically. You build a standard Linux container image — no OpenShell-specific dependencies or configuration required. When you create a sandbox with --from, OpenShell ensures that sandboxing (network policy, filesystem isolation, SSH access) works the same as with the default image.

Port forwarding is entirely client-side: the CLI spawns a background ssh -L tunnel through the gateway. The sandbox's embedded SSH daemon bridges the tunnel to 127.0.0.1:<port> inside the container.

Cleanup

Delete the sandbox when you're done (this also stops port forwards):

openshell sandbox delete <sandbox-name>