* 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.
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'sCMD/ENTRYPOINTis replaced by the sandbox supervisor at runtime. - Create a
sandboxuser (uid/gid 1000) for non-root execution. - Install
iproute2for full network namespace isolation. - Use a standard Linux base image — distroless and
FROM scratchimages 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>