Skip to content

Commit 7de71bf

Browse files
committed
feat(kubernetes): add cni-sidecar supervisor topology
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
1 parent 0928bf7 commit 7de71bf

34 files changed

Lines changed: 1918 additions & 66 deletions

.agents/skills/debug-openshell-cluster/SKILL.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,18 @@ workload entrypoint PID to `OPENSHELL_ENTRYPOINT_PID_FILE`
285285
should read it for binary-scoped policy decisions; if allowed network rules are
286286
all denied, inspect that file and the network sidecar logs.
287287

288+
If `supervisor_topology = "cni-sidecar"` is rendered, the gateway should render
289+
the same process container and long-running network sidecar as sidecar mode, but
290+
there should be no `openshell-network-init` init container in sandbox pods.
291+
Instead, the chart must install the privileged `openshell-cni` DaemonSet and the
292+
sandbox pod should carry `openshell.ai/cni=enabled`,
293+
`openshell.ai/network-enforcement-mode=cni-sidecar`, and
294+
`openshell.ai/proxy-uid=<uid>` annotations. The CNI DaemonSet copies
295+
`/openshell-cni` into the host CNI binary directory and patches an existing CNI
296+
`.conflist`; if sandbox pods bypass network enforcement or fail during pod
297+
network setup, inspect the DaemonSet logs, the host CNI config, and whether the
298+
cluster actually invokes chained CNI plugins for the sandbox runtime class.
299+
288300
If `supervisor_topology = "proxy-pod"` is rendered, each sandbox should have a
289301
separate supervisor Deployment with one supervisor pod, a headless supervisor
290302
Service, a proxy CA Secret, and two per-sandbox NetworkPolicies. The agent pod
@@ -305,6 +317,9 @@ Inspect all three when sandbox registration or egress enforcement fails:
305317
kubectl -n openshell get configmap openshell-config -o jsonpath='{.data.gateway\.toml}' | grep supervisor_topology
306318
kubectl -n <sandbox-namespace> get pod <sandbox-pod> -o jsonpath='{range .spec.initContainers[*]}{.name}{" "}{.command}{"\n"}{end}'
307319
kubectl -n <sandbox-namespace> get pod <sandbox-pod> -o jsonpath='{range .spec.containers[*]}{.name}{" "}{.command}{"\n"}{end}'
320+
kubectl -n <sandbox-namespace> get pod <sandbox-pod> -o jsonpath='{.metadata.annotations}'
321+
kubectl -n openshell get daemonset,pod -l app.kubernetes.io/component=cni
322+
kubectl -n openshell logs daemonset/openshell-cni -c install-cni --tail=200
308323
kubectl -n <sandbox-namespace> logs <sandbox-pod> -c openshell-network-init --tail=200
309324
kubectl -n <sandbox-namespace> logs <sandbox-pod> -c openshell-supervisor-network --tail=200
310325
kubectl -n <sandbox-namespace> logs <sandbox-pod> -c agent --tail=200
@@ -338,6 +353,7 @@ openshell logs <sandbox-name>
338353
| Kubernetes gateway pod crash loops | Missing secret, bad DB URL, bad TLS config | `kubectl -n openshell logs deployment/openshell -c openshell-gateway` or `kubectl -n openshell logs statefulset/openshell -c openshell-gateway` |
339354
| CLI TLS error | Local mTLS bundle does not match server cert/CA | Check `~/.config/openshell/gateways/<name>/mtls/` |
340355
| Image pull failure | Gateway or sandbox image cannot be pulled | Runtime events and image pull credentials |
356+
| CNI-sidecar sandbox pods fail network setup | OpenShell CNI DaemonSet did not patch the node CNI conflist, cannot read pods, or the runtime class does not invoke the chained plugin | `kubectl -n openshell logs daemonset/openshell-cni -c install-cni`, chart `cni.*` values, host CNI config |
341357
| `K8s namespace not ready` with `envoy-gateway-openshell.yaml: the server could not find the requested resource` | Optional Gateway API manifest was applied without Envoy Gateway CRDs, or k3s Helm controller startup exceeded the namespace wait | Apply `deploy/kube/manifests/envoy-gateway-openshell.yaml` manually only after Envoy Gateway is installed and `grpcRoute` is enabled |
342358
| HTTPS ingress (`grpcRoute.gateway.listener.protocol=HTTPS`) connection resets or TLS handshake hangs | Envoy terminates TLS but the gateway pod still expects TLS, so the plaintext backend hop fails | Set `server.disableTls=true` so Envoy forwards plaintext to the pod; verify the listener `certificateRefs` Secret exists in the release namespace and `openshell status` over `https://<host>` |
343359
| HTTPS ingress returns `Unauthenticated` after connecting | TLS terminates at Envoy, so the gateway never sees a client cert; no OIDC issuer is configured for identity | Configure `server.oidc.issuer` and register with `openshell gateway add https://<host> --oidc-issuer <url>`, or set `server.auth.allowUnauthenticatedUsers=true` for a trusted-proxy/dev cluster |

