You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit f3fd9d9
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: crates/openshell-driver-docker/README.md
+53-31Lines changed: 53 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,18 +29,31 @@ no candidate responds.
29
29
30
30
## Runtime Model
31
31
32
-
The gateway runs as a host process. The Docker driver creates one container per
33
-
sandbox and starts the `openshell-sandbox` supervisor inside that container. The
34
-
supervisor then creates the nested sandbox namespace for the agent process.
32
+
The gateway and Docker driver run on the host. For each sandbox, the driver
33
+
starts `openshell-sandbox --mode=control` beside the gateway and
34
+
`openshell-sandbox --mode=boundary` as PID 1 in the workload container. The two
35
+
roles use the shared RFC 0012 protocol over a private bind-mounted Unix socket.
36
+
37
+
Control owns the gateway session, policy, SSH relay, and network proxy. The
38
+
boundary owns the process tree, exec/signal/wait/PTY operations, loopback port
39
+
forwarding, procfs binary identity, and the workload network namespace. The
40
+
boundary receives no gateway JWT, client TLS private key, or policy authority.
41
+
42
+
The driver bind-mounts the boundary socket from a short, digest-keyed runtime
43
+
directory under `/tmp/openshell-docker-boundaries`. This keeps the host endpoint
44
+
below Linux's Unix-socket path limit even when the gateway state directory is
45
+
deeply nested. The full protected topology and gateway material remain in the
46
+
normal driver state directory, and sandbox cleanup removes both locations.
35
47
36
48
## Stop and Start
37
49
38
-
Stop stops the managed container without removing it. Docker retains the
39
-
container writable layer, attached volumes, labels, token material, and restart
40
-
policy. Start starts that same container, so files in the resolved OCI
41
-
workspace remain available. A durably stopped sandbox is excluded from
42
-
gateway startup recovery and stays stopped across gateway restarts. Delete
43
-
continues to force-remove the container and clean up driver-owned material.
50
+
Stop terminates host control and stops the managed container without removing
51
+
it. Docker retains the container writable layer, attached volumes, labels, and
52
+
the protected driver topology. Start starts that same container and recreates
53
+
host control, so files in the resolved OCI workspace remain available. A
54
+
durably stopped sandbox is excluded from gateway startup recovery and stays
55
+
stopped across gateway restarts. Delete force-removes the container and the
56
+
driver-owned boundary directory.
44
57
Graceful gateway shutdown sends `StopSandbox` for each sandbox whose persisted
45
58
phase requires running compute without changing that persisted intent. On
46
59
startup, the gateway sends an idempotent `StartSandbox` request for the same
@@ -50,9 +63,9 @@ remain excluded.
50
63
Before creating the container, the driver inspects the final sandbox image and
51
64
captures its immutable image ID, raw OCI `Config.User`, and OCI
52
65
`Config.WorkingDir`. Container creation uses that image ID, preventing a
53
-
mutable tag from changing between inspection and launch. The supervisor runs as
54
-
root, resolves omitted policy identity fields from the image declaration, and
55
-
drops only agent children to the resulting identity. Named OCI components
66
+
mutable tag from changing between inspection and launch. The boundary runs as
67
+
root, resolves omitted policy identity fields from the image declaration inside
68
+
the image filesystem, and drops only agent children to the resulting identity. Named OCI components
56
69
remain names after validation; a missing group is filled with the user's
57
70
numeric primary GID. Explicit `process.run_as_user` and
58
71
`process.run_as_group` values take precedence independently.
@@ -83,14 +96,12 @@ are rejected, as are paths that overlap concrete OpenShell control resources.
83
96
The workspace is the child cwd and `HOME`. The supervisor starts from `/`, then
84
97
reports an invalid workdir as a readiness failure.
85
98
86
-
Docker containers join an OpenShell-managed bridge network. The driver injects
87
-
`host.openshell.internal` and `host.docker.internal` so supervisors have stable
88
-
names for reaching the gateway host. On Docker Desktop, Colima, Rancher
89
-
Desktop, OrbStack, and macOS-hosted gateways, those names use Docker's
90
-
`host-gateway` alias. The driver requests a separate IPv4 loopback callback
91
-
listener when the primary listener does not already cover it. On native Linux
92
-
Docker, the gateway also binds the bridge gateway IP so containers can call
93
-
back to the host process.
99
+
Boundary containers use Docker `network_mode=none`. The nested agent network
100
+
namespace can reach only the boundary's mediation listener. Each accepted TCP
101
+
stream and its procfs-derived executable identity cross the private Unix socket
102
+
to host control, which applies policy before dialing the destination. Host
103
+
control reaches the gateway directly; the existing managed bridge listener is
104
+
retained for compatibility with legacy containers created before this split.
94
105
95
106
## Container Contract
96
107
@@ -100,13 +111,15 @@ contract:
100
111
| Setting | Purpose |
101
112
|---|---|
102
113
|`user = "0"`| The supervisor needs root inside the container to prepare namespaces, mounts, Landlock, and seccomp. |
103
-
|`network_mode = openshell`|Places the supervisor on the managed Docker bridge network. |
114
+
|`network_mode = none`|Removes direct container egress; the nested workload namespace can emit only through the authenticated boundary stream. |
104
115
|`cap_add`| Grants supervisor-only capabilities required for namespace setup and process inspection. |
105
116
|`apparmor=unconfined`| Avoids Docker's default profile blocking required mount operations. |
106
117
|`restart_policy = no`| A canonical main-process exit remains terminal and is not silently restarted by Docker. |
107
118
|`PidsLimit`| Enforces the sandbox PID budget at the Docker cgroup layer. Set `[openshell.drivers.docker].sandbox_pids_limit = 0` to inherit the Docker/runtime default. |
108
119
| CDI GPU request | Uses opaque `driver_config.cdi_devices` values when set; otherwise selects the requested count of NVIDIA CDI GPUs in round-robin order when daemon CDI support is detected. Docker daemon `/info` can permit `nvidia.com/gpu=all` as a WSL2 all-only compatibility fallback, where it counts as one selectable device. Exact CDI device lists must not contain duplicates and must match the effective GPU count. |
109
-
|`policy-dns-transparent-tcp` capability | Declares that the combined Docker supervisor can own namespace-local DNS/TCP capture and coupled workload restart. The shared supervisor still owns DNS eligibility, mappings, authorization, pinned dialing, relaying, and OCSF decisions. The marker is stripped from the workload environment. |
120
+
| Read-only supervisor/runtime mounts | Supplies the boundary binary and trusted `ip`/`nft` helper runtime without trusting workload-image tools. |
121
+
| Private boundary bind mount | Carries the protected boundary config and Unix control socket. The directory is host-owned mode `0700`; workload children cannot access it. |
122
+
|`policy-dns-transparent-tcp` capability | Declares that the shared boundary can own namespace-local DNS/TCP capture while host control retains authorization, pinned dialing, relaying, and OCSF decisions. The marker is not exposed to the workload environment. |
110
123
111
124
The agent child process does not retain these supervisor privileges.
112
125
@@ -127,8 +140,9 @@ mount types:
127
140
128
141
Host bind mounts are disabled by default because they expose gateway host
129
142
paths to sandbox requests. Image mounts are not part of the Docker
130
-
driver-config schema. The driver still uses internal bind mounts for
131
-
OpenShell-owned supervisor, token, and TLS material.
143
+
driver-config schema. The driver still uses internal bind mounts for the
144
+
OpenShell-owned boundary binary, trusted helper runtime, and private socket
145
+
directory. Gateway token and TLS material remain on the host.
132
146
133
147
Docker `bind` mounts accept `source`, `target`, optional `read_only`, and an
134
148
optional `selinux_label` of `shared` (applies `:z`) or `private` (applies
@@ -154,8 +168,9 @@ openshell sandbox create \
154
168
155
169
## Supervisor Binary Resolution
156
170
157
-
The Docker driver bind-mounts a host-side Linux `openshell-sandbox` binary into
158
-
each sandbox container. Resolution order is:
171
+
The Docker driver bind-mounts a host-side Linux `openshell-sandbox` boundary
172
+
binary and trusted helper runtime into each sandbox container. Resolution order
173
+
for the Linux artifact is:
159
174
160
175
1.`supervisor_bin` in `[openshell.drivers.docker]`.
161
176
2.`supervisor_image` in `[openshell.drivers.docker]`, extracting
@@ -164,22 +179,28 @@ each sandbox container. Resolution order is:
164
179
4. A local Linux cargo target build for the Docker daemon architecture.
165
180
5. The release-matched default supervisor image, extracting `/openshell-sandbox`.
166
181
182
+
When a selected binary has no valid sibling `openshell-runtime`, the driver
183
+
extracts `/openshell-runtime` from the configured or release-matched supervisor
184
+
image. Host control uses the native `openshell-sandbox` beside the gateway (or a
185
+
local native cargo build); Linux hosts can reuse the boundary binary.
186
+
167
187
Release and Docker-image gateway builds bake the matching supervisor image tag
168
188
into the binary at compile time. The default Docker supervisor image is not
169
189
`:latest` unless a custom build explicitly sets that tag.
170
190
171
191
## Callback and TLS
172
192
173
-
`OPENSHELL_ENDPOINT` is injected from the gateway's configured gRPC endpoint.
193
+
`OPENSHELL_ENDPOINT` is injected only into host control from the gateway's
194
+
configured gRPC endpoint.
174
195
When no endpoint is configured, the driver uses
175
196
`host.openshell.internal:<gateway-port>` with the appropriate HTTP or HTTPS
176
197
scheme. Set `host_gateway_ip` only when the host has an explicit, locally
177
198
assigned address that containers should use for callbacks; package-managed
178
199
macOS gateways should leave it unset.
179
200
180
201
For HTTPS endpoints, the server certificate must include the endpoint host as a
181
-
subject alternative name. Docker sandboxes also need the client TLS bundle
182
-
mounted into the container and exposed with:
202
+
subject alternative name. The client TLS bundle is exposed only to host control
203
+
with:
183
204
184
205
-`OPENSHELL_TLS_CA`
185
206
-`OPENSHELL_TLS_CERT`
@@ -189,8 +210,9 @@ HTTP endpoints reject TLS material because the supervisor would not use it.
189
210
190
211
## Environment Ownership
191
212
192
-
The driver merges template environment and sandbox spec environment first, then
193
-
overwrites security-critical keys:
213
+
The driver writes template and sandbox environment into the protected boundary
214
+
config. Boundary mode exposes that map only to workload children. Host control
0 commit comments