fix(docker): don't crash on read-only /tools; writable on-PATH install dirs for non-root agent (0.23.0-beta.2)#1321
Merged
Conversation
…table on-PATH install dirs The bind-mount ownership repair added in 0.23.0-beta.1 (#1281) ran a recursive chown over /tools and aborted fatally on a read-only mount, crash-looping the container. /tools is a PATH directory the agent only reads from, so the entrypoint now treats it as best-effort: never recursive-chowns it, never fatal on a read-only/already-correct mount. #1281 also dropped the daemon to the non-root netclaw user, which left no writable directory on its PATH for runtime tool installs (the agent can't apt-get or write system dirs). The image now ships writable, on-PATH locations (~/.local/bin, ~/.dotnet, ~/.dotnet/tools, /tools/bin) and a default PATH that includes them, so a runtime-installed dotnet / pip --user tool / .NET global tool resolves as a bare command in the agent's non-interactive shell. Bumps to 0.23.0-beta.2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two regressions shipped in 0.23.0-beta.1 via the bind-mount ownership repair (#1281), both rooted in the daemon now running as the non-root
netclawuser. This fixes both and bumps to 0.23.0-beta.2.1. Container crash-loops on a read-only
/toolsmountThe entrypoint ran
ensure_runtime_ownership /tools, which does a recursivechown -R 1654:1654 /toolsandexit 1s if it fails. When/toolsis a read-only mount (a legitimate, common pattern: an immutable, pre-provisioned toolset such as a vendored .NET SDK), thechownfails withEROFSand the container crash-loops.Observed in the wild (Kubernetes,
/toolsmountedreadOnly: truefrom an init-container-populated volume):Fix:
/toolsis a PATH directory the agent only ever reads/executes from — it never needs to own or write it. The newensure_tools_accessiblehelper is best-effort: it never recursively chowns/tools, only confirms the runtime user can traverse+read it, opportunistically fixes the top-level owner if the mount is writable, and never exits fatally on a read-only mount. The writable data dir (~/.netclaw) keeps its existing strict, fatal ownership repair — that one genuinely must be writable.2. Non-root agent can't install tools at runtime
Because the daemon dropped to non-root, it can't
apt-getor write systemPATHdirs (/usr/local/bin,/usr/share/dotnet). Worse, its non-interactive shells never source~/.bashrc/~/.profile, so an install into~/.dotnetwasn't resolvable as a bare command. Net effect: live tool installs (e.g.dotnet-install.sh) were effectively broken.Fix: the image now pre-creates user-writable, on-
PATHinstall locations and sets a defaultPATHthat includes them:~/.local/bin(pip--user, generic)~/.dotnet+~/.dotnet/tools(dotnet-install.shdefault + .NET global tools)/tools/bin(operator/init-container toolsets mounted at/tools)A runtime-installed
dotnetnow resolves as a baredotnetin the agent's shell, no PATH gymnastics required. The misleading Dockerfile comment about runtimeapt-get installis corrected to reflect the non-root model.Why keep non-root at all?
Running an arbitrary-command agent as non-root is the right posture — it caps the blast radius of prompt-injection-driven command execution. These were implementation bugs in how the drop was rolled out, not a reason to revert it. This PR makes the non-root model actually usable.
Changes
docker/entrypoint.sh—ensure_tools_accessiblefor/tools(non-fatal, non-recursive); data dir repair unchanged.docker/Dockerfile— pre-create~/.local/bin,~/.dotnet/tools; defaultPATHincludes the user-writable +/tools/binlocations; corrected runtime-install comment.Directory.Build.props+RELEASE_NOTES.md— version →0.23.0-beta.2.Test plan
validate_docker_imagesmoke test passes (normal startup unaffected)./toolsread-only → container starts (no crash),WARNlogged instead of fatal.dotnet-install.shinto~/.dotnet→ baredotnet --inforesolves in a non-interactive shell.Release
Tag
0.23.0-beta.2after merge to firepublish_release_binaries.yml(builds binaries, creates the GitHub prerelease, moves the floating:betaDocker tag).