.agents/skills/helm-dev-environment/SKILL.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ mise run helm:skaffold:run
6565
mise run helm:skaffold:run:sidecar
6666
```
6767

68+
**Supervisor CNI-sidecar topology** (build once and leave running):
69+
```bash
70+
mise run helm:skaffold:run:cni-sidecar
71+
```
72+
6873
**Supervisor proxy-pod topology** (build once and leave running):
6974
```bash
7075
mise run helm:skaffold:run:proxy-pod
@@ -73,7 +78,9 @@ mise run helm:skaffold:run:proxy-pod
7378
All Skaffold commands build the `gateway` and `supervisor` images and deploy the OpenShell Helm
7479
chart. The sidecar profile renders an `openshell-network-init` init container for
7580
nftables setup and a non-root `openshell-supervisor-network` runtime sidecar for
76-
proxying. The proxy-pod profile renders network supervision in a separate
81+
proxying. The cni-sidecar profile enables the privileged OpenShell CNI
82+
DaemonSet and uses the sidecar runtime model without the pod-local network init
83+
container. The proxy-pod profile renders network supervision in a separate
7784
supervisor Deployment with one pod and relies on Kubernetes NetworkPolicy
7885
enforcement so the agent pod can reach only its paired supervisor plus DNS. The
7986
default local k3s/k3d cluster keeps k3s's embedded NetworkPolicy controller
@@ -104,6 +111,12 @@ Run the sidecar topology e2e environment:
104111
mise run e2e:kubernetes:sidecar
105112
```
106113

114+
Run the CNI-sidecar topology e2e environment:
115+
116+
```bash
117+
mise run e2e:kubernetes:cni-sidecar
118+
```
119+
107120
Run the proxy-pod topology e2e environment:
108121

109122
```bash
@@ -176,6 +189,12 @@ For a sidecar-profile deployment:
176189
mise run helm:skaffold:delete:sidecar
177190
```
178191

192+
For a cni-sidecar-profile deployment:
193+
194+
```bash
195+
mise run helm:skaffold:delete:cni-sidecar
196+
```
197+
179198
For a proxy-pod-profile deployment:
180199

181200
```bash
@@ -307,6 +326,7 @@ for dependencies still declared in `Chart.yaml`.
307326
| `deploy/helm/openshell/ci/values-high-availability.yaml` | HA test overlay (`replicaCount: 2` with external PostgreSQL Secret) |
308327
| `deploy/helm/openshell/ci/values-keycloak.yaml` | Keycloak OIDC overlay |
309328
| `deploy/helm/openshell/ci/values-sidecar.yaml` | Supervisor sidecar topology overlay for Kubernetes e2e/dev |
329+
| `deploy/helm/openshell/ci/values-cni-sidecar.yaml` | Supervisor CNI-sidecar topology overlay for Kubernetes e2e/dev; enables the OpenShell CNI DaemonSet |
310330
| `deploy/helm/openshell/ci/values-proxy-pod.yaml` | Supervisor proxy-pod topology overlay for Kubernetes e2e/dev; requires NetworkPolicy enforcement |
311331
| `deploy/helm/openshell/ci/values-spire.yaml` | SPIFFE/SPIRE provider token grant overlay |
312332
| `deploy/helm/openshell/ci/values-spire-stack.yaml` | SPIRE hardened chart values for local dev |

