Skip to content

fix(podman): chown workspace for non-root remoteUser on macOS/Windows#528

Merged
skevetter merged 4 commits into
mainfrom
fix/podman-chown-workspace-nonroot
Jun 25, 2026
Merged

fix(podman): chown workspace for non-root remoteUser on macOS/Windows#528
skevetter merged 4 commits into
mainfrom
fix/podman-chown-workspace-nonroot

Conversation

@skevetter

@skevetter skevetter commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Podman workspaces with a non-root remoteUser failed to open from VS Code Remote-SSH on macOS/Windows hosts, with:

start command: fork/exec /usr/bin/bash: permission denied

The container launched fine and devsy's own terminal worked, but VS Code (and devsy workspace ssh) could not start a shell.

Root cause

Rootless podman machine surfaces the host workspace bind mount as root-owned (root:root 0750) inside the container. Go's os/exec performs chdir(cmd.Dir) in the forked child before execve; the non-root remote user cannot enter the workspace folder, so the chdir returns EACCES — which Go reports against the shell path, producing the misleading "fork/exec /usr/bin/bash: permission denied".

addChownFlag gated --chown-workspace on runtime.GOOS == "linux" || !isDockerDriver. Podman runs through the docker driver, so on a macOS/Windows host neither clause held and the workspace was never chowned to the remote user. The fix extends the condition to also chown for the Podman runtime regardless of host OS.

devsy's own terminal was unaffected because the podman provider runs commands through the in-process emulated shell (devsy internal sh), which never execs a real /usr/bin/bash.

Spec alignment

The Dev Container spec scopes UID/GID syncing to "an optional task for Linux (only)" whose purpose is "to avoid permission problems with bind mounts," and explicitly lets implementations handle the case themselves when the engine does not translate automatically. This fix serves that exact goal for rootless Podman on macOS, where the reference CLI's Linux-only UID-remap mechanism does not apply. The chown writes through the bind mount to the host inode (cf. loft-sh/devpod#1879); it is scoped to non-root remote users, so root-user containers are unaffected.

Tests

  • Unit: shouldChownWorkspace and isPodmanRuntime table tests.
  • E2E: new rootless-podman spec with a non-root remoteUser fixture (docker-nonroot-user) asserting the workspace folder is owned by the remote user. Validated end-to-end against real rootless Podman (fails without the fix, passes with it).

Operational note

The chownWorkspace setup step is guarded by a one-time marker file, so existing broken workspaces need a Recreate (or a fresh workspace) to pick up the chown. New workspaces work immediately.

UI

No UI changes required — the desktop's workspace_up IPC handler shells out to the same workspace up CLI path, so this fix applies once the bundled CLI binary is built from this branch.

Summary by CodeRabbit

  • Bug Fixes
    • Improved workspace ownership handling for rootless Podman so files and folders are assigned to the expected user.
    • Fixed container setup behavior for non-root dev containers, helping remoteUser sessions start with the correct identity.
    • Refined mount consistency and platform checks to behave more reliably across Linux, macOS, and Windows.

The in-container SSH server (which VS Code Remote-SSH connects through)
failed with "start command: fork/exec /usr/bin/bash: permission denied"
for Podman workspaces with a non-root remoteUser on macOS/Windows hosts.

Root cause: rootless `podman machine` surfaces the host workspace bind
mount as root-owned (root:root 0750) inside the container. Go's exec
performs chdir(cmd.Dir) in the forked child before execve; the non-root
remote user cannot enter the workspace folder, so the chdir returns
EACCES, which Go reports against the shell path.

addChownFlag previously gated `--chown-workspace` on
`runtime.GOOS == "linux" || !isDockerDriver`. Podman uses the docker
driver, so on a macOS/Windows host neither clause held and the workspace
was never chowned to the remote user. Extend the condition to also chown
for the Podman runtime regardless of host OS.

devsy's own terminal was unaffected because the podman provider runs
commands through the in-process emulated shell (`devsy internal sh`),
which never execs a real /usr/bin/bash.

Tests:
- Unit: shouldChownWorkspace and isPodmanRuntime tables.
- E2E: new rootless-podman spec with a non-root remoteUser fixture
  asserting the workspace folder is owned by the remote user.
@netlify

netlify Bot commented Jun 25, 2026

Copy link
Copy Markdown

Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit 70920bf
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a3ccd45ffee090008101e6f

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@skevetter, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 50 minutes and 57 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aa2cbcc1-1692-4e5a-8a35-18c0a4e16ddb

📥 Commits

Reviewing files that changed from the base of the PR and between 11acd4e and 70920bf.

📒 Files selected for processing (4)
  • e2e/tests/up/provider_podman.go
  • e2e/tests/up/testdata/docker-nonroot-user/Dockerfile
  • pkg/devcontainer/setup.go
  • pkg/devcontainer/setup_test.go
📝 Walkthrough

Walkthrough

The PR updates workspace ownership handling for Podman, adds unit tests for the chown and runtime checks, and adds an e2e regression using a non-root devcontainer fixture to verify SSH user identity and workspace ownership.

Changes

Rootless Podman workspace ownership

Layer / File(s) Summary
Workspace chown logic
pkg/devcontainer/setup.go, pkg/devcontainer/run.go
Adds shared OS/runtime helpers for the chown decision and switches default mount consistency to compare against the Linux constant.
Helper tests
pkg/devcontainer/setup_test.go
Adds table-driven coverage for shouldChownWorkspace and runner.isPodmanRuntime().
Rootless Podman regression
e2e/tests/up/provider_podman.go, e2e/tests/up/testdata/docker-nonroot-user/*
Adds a non-root remoteUser devcontainer fixture and an e2e test that checks whoami and workspace ownership after DevsyUp.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • devsy-org/devsy#278: Updates the CI workflow to run up-provider-podman in rootless mode, which directly exercises the Podman regression covered here.
  • devsy-org/devsy#280: Also changes e2e/tests/up/provider_podman.go for Podman provider coverage, including rootless scenarios.
  • devsy-org/devsy#284: Adds another rootless Podman e2e scenario in the same test area and overlaps with the provider regression flow.

Suggested labels

size/xl

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the Podman workspace chown fix for non-root remote users on macOS/Windows.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@netlify

netlify Bot commented Jun 25, 2026

Copy link
Copy Markdown

Deploy Preview for images-devsy-sh canceled.

Name Link
🔨 Latest commit 70920bf
🔍 Latest deploy log https://app.netlify.com/projects/images-devsy-sh/deploys/6a3ccd4599ab6a0008010095

Satisfy goconst: replace repeated "linux"/"darwin"/"windows" string
literals with goosLinux/goosDarwin/goosWindows constants, and use the
existing docker.Runtime* constants in the runtime table test.
@skevetter
skevetter marked this pull request as ready for review June 25, 2026 06:30
@github-actions github-actions Bot added size/m and removed size/l labels Jun 25, 2026
@skevetter
skevetter merged commit ea59fd3 into main Jun 25, 2026
59 checks passed
@skevetter
skevetter deleted the fix/podman-chown-workspace-nonroot branch June 25, 2026 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant