|
| 1 | +# Docker Driver |
| 2 | + |
| 3 | +The Docker compute driver manages sandbox containers through the local Docker |
| 4 | +daemon using the `bollard` client. It targets local developer environments |
| 5 | +where running a full Kubernetes cluster is unnecessary but Docker is already |
| 6 | +available. |
| 7 | + |
| 8 | +The gateway remains a host process. Each sandbox container bind-mounts a Linux |
| 9 | +`openshell-sandbox` supervisor binary and uses Docker host networking so the |
| 10 | +supervisor can connect to a gateway that is listening on host loopback without |
| 11 | +requiring an additional bridge-reachable listener on Linux. |
| 12 | + |
| 13 | +## Source Map |
| 14 | + |
| 15 | +| Path | Purpose | |
| 16 | +|---|---| |
| 17 | +| `crates/openshell-driver-docker/src/lib.rs` | Docker compute driver implementation | |
| 18 | +| `crates/openshell-driver-docker/src/tests.rs` | Unit tests for container spec, env, TLS paths, GPU, resource limits, and cache helpers | |
| 19 | +| `crates/openshell-server/src/cli.rs` | Gateway CLI flags for Docker driver configuration | |
| 20 | +| `crates/openshell-server/src/lib.rs` | In-process Docker compute runtime wiring | |
| 21 | + |
| 22 | +## Runtime Model |
| 23 | + |
| 24 | +```mermaid |
| 25 | +flowchart LR |
| 26 | + CLI["OpenShell CLI<br/>host"] -->|gRPC/HTTP<br/>127.0.0.1:8080| GW["Gateway<br/>host process"] |
| 27 | + GW -->|Docker API| DA["Docker daemon"] |
| 28 | + DA --> C["Sandbox container<br/>network_mode=host"] |
| 29 | + C --> SV["openshell-sandbox<br/>supervisor"] |
| 30 | + SV -->|ConnectSupervisor<br/>OPENSHELL_ENDPOINT| GW |
| 31 | + SV --> NS["Nested sandbox netns<br/>workload + policy proxy"] |
| 32 | +``` |
| 33 | + |
| 34 | +The Docker container itself uses `network_mode = "host"`. This is intentional |
| 35 | +for now: it makes a gateway bound to `127.0.0.1` reachable from the supervisor |
| 36 | +as `127.0.0.1`, matching the host process' endpoint without a bridge listener, |
| 37 | +NAT rule, or userland proxy. |
| 38 | + |
| 39 | +The container also gets a Docker-managed `/etc/hosts` entry for |
| 40 | +`host.openshell.internal` that resolves to `127.0.0.1`. This gives callers a |
| 41 | +stable OpenShell-owned hostname for host services without requiring changes to |
| 42 | +the host machine's hosts file. |
| 43 | + |
| 44 | +The supervisor still creates a nested network namespace for the actual workload |
| 45 | +and routes workload traffic through its policy proxy. Agent network requests are |
| 46 | +enforced by the supervisor in that nested namespace. |
| 47 | + |
| 48 | +## Container Spec |
| 49 | + |
| 50 | +`build_container_create_body()` constructs the Docker container: |
| 51 | + |
| 52 | +| Field | Value | Reason | |
| 53 | +|---|---|---| |
| 54 | +| `image` | Sandbox template image | User-selected runtime image | |
| 55 | +| `user` | `"0"` | Supervisor needs root inside the container for namespace and mount setup | |
| 56 | +| `entrypoint` | `/opt/openshell/bin/openshell-sandbox` | Bind-mounted supervisor binary | |
| 57 | +| `cmd` | Empty vector | Prevents image CMD args from being appended to the supervisor entrypoint | |
| 58 | +| `network_mode` | `"host"` | Lets supervisor connect to host loopback gateway endpoints | |
| 59 | +| `extra_hosts` | `host.openshell.internal:127.0.0.1` | Stable container-local alias for host loopback services | |
| 60 | +| `cap_add` | `SYS_ADMIN`, `NET_ADMIN`, `SYS_PTRACE`, `SYSLOG` | Required for supervisor isolation setup and process inspection | |
| 61 | +| `security_opt` | `apparmor=unconfined` | Docker's default AppArmor profile blocks mount operations required by network namespace setup | |
| 62 | +| `restart_policy` | `unless-stopped` | Resume managed sandboxes after Docker or gateway restarts | |
| 63 | +| `device_requests` | CDI all-GPU request when `spec.gpu` is true | Enables Docker CDI GPU sandboxes when daemon support is detected | |
| 64 | + |
| 65 | +## Gateway Callback |
| 66 | + |
| 67 | +The Docker driver injects `OPENSHELL_ENDPOINT` into each sandbox container from |
| 68 | +`Config::grpc_endpoint` without rewriting it. This is the key difference from a |
| 69 | +bridge-network design. |
| 70 | + |
| 71 | +Examples: |
| 72 | + |
| 73 | +```shell |
| 74 | +OPENSHELL_GRPC_ENDPOINT=http://127.0.0.1:8080 |
| 75 | +``` |
| 76 | + |
| 77 | +and: |
| 78 | + |
| 79 | +```shell |
| 80 | +OPENSHELL_GRPC_ENDPOINT=https://127.0.0.1:8080 |
| 81 | +``` |
| 82 | + |
| 83 | +are passed into the supervisor as-is. Because the container shares the host |
| 84 | +network namespace, `127.0.0.1` resolves to the host loopback interface and the |
| 85 | +gateway is reachable when it binds loopback. |
| 86 | + |
| 87 | +The endpoint can also use the stable alias: |
| 88 | + |
| 89 | +```shell |
| 90 | +OPENSHELL_GRPC_ENDPOINT=http://host.openshell.internal:8080 |
| 91 | +``` |
| 92 | + |
| 93 | +In host network mode this name resolves to `127.0.0.1` inside the container. |
| 94 | + |
| 95 | +For TLS endpoints, the gateway certificate must include the exact endpoint host |
| 96 | +as a subject alternative name. For `https://127.0.0.1:8080`, the certificate |
| 97 | +needs an IP SAN for `127.0.0.1`. For `https://localhost:8080`, it needs a DNS |
| 98 | +SAN for `localhost`. For `https://host.openshell.internal:8080`, it needs a DNS |
| 99 | +SAN for `host.openshell.internal`. Docker sandboxes also require client TLS |
| 100 | +material: |
| 101 | + |
| 102 | +| Env / flag | Purpose | |
| 103 | +|---|---| |
| 104 | +| `OPENSHELL_DOCKER_TLS_CA` / `--docker-tls-ca` | CA certificate mounted at `/etc/openshell/tls/client/ca.crt` | |
| 105 | +| `OPENSHELL_DOCKER_TLS_CERT` / `--docker-tls-cert` | Client certificate mounted at `/etc/openshell/tls/client/tls.crt` | |
| 106 | +| `OPENSHELL_DOCKER_TLS_KEY` / `--docker-tls-key` | Client private key mounted at `/etc/openshell/tls/client/tls.key` | |
| 107 | + |
| 108 | +When `OPENSHELL_GRPC_ENDPOINT` uses `http://`, these TLS mounts are not |
| 109 | +required and providing them is rejected. When it uses `https://`, all three are |
| 110 | +required. |
| 111 | + |
| 112 | +## Environment |
| 113 | + |
| 114 | +`build_environment()` merges template environment, spec environment, and |
| 115 | +driver-controlled keys. Driver-controlled keys win: |
| 116 | + |
| 117 | +| Variable | Value | |
| 118 | +|---|---| |
| 119 | +| `OPENSHELL_ENDPOINT` | Exact configured gateway endpoint | |
| 120 | +| `OPENSHELL_SANDBOX_ID` | Sandbox id | |
| 121 | +| `OPENSHELL_SANDBOX` | Sandbox name | |
| 122 | +| `OPENSHELL_SSH_SOCKET_PATH` | Unix socket path used by the supervisor's embedded SSH daemon | |
| 123 | +| `OPENSHELL_SANDBOX_COMMAND` | `sleep infinity` | |
| 124 | +| `OPENSHELL_TLS_CA` | Mounted CA path when HTTPS is enabled | |
| 125 | +| `OPENSHELL_TLS_CERT` | Mounted client cert path when HTTPS is enabled | |
| 126 | +| `OPENSHELL_TLS_KEY` | Mounted client key path when HTTPS is enabled | |
| 127 | + |
| 128 | +The Docker driver does not inject `OPENSHELL_SSH_HANDSHAKE_SECRET`; the |
| 129 | +supervisor-to-gateway path relies on mTLS for the Docker callback. |
0 commit comments