kubepath
The request path hop 6 of 10 traces to the constraint

The pod, inside gVisor

One node means tenant code and platform code share a kernel. gVisor is what stands between them.

The request has reached a pod running a tenant’s code. On a multi-node cluster you would separate tenant workloads onto their own nodes and let the kernel boundary be a machine boundary. There is one worker node, so that option does not exist: tenant code and the platform’s own components share a kernel.

gVisor is the answer. A DaemonSet installs runsc and its containerd shim onto the node; a RuntimeClass makes it selectable; and all user code runs under it. What that buys is a user-space kernel between the container and the real one — syscalls are handled by the Sentry rather than passed through, so a container escape has to defeat runsc before it has anything to escape into.

It costs something real. Syscall-heavy workloads are measurably slower, some syscalls are unimplemented and return ENOSYS rather than working, /proc is partial, and things that expect direct kernel features — io_uring, some eBPF, certain mount operations — do not work. Which is why the platform’s own components deliberately do not run under it. The sandbox is for the code you did not write.

The failure mode worth naming in a classroom. runtimeClassName: gvisor is a field on the pod spec. A pod that omits it runs on runc, starts fine, serves traffic fine, and is silently outside the sandbox. Nothing warns you. So the security property is not "user code runs under gVisor" — it is "user code runs under gVisor if every template that creates it got one line right." Turning that convention into a guarantee needs enforcement: a validating admission policy that rejects pods in tenant namespaces without the RuntimeClass, or a namespace-level default. Until then, the check is an audit query, and it belongs in the runbook.

opt-in, one line, no warning if you forget
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc                       # installed by the installer DaemonSet
---
apiVersion: v1
kind: Pod
spec:
  runtimeClassName: gvisor           # omit this and you get runc, silently
  automountServiceAccountToken: false
  containers:
    - name: app
      image: registry.example.com/tenant/app@sha256:1f4a...

# the audit query, until an admission policy makes it unnecessary:
#   kubectl get pods -n tenants -o json | jq -r '.items[]
#     | select(.spec.runtimeClassName != "gvisor") | .metadata.name'

Check yourself

2 questions. One attempt each is recorded; the explanation is the point, not the score.

A tenant pod is created without runtimeClassName: gvisor. What happens?

Why is gVisor needed on this cluster in particular?