Skip to content

fix(podman): create managed workspace volumes owned by the workload identity - #3932

Merged
matthewgrossman merged 0 commit into
podman-workdir/a-oci-image-suitefrom
podman-workdir/b-volume-ownership
Sep 30, 2026
Merged

matthewgrossman merged 0 commit into
podman-workdir/a-oci-image-suitefrom
podman-workdir/b-volume-ownership

Conversation

@matthewgrossman

@matthewgrossman matthewgrossman commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fix Podman sandboxes that cannot start when their image or policy selects a non-root user. Podman now creates the managed /sandbox volume for that user, so the workload can start and write there without first running as root.

This is the second of three stacked PRs split out of #3801: #3931 ← this PR ← #3933. Review after #3931.

What breaks today, and why this fix

For example, build an image with USER 2345:2346 and no /sandbox, or use a USER-less image with policy run_as_user: "2345" and run_as_group: "2346". On a rootful Podman gateway, OpenShell gives the workload a managed /sandbox volume, but Podman creates its root as root:root with mode 0700. User 2345 cannot enter it, so sandbox provisioning fails before the agent command can run (locally, ContainerExited ... code 1; the entrypoint can report Permission denied). The user cannot fix this from inside the sandbox because the process has no authority to change the directory's ownership.

Docker already prepares its managed /sandbox directory owned by the workload UID/GID before starting the capability-free process: it stages a directory in the container archive with those ownership and permission settings. Podman uses a named volume instead of that Docker archive directory. We could start Podman's workload as root, adjust /sandbox ownership/permissions, then drop to the requested user—as the old root-then-drop path did for some launches—but that adds a privileged setup step to every affected start. Instead, this PR passes the final UID/GID directly to Podman when creating the volume. Podman makes the volume root owned by that user, and the workload starts as that user from the outset with no added capabilities. The channel volume remains unchanged.

Related Issue

Fixes #3937. This bug was found while working on #3801 / #2526: the shared oci-image suite's default_workdir_uses_managed_workspace scenario exposed it on rootful Podman. Docker already gives the image user a writable /sandbox.

Changes

  • client.rs: create_owned_volume(..., owner: Option<(u32, u32)>) sends Options: {"o": "uid=…,gid=…"} and verifies the created or existing volume against a shared volume_options_match_owner predicate. Podman records the parsed UID/GID next to o. Add ContainerConfig.user for admission.
  • driver.rs: create the workspace volume owned by (identity.uid, identity.gid) and the channel volume with no options. Resource admission accepts a workspace volume whose options match the container's numeric Config.User, or have no options (created by older gateways). The channel volume still requires empty options. Remove the now-unused rootless driver field.
  • container.rs: remove the launch-capability-free root-then-drop branch and IsolationSpecInput.rootless. The workload always runs --bootstrap as the final identity with cap_drop: ALL and no added capabilities. launch-capability-free stays in openshell-sandbox because the VM driver still uses it.
  • e2e/rust/tests/podman_oci_identity.rs: the workload container user is now {OCI_UID}:{OCI_GID} instead of 0:0.
  • Podman README: note the volume ownership.

Testing

All checks ran against a local rootful Podman machine (macOS, libkrun).

  • mise run pre-commit passes
  • Unit tests: cargo test -p openshell-driver-podman --lib (228 passed), with new create_owned_volume_verifies_requested_owner and admission_accepts_workspace_volume_owned_by_workload_identity, and updated isolation-spec tests. cargo clippy -p openshell-driver-podman --all-targets -- -D warnings is clean.
  • E2E: podman_oci_identity passes.
  • Manual repro through e2e/with-podman-gateway.sh. Each case runs id; stat /sandbox; touch /sandbox/probe:
    • USER-less ubuntu:24.04 with policy run_as_user: "2345", run_as_group: "2346": on main, provisioning fails with ContainerExited ... code 1. With this PR it runs as 2345:2346, /sandbox is 2345:2346 700, and the write succeeds.
    • Image with USER 2345:2346 and no /sandbox: same results on main and with this PR.
  • Resource admission: with resource_admission.enabled = true temporarily set in e2e/support/podman-gateway-config.sh (not committed), oci-image's default_workdir_uses_managed_workspace passes. With the owner-match branch of the admission check disabled, it fails with sandbox lacks attachment provenance.

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable) — Podman driver README

@copy-pr-bot

copy-pr-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@matthewgrossman

Copy link
Copy Markdown
Contributor Author

/ok to test b6eabeb

@matthewgrossman

Copy link
Copy Markdown
Contributor Author

GitHub automatically marked this PR merged when its reordered head became an ancestor of its old base during stack maintenance. The change has not landed on main. Replacement draft PR: #3981, in the new stack #3983.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: rootful Podman managed workspace is unwritable for non-root workloads

1 participant