.github/workflows/docker-build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ jobs:
246246
fi
247247
mkdir -p "$stage"
248248
install -m 0755 "$found" "$stage/$binary"
249+
if [[ "${{ inputs.component }}" == "supervisor" ]]; then
250+
PREBUILT_ARCH="${{ matrix.arch }}" tasks/scripts/stage-prebuilt-binaries.sh cni
251+
fi
249252
ls -lh "$stage/"
250253
251254
- name: Build ${{ inputs.component }} image

Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

architecture/compute-runtimes.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,19 +89,23 @@ Driver-controlled environment variables must override sandbox image or template
8989
values for sandbox ID, sandbox name, gateway endpoint, relay socket path, TLS
9090
paths, and command metadata.
9191

92-
Kubernetes can run the supervisor in the default combined topology or in a
93-
sidecar topology. Combined mode keeps network and process supervision in the
92+
Kubernetes can run the supervisor in combined, sidecar, cni-sidecar, or
93+
proxy-pod topology. Combined mode keeps network and process supervision in the
9494
agent container. Sidecar mode runs network enforcement, the proxy, and gateway
9595
loopback forwarding in a dedicated sidecar, while the agent container runs only
9696
the process-supervision leaf and launches the user workload after the sidecar
9797
signals readiness. In sidecar mode, an init container performs the privileged
9898
pod-network nftables setup with `NET_ADMIN` and hands shared state ownership to
9999
the configured proxy UID; the long-running network sidecar runs as that UID and
100-
does not keep `NET_ADMIN`. The agent container runs as the resolved sandbox
101-
UID/GID with no added Linux capabilities. Sidecar mode preserves gateway session
102-
and SSH behavior, but treats the process leaf as network-only: Landlock
103-
filesystem policy, process privilege dropping, and process/binary identity
104-
checks are not applied there.
100+
does not keep `NET_ADMIN`. CNI-sidecar mode keeps the sidecar runtime model but
101+
requires the privileged OpenShell CNI DaemonSet to install the pod-network rules
102+
during CNI `ADD` using nftables or iptables. Proxy-pod mode moves network
103+
enforcement into a paired supervisor Deployment and requires NetworkPolicy
104+
enforcement. The agent container runs as the resolved sandbox UID/GID with no
105+
added Linux capabilities in the alternate topologies. They preserve gateway
106+
session and SSH behavior, but
107+
treat the process leaf as network-only: Landlock filesystem policy, process
108+
privilege dropping, and process/binary identity checks are not applied there.
105109

106110
## Images
107111

crates/openshell-cni/Cargo.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
[package]
5+
name = "openshell-cni"
6+
description = "OpenShell chained CNI plugin for Kubernetes sidecar network enforcement"
7+
version.workspace = true
8+
edition.workspace = true
9+
license.workspace = true
10+
repository.workspace = true
11+
rust-version.workspace = true
12+
13+
[dependencies]
14+
base64 = { workspace = true }
15+
miette = { workspace = true }
16+
reqwest = { workspace = true, features = ["blocking"] }
17+
serde = { workspace = true }
18+
serde_json = { workspace = true }
19+
serde_yml = { workspace = true }
20+
tempfile = "3"
21+
22+
[target.'cfg(target_os = "linux")'.dependencies]
23+
libc = "0.2"
24+
25+
[dev-dependencies]
26+
tempfile = "3"
27+
28+
[lints]
29+
workspace = true

0 commit comments

Comments
 (0)