Skip to content

feat: preserve image-declared identity in Docker and Podman #2331

Description

@matthewgrossman

Problem

Docker and Podman run the OpenShell supervisor as root and replace the image
entrypoint. Agent children currently fall back to the policy identity
sandbox:sandbox, forcing otherwise compatible images to contain those exact
accounts.

Docker and Podman should instead use the image's OCI USER for any process
identity field omitted by policy. The supervisor remains root; only agent
children use the resolved identity.

Required behavior

Policy input Final user Final group
both supplied policy user policy group
only user supplied policy user OCI group
only group supplied OCI user policy group
neither supplied OCI user OCI group
  • Explicit policy fields remain authoritative and retain current validation.
  • Explicit sandbox remains a request for that account; omission activates OCI
    fallback.
  • Support OCI app, app:staff, 1234, 1234:1235, and mixed forms.
  • For USER app or USER 1234, use the passwd primary GID when group fallback
    is required.
  • Reject any selected OCI component that is missing, malformed, unresolved,
    ambiguous, or resolves to UID/GID 0.
  • Ignore an invalid OCI component when policy explicitly supplies that field.
  • An image without OCI USER fails only when at least one policy field needs
    OCI fallback.

Design

  1. Preserve omission of run_as_user and run_as_group for Docker and Podman.
    Continue materializing the legacy sandbox defaults for Kubernetes,
    OpenShift, VM, and unknown/remote drivers.

  2. Docker and Podman inspect the final image for its immutable ID and raw OCI
    Config.User, then create the container from that exact image ID.

  3. Drivers provide protected identity environment:

    • Docker/Podman set raw/empty OPENSHELL_OCI_IMAGE_USER and clear
      OPENSHELL_SANDBOX_UID/GID.
    • Kubernetes/OpenShift continue setting the authoritative numeric UID/GID
      pair.
    • VM/offline continue supplying neither.
  4. The supervisor normalizes that input into an internal enum:

    enum DriverIdentity {
        Resolved { uid: u32, gid: u32 },
        OciUser { declaration: String },
        None,
    }
  5. For OciUser, resolve only omitted policy components against bounded,
    read-only /etc/passwd and /etc/group parsing. Do not use NSS or rewrite
    account files.

  6. Produce one non-root UID/GID pair before readiness and feed it into the
    existing direct/SSH privilege-drop path.

Scope

  • Docker and Podman gain policy-first OCI fallback and immutable image pinning.
  • This issue changes process identity only. It does not create, chown, or
    otherwise prepare /sandbox for OCI-derived identities.
  • /sandbox remains the current workspace convention. Until the follow-up
    lands, custom images must provide a workspace usable by their selected
    identity.
  • Kubernetes/OpenShift retain their numeric identity, supplementary-group,
    PVC, fsGroup, and existing /sandbox ownership behavior.
  • VM retains its existing guest identity and /sandbox initialization.
  • Existing mount behavior remains unchanged.
  • OCI WorkingDir and dynamic workspace-root support are tracked in follow-up
    issue feat: honor OCI WorkingDir for Docker and Podman workspaces #2526 and will be implemented separately.
  • No fixed identity mode, gateway identity persistence, public identity
    status, session handshake, readiness protocol, supervisor CLI flag, or new
    public setting.

Tests

  • All four policy/OCI precedence combinations.
  • Named, numeric, mixed, primary-GID, and passwd-less numeric OCI forms.
  • Missing, malformed, unknown, ambiguous, UID-0, and GID-0 failures.
  • Fully explicit policy succeeds with missing, root, or different OCI USER.
  • Docker and Podman inspect and launch the same immutable image.
  • Protected environment cannot be spoofed.
  • Direct and SSH children use the same resolved identity.
  • Kubernetes/OpenShift retain their existing workspace and supplementary-group
    behavior.
  • Kubernetes/OpenShift, VM, and remote drivers retain legacy policy-default
    persistence behavior.
  • Account files and mount behavior stay unchanged.

Scope update

Earlier revisions included Docker/Podman preparation of /sandbox. That work
was removed from #2509 because it coupled identity fallback to a partial
workspace solution. Workspace behavior belongs with OCI WorkingDir support
in #2526.

Activity

  1. matthewgrossman commented on Jul 16, 2026

    @matthewgrossman
    MemberAuthor

    🏗️ build-plan

    Implementation Plan

    Issue type: feat
    Complexity: Medium
    Confidence: High — the policy precedence, driver boundary, resolution
    timing, workspace boundary, and non-goals are defined.

    Summary

    Remove the requirement for a blessed sandbox:sandbox image account.
    Docker/Podman inspect and pin the final image, pass protected raw OCI
    Config.User, and let the supervisor fill only policy fields that were
    omitted. The result is one numeric non-root identity shared by direct and SSH
    children.

    This PR keeps /sandbox as the fixed OpenShell workspace. When OCI fallback is
    used, OpenShell creates it if absent and chowns only the directory itself.
    Existing image content and nested mounts retain their ownership. OCI
    WorkingDir support belongs in the stacked follow-up tracked by #2526.

    Implementation Steps

    1. Preserve omission of run_as_user and run_as_group through policy
      defaults, conversion, persistence, create, and update paths. Keep explicit
      field validation unchanged.

    2. Add protected OPENSHELL_OCI_IMAGE_USER metadata and normalize supervisor
      input into:

      enum DriverIdentity {
          Resolved { uid: u32, gid: u32 },
          OciUser { declaration: String },
          None,
      }

      Docker/Podman select OciUser, Kubernetes/OpenShift retain Resolved, and
      VM/offline retain None. Reject conflicting or partial inputs.

    3. Update Docker and Podman to inspect after image selection, retain immutable
      image ID plus raw/empty Config.User, create from that ID, keep the
      supervisor at 0:0, and overwrite protected identity environment.

    4. Add a focused supervisor resolver that applies explicit policy field, then
      matching OCI component, then error. Resolve names with bounded, read-only,
      no-follow account-file parsing; reject ambiguity and root.

    5. Store the completed numeric pair in the in-memory process policy before
      readiness. Reuse the existing direct/SSH privilege-drop path and do not
      rewrite account files on the OCI path.

    6. When OCI fallback supplies either identity component, create /sandbox if
      absent and chown only its root directory. Do not traverse existing contents,
      symlinks, or nested mounts. Fail before readiness if the root cannot be
      prepared.

    7. Add focused policy, driver, resolver, direct/SSH, and Docker/Podman E2E
      coverage. Regression-test workspace content ownership, mounts, and
      Kubernetes/OpenShift/VM behavior.

    8. Update architecture, Docker/Podman, BYOC, security, and PR walkthrough
      documentation to describe the policy-first fallback and root-only workspace
      preparation.

    Test Plan

    • Unit: omission preservation; per-field precedence; named, numeric, mixed,
      missing, malformed, ambiguous, and root OCI identities; driver-input
      conflicts; missing, invalid, and unpreparable workspace roots.
    • Integration: Docker/Podman image inspection, immutable-ID pinning,
      protected environment precedence, and unchanged mount behavior.
    • E2E: named and passwd-less numeric images, missing/root failures, fully
      explicit policy override, root-only workspace ownership, retained ownership
      of existing content and nested mounts, workspace writes, and direct/SSH
      identity agreement for Docker and Podman where available.

    Risks & Open Questions

    • Removing default normalization may expose assumptions in non-local drivers;
      protect their current effective behavior with regression tests.
    • Account-file parser tests must remain file-based for SELinux/AppArmor
      compatibility.
    • Identity failure is asynchronous container startup failure; report a precise
      fatal error before readiness without adding a new protocol.
    • A fixed /sandbox remains a temporary compatibility boundary. Follow-up
      issue feat: honor OCI WorkingDir for Docker and Podman workspaces #2526 must resolve OCI WorkingDir consistently across mounts,
      filesystem policy, direct/SSH cwd, upload/download, and editor behavior.

    Documentation Impact

    Update relevant architecture, driver, BYOC, security, and walkthrough
    documentation. No gateway configuration fields are added.


    Revision 4 — keep /sandbox fixed in #2509, prepare only its root directory,
    retain existing content/mount ownership, and defer OCI WorkingDir to stacked
    follow-up #2526.

    Revision 3 — replace fixed modes, persisted identity, gateway handshake, mount
    restrictions, and complete-pair image authority with policy-first per-field OCI
    fallback.

    Revision 2 — replace fixed 10001:10001 design with OCI USER architecture;
    add persistence, storage boundary, policy authority, and observability.

    Revision 1 — initial fixed-identity plan for #2331.

  2. matthewgrossman commented on Jul 16, 2026

    @matthewgrossman
    MemberAuthor

    🏗️ build-from-issue-agent

    Implementation Complete

    PR: #2335

    What was built

    Docker and Podman now choose a validated numeric sandbox UID/GID, inject it as protected supervisor configuration, and launch agent processes with consistent identity environment variables. A userless image fixture verifies that images no longer need a baked-in sandbox account.

    Tests

    • Unit: driver identity defaulting, overrides, range validation, and environment precedence; supervisor child identity environment behavior
    • Integration: Docker and Podman custom_image feature variants compile
    • E2E: Docker userless-image path passed locally; rootless Podman runtime validation is covered by CI because Podman is unavailable on the development host

    Docs updated

    • Runtime and sandbox architecture overviews
    • Docker and Podman driver READMEs
    • Gateway configuration reference and Docker sample
    • Bring-your-own-container example

    The issue will auto-close when the PR is merged.

  3. matthewgrossman commented on Jul 16, 2026

    @matthewgrossman
    MemberAuthor

    🏗️ build-from-issue-agent

    PR blocked by vouch workflow

    PR #2335 was created successfully, but the Vouch Check workflow closed it twice while reporting matthewgrossman as unvouched. GitHub reports the author's repository association as member; the workflow's org-membership bypass did not take effect.

    The implementation, signed-off commit, full pre-commit suite, and local Docker userless-image E2E are complete. To resume CI, add matthewgrossman to the dedicated vouched branch through the normal /vouch process (or repair the workflow's ORG_READ_TOKEN membership check), then reopen #2335.

  4. added
    state:pr-openedPR has been opened for this issue
    and removed
    state:pr-openedPR has been opened for this issue
    on Jul 16, 2026
  5. 9 remaining items

  6. matthewgrossman commented on Jul 25, 2026

    @matthewgrossman
    MemberAuthor

    Implementation is ready for review in #2476.

    Validation completed:

    • mise run pre-commit
    • Docker custom-image identity E2E: 4 passed
    • Final principal/security review: no blocking findings

    Podman E2E remains unrun locally because Podman is unavailable.

  7. matthewgrossman commented on Jul 28, 2026

    @matthewgrossman
    MemberAuthor

    🏗️ build-from-issue-agent

    Implementation Complete

    PR: #2509

    What was built

    Docker and Podman now use policy-first, per-field OCI image identity fallback without requiring a baked-in sandbox account. The drivers pin launch to the inspected immutable image ID, and the supervisor resolves the final numeric UID/GID before starting direct or SSH agent children.

    Tests

    • Unit: 14 tests added or updated across policy persistence, driver launch, and identity resolution
    • Integration: Full mise run test and mise run ci suites pass
    • E2E: 2 focused Docker custom-image tests pass on commit 3bf0b9f6; Podman E2E unavailable because Podman is not installed on this host

    Docs updated

    • Compute runtime and sandbox architecture
    • Docker and Podman driver guidance
    • Policy schema, compute driver, policy, security, and BYOC documentation
    • Related OpenShell agent skills

    The issue will auto-close when the PR is merged.

  8. matthewgrossman commented on Jul 28, 2026

    @matthewgrossman
    MemberAuthor

    🏗️ build-from-issue-agent

    Workspace scope clarified

    PR #2509 now keeps /sandbox
    as the fixed compatibility workspace but no longer performs a recursive
    ownership rewrite. On head
    5ca344e6:

    • OCI identity fallback creates /sandbox if needed and chowns only that
      directory.
    • Existing image content, symlinks, and nested mounts are not traversed and
      retain their ownership.
    • Both direct and SSH children can create files in the prepared workspace.
    • Images only need to pre-own existing files that the agent must modify.
    • Kubernetes/OpenShift and VM behavior remains unchanged.

    Adopting OCI WorkingDir and removing /sandbox as a Docker/Podman image-facing
    workspace convention is intentionally deferred to a separate stacked follow-up
    PR. This keeps #2509 focused on removing the sandbox:sandbox account
    requirement.

    Validation on the new head:

    • mise run pre-commit
    • Supervisor unit tests: 98 passed, 1 ignored
    • Focused Docker E2E: 5 passed
    • Docker and Podman E2E targets compile
  9. matthewgrossman commented on Jul 28, 2026

    @matthewgrossman
    MemberAuthor

    🏗️ build-from-issue-agent

    OCI WorkingDir and dynamic Docker/Podman workspace-root support are now
    tracked separately in #2526. That follow-up is designed to stack on #2509 so
    this issue remains focused on removing the sandbox:sandbox account
    requirement.

  10. added a commit that references this issue on Jul 28, 2026
    9301387
  11. added a commit that references this issue on Jul 29, 2026
    bc14018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:sandboxSandbox runtime and isolation workstate:agent-readyApproved for agent implementationstate:pr-openedPR has been opened for this issue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions