Skip to content

bug: sandbox bind-mount ownership breaks after host reboot (unpinned rootless userns mapping, no launch-time reconcile) #2336

Closed as not planned

Description

@TonyLuo-NV

Summary

On the rootless Podman and Docker sandbox drivers, OpenShell attaches no user-namespace ID mapping to the container and mounts host state directories as plain (non-idmapped) rbinds. The host-side ownership of a bind-mounted state dir is therefore just the projection of a container-relative UID (e.g. sandbox) through whatever rootless subuid range the runtime happens to assign at create time. OpenShell never pins, persists, or reconciles that range. If the range shifts across a host reboot / runtime reconfig, previously-written inodes keep their old raw numbers and now resolve to unrelated host users — and OpenShell never repairs them.

Result: after a host reboot the sandbox's persisted state tree comes back owned by root / systemd-network / dnsmasq (group systemd-journal) with modes like drwx------ and -r--r--r--, and the sandbox process can no longer read/write its own state (readonly sqlite, EACCES). Reported downstream as NVIDIA/NemoClaw#6972 on a GB10 / Ubuntu 24.04 box: the agent could not recover the gateway and a rebuild's host-side backup could not read the mis-owned dirs.

Reproduction

  1. Launch a sandbox (rootless Podman driver) with a bind-mounted persistent state dir under ~/.../mounts/<sandbox>/.
  2. Let the sandbox write state (owned, on the host, by the runtime's rootless subuid projection of the container sandbox user).
  3. Reboot the host (or otherwise cause the rootless subuid base / userns-remap to shift — daemon reconfig, storage//etc/subuid change).
  4. Re-launch / recover the sandbox. Observe on the host: the state tree is now owned by unrelated low-numbered users (root, systemd-network, dnsmasq) with restrictive modes; inside the container the process hits EACCES / attempt to write a readonly database.

Root cause (code)

Confirmed against current main:

  • No userns / idmap on the container spec. crates/openshell-driver-podman/src/container.rs ContainerSpec (~L174–222) has no userns/idmappings field and runs the supervisor as user: "0:0" (~L908). The Docker driver sets no UsernsMode and also runs 0:0 (crates/openshell-driver-docker/src/lib.rs ~L908). Mapping is fully delegated to the runtime default.
  • Bind mounts are plain rbinds, not idmapped. Podman: crates/openshell-driver-podman/src/container.rs ~L562–585 builds options ["ro"|"rw","rbind"] (+ optional SELinux z/Z) — no idmap, no U. Docker: crates/openshell-driver-docker/src/lib.rs ~L1794–1829 emits source:target[:ro,z|Z] only.
  • No subuid base is pinned or persisted. The only subuid handling is crates/openshell-driver-podman/src/driver.rs check_subuid_range() (~L807, called ~L242), which is warn-only — it neither allocates, chooses, nor records a base.
  • Launch-time re-chown does not repair a persisted tree for these drivers. crates/openshell-supervisor-process/src/process.rs prepare_filesystem() (~L1147): read-write paths are chowned only when newly created (~L1183–1194, prepare_read_write_path returns true only on create), and the recursive /sandbox re-chown (~L1201–1207 → chown_sandbox_home ~L1105) runs only when OPENSHELL_SANDBOX_UID is set — which the rootless Podman/Docker path does not set (only K8s/VM drivers do). So an existing .hermes tree with a shifted mapping is never re-owned.
  • No reboot/recreate reconcile exists anywhere for the bind-mounted tree.

Impact

  • Permanent loss of access to sandbox state after a benign host reboot; user-visible as "impossible to recover the sandbox," data-loss on rebuild, and readonly-DB gateway failures.
  • Security-adjacent: host ownership of sandbox-written files is non-deterministic and, on some runtimes, can collapse onto low host UIDs.

Proposed fix directions (for maintainer input)

  1. Durable / deterministic (preferred, architectural): attach an idmapped mount to the host state binds, or pass explicit deterministic --uidmap/--gidmap to the container so the host-side ownership of the bind-mounted tree is stable and independent of the runtime's default rootless allocation across reboots. This pins the mapping OpenShell currently leaves implicit.
  2. Mitigation (contained, self-healing at launch): extend the existing prepare_filesystem re-chown so the sandbox state tree is reconciled to the configured sandbox identity on every launch for the Podman/Docker drivers too (not only when OPENSHELL_SANDBOX_UID is injected). This makes a mis-owned tree readable again under the current mapping without changing the mapping model. It heals the symptom each start but does not pin the mapping.
  3. Make check_subuid_range() fail-closed (or emit a clearer remediation) when the range is missing/ambiguous rather than warn-only.

Related: #1909 (rootless Podman UID-mapping issues on Fedora 44) touches adjacent territory but is a distinct scenario.

I'm happy to open a PR for the contained mitigation (#2) if that direction is acceptable, and defer the idmapped-mount design (#1) to maintainers.

Filed while triaging NVIDIA/NemoClaw#6972; root cause traced into OpenShell.

Activity

  1. TonyLuo-NV commented on Jul 17, 2026

    @TonyLuo-NV
    ContributorAuthor

    Draft fix up for the contained mitigation (opt-in launch-time ownership reconcile): #2338. It's deliberately default-off so the existing create-only chown contract is unchanged; the durable fix (idmapped mounts / pinned uid/gid map) is left for maintainer direction as noted above. Downstream context: NVIDIA/NemoClaw#6972.

  2. TonyLuo-NV commented on Aug 3, 2026

    @TonyLuo-NV
    ContributorAuthor

    Status ping: the proposed mitigation in #2338 is rebased onto current main, conflict-free, and green (tests + fmt + clippy; mirrored Branch Checks passed). It's been open ~2 weeks without review and the stale bot has already flagged it once.

    The blocking question is a design call, not code: should the default-off OPENSHELL_RECONCILE_SANDBOX_OWNERSHIP reconcile land as a stop-gap, or should this wait for the durable fix (idmapped mounts / pinned uid-gid map) described above? Either answer is fine — I'll close #2338 if the durable fix is preferred. Downstream users are currently hitting permanent state-access loss after a benign host reboot (NVIDIA/NemoClaw#6972).

  3. krishicks commented on Aug 14, 2026

    @krishicks
    Collaborator

    📋 triage-agent

    Removing state:triage-needed as workflow cleanup. Agent review of the issue's current state found substantive maintainer discussion, indicating that it is no longer awaiting initial triage. This label removal does not imply roadmap acceptance or implementation priority.

  4. github-actions commented on Aug 29, 2026

    @github-actions

    This issue has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. Comment or remove the state:stale label to keep it open.

  5. github-actions commented on Oct 13, 2026

    @github-actions

    Closing this issue after 14 days with no activity since it was marked stale. Reopen it if the work is still relevant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:staleInactive item at risk of automatic closure.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions