Support pod.spec.hostUsers: false (KEP-127): runsc not advertised as supporting user namespaces in CRI
- Dominant language
- Go
- Stars
- 19.3k
- Forks
- 2k
- Avg merge
- 3d 5h
- Merged PRs (30d)
- 264
Description
## Description
Pods with `runtimeClassName: gvisor` and `pod.spec.hostUsers: false` (Kubernetes user namespaces, KEP-127) are rejected by the kubelet before the runsc shim is ever invoked:
```
Failed to create pod sandbox: can't set `spec.hostUsers: false`,
RuntimeClass handler "runsc" does not support user namespaces
```
`runc` works in the same cluster. `crictl info` on the node shows the gap clearly:
```json
{ "name": "runc", "features": { "user_namespaces": true, "recursive_read_only_mounts": true } }
{ "name": "runsc" } // no features object
```
Reproducer:
- Kubernetes 1.35.2 (kubelet honors `RuntimeHandlerFeatures.UserNamespaces` from CRI).
- containerd 2.2.2.
- runsc `release-20260520.0`, shim `containerd-shim-runsc-v1` 2.1.5.
- Linux 6.17 (Ubuntu 24.04).
## Is this feature related to a specific bug?
No standing gVisor bug, but the proximate cause is split between containerd and gVisor:
1. **containerd's `introspectRuntimeFeatures` hardcodes runc as the only runtime it queries for OCI features**:
```go
if r.Type != plugins.RuntimeRuncV2 {
return nil, fmt.Errorf("introspecting OCI runtime features needs the runtime type to be %q, got %q", ...)
}
```
So `io.containerd.runsc.v1` never has its `manager.Info()` Features consulted, even though `pkg/shim/v1/manager.go` already returns them via `specutils.Features()`.
2. **Even if introspection ran, containerd's `supportsCRIUserns` requires both `Linux.Namespaces` to contain `"user"` AND `Linux.MountExtensions.IDMap.Enabled = true`.** runsc satisfies the first (`runsc/specutils/namespace.go:KnownNamespaces` includes `specs.UserNamespace`) but explicitly returns `IDMap.Enabled: false` in `runsc/specutils/specutils.go`.
## Do you have a specific solution in mind?
Two pieces, both probably needed:
- **gVisor side:** Decide whether runsc can advertise idmapped mount support (or an equivalent capability that satisfies kubelet's volume-ownership translation expectations). If idmap mounts aren't on the roadmap, an alternative would be working with containerd to allow runtimes that handle volume ownership another way (gofer-side chown, etc.) to opt in without `IDMap.Enabled`.
- **containerd side (separate issue):** relax `introspectRuntimeFeatures` to call `PluginInfo` for non-runc runtime types as well, so the runsc shim's existing OCI features can actually be surfaced. Happy to file that one separately and link it.
Today the only path I see for `pod.spec.hostUsers: false` to work with `runtimeClassName: gvisor` is patching either side. The corresponding `runc` + `hostUsers: false` configuration works fine on the same node, so this is purely a CRI-advertisement and idmap-feature gap, not a runsc capability gap (runsc itself supports user namespaces via OCI uidMappings, per the [Rootless guide](https://gvisor.dev/docs/user_guide/rootless/) Method 3).
What is the gVisor team's current plan for KEP-127 / `pod.spec.hostUsers: false` interop? Happy to contribute the patch on either side once the direction is clear.
Contributor guide
Assessment
This issue has not been assessed yet.