Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
There was a problem hiding this comment.
Pull request overview
Updates the agent container entrypoint to ensure gh-aw runtime/config directories created as root on self-hosted runners remain accessible after the chroot privilege drop, preventing silent failures in MCP tool discovery and safe-output writes (closes #1463).
Changes:
- Recursively relaxes permissions on
/host/tmp/gh-awbefore executing the chrooted agent command. - Recursively relaxes permissions on
/host/opt/gh-aw/safeoutputsbefore executing the chrooted agent command.
Show a summary per file
| File | Description |
|---|---|
containers/agent/entrypoint.sh |
Adds a pre-privilege-drop permission fixup for host-mounted gh-aw config and safe-output directories in chroot mode. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 1/1 changed files
- Comments generated: 3
containers/agent/entrypoint.sh
Outdated
| chmod -R a+rwX /host/tmp/gh-aw 2>/dev/null || true | ||
| echo "[entrypoint] Made /host/tmp/gh-aw accessible to chroot user" | ||
| fi | ||
| if [ -d /host/opt/gh-aw/safeoutputs ]; then | ||
| chmod -R a+rwX /host/opt/gh-aw/safeoutputs 2>/dev/null || true |
There was a problem hiding this comment.
chmod -R a+rwX makes these host-mounted directories (especially /host/opt/gh-aw/safeoutputs) world-readable and world-writable, and the permission change persists on the host after the job. This is broader than necessary and can allow unintended cross-user tampering / data exposure on multi-user self-hosted runners. Consider changing ownership to the intended chroot user (e.g., chown -R ${HOST_UID}:${HOST_GID}) and using a more restrictive mode (e.g., user/group +rwX only) instead of granting access to a.
| chmod -R a+rwX /host/tmp/gh-aw 2>/dev/null || true | |
| echo "[entrypoint] Made /host/tmp/gh-aw accessible to chroot user" | |
| fi | |
| if [ -d /host/opt/gh-aw/safeoutputs ]; then | |
| chmod -R a+rwX /host/opt/gh-aw/safeoutputs 2>/dev/null || true | |
| chown -R "${HOST_UID}:${HOST_GID}" /host/tmp/gh-aw 2>/dev/null || true | |
| chmod -R ug+rwX,o-rwX /host/tmp/gh-aw 2>/dev/null || true | |
| echo "[entrypoint] Made /host/tmp/gh-aw accessible to chroot user" | |
| fi | |
| if [ -d /host/opt/gh-aw/safeoutputs ]; then | |
| chown -R "${HOST_UID}:${HOST_GID}" /host/opt/gh-aw/safeoutputs 2>/dev/null || true | |
| chmod -R ug+rwX,o-rwX /host/opt/gh-aw/safeoutputs 2>/dev/null || true |
There was a problem hiding this comment.
Fixed in 4d51fdc. Replaced chmod -R a+rwX with chown -R "${HOST_UID}:${HOST_GID}" so that only the specific agent user gets access, not all users on the host. This addresses the world-writable concern on multi-user self-hosted runners.
containers/agent/entrypoint.sh
Outdated
| chmod -R a+rwX /host/tmp/gh-aw 2>/dev/null || true | ||
| echo "[entrypoint] Made /host/tmp/gh-aw accessible to chroot user" | ||
| fi | ||
| if [ -d /host/opt/gh-aw/safeoutputs ]; then | ||
| chmod -R a+rwX /host/opt/gh-aw/safeoutputs 2>/dev/null || true | ||
| echo "[entrypoint] Made /host/opt/gh-aw/safeoutputs accessible to chroot user" |
There was a problem hiding this comment.
The success logs here are unconditional even if chmod fails (errors are redirected and || true forces a 0 exit). This can be misleading when debugging permission issues on self-hosted runners. Consider only printing the success message when the chmod command succeeds, and logging a warning when it fails.
| chmod -R a+rwX /host/tmp/gh-aw 2>/dev/null || true | |
| echo "[entrypoint] Made /host/tmp/gh-aw accessible to chroot user" | |
| fi | |
| if [ -d /host/opt/gh-aw/safeoutputs ]; then | |
| chmod -R a+rwX /host/opt/gh-aw/safeoutputs 2>/dev/null || true | |
| echo "[entrypoint] Made /host/opt/gh-aw/safeoutputs accessible to chroot user" | |
| if chmod -R a+rwX /host/tmp/gh-aw 2>/dev/null; then | |
| echo "[entrypoint] Made /host/tmp/gh-aw accessible to chroot user" | |
| else | |
| echo "[entrypoint][WARN] Failed to make /host/tmp/gh-aw accessible to chroot user" | |
| fi | |
| fi | |
| if [ -d /host/opt/gh-aw/safeoutputs ]; then | |
| if chmod -R a+rwX /host/opt/gh-aw/safeoutputs 2>/dev/null; then | |
| echo "[entrypoint] Made /host/opt/gh-aw/safeoutputs accessible to chroot user" | |
| else | |
| echo "[entrypoint][WARN] Failed to make /host/opt/gh-aw/safeoutputs accessible to chroot user" | |
| fi |
There was a problem hiding this comment.
Fixed in 4d51fdc. The log messages are now conditional — success prints the ownership transfer with the UID:GID, and failure prints a [WARN] message. This makes debugging permission issues on self-hosted runners much easier.
containers/agent/entrypoint.sh
Outdated
| # Make gh-aw config directories readable/writable by the chroot user. | ||
| # On self-hosted runners these directories are created by the host-side | ||
| # gh-aw tooling as root, so the unprivileged chroot user cannot access them. | ||
| if [ -d /host/tmp/gh-aw ]; then | ||
| chmod -R a+rwX /host/tmp/gh-aw 2>/dev/null || true | ||
| echo "[entrypoint] Made /host/tmp/gh-aw accessible to chroot user" | ||
| fi | ||
| if [ -d /host/opt/gh-aw/safeoutputs ]; then | ||
| chmod -R a+rwX /host/opt/gh-aw/safeoutputs 2>/dev/null || true | ||
| echo "[entrypoint] Made /host/opt/gh-aw/safeoutputs accessible to chroot user" | ||
| fi |
There was a problem hiding this comment.
This change fixes an important chroot-mode failure mode on root-runner self-hosted setups, but there isn’t a regression test covering it. Adding an integration test that creates root-owned /tmp/gh-aw and /opt/gh-aw/safeoutputs on the host and then verifies the chrooted agent can read the MCP config and write safe-outputs would help prevent future breakage.
There was a problem hiding this comment.
Good suggestion. This would require an integration test that creates root-owned directories and verifies the chroot agent can still access them. Deferring this to a follow-up since it needs Docker/sudo and would be better suited as a chroot integration test in the existing test suite. The smoke tests passing on this PR provide some coverage in the meantime.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Security ReviewThe fix correctly addresses the root cause (root-owned directories inaccessible after privilege drop), and the However, Issue: World-writable permissions on security-sensitive directoriesFile: chmod -R a+rwX /host/tmp/gh-aw 2>/dev/null || true
# ...
chmod -R a+rwX /host/opt/gh-aw/safeoutputs 2>/dev/null || true
Concrete risks:
Recommended fix
if [ -d /host/tmp/gh-aw ]; then
chown -R "$HOST_UID:$HOST_GID" /host/tmp/gh-aw 2>/dev/null || true
echo "[entrypoint] Transferred /host/tmp/gh-aw ownership to chroot user ($HOST_UID:$HOST_GID)"
fi
if [ -d /host/opt/gh-aw/safeoutputs ]; then
chown -R "$HOST_UID:$HOST_GID" /host/opt/gh-aw/safeoutputs 2>/dev/null || true
echo "[entrypoint] Transferred /host/opt/gh-aw/safeoutputs ownership to chroot user ($HOST_UID:$HOST_GID)"
fiThis gives the agent user full access (as owner) without granting write permission to arbitrary other users on the host.
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Security Review[CONCERN]
|
Replace `chmod -R a+rwX` with `chown -R $HOST_UID:$HOST_GID` to avoid making gh-aw config directories world-writable on the host filesystem. Also make success/failure log messages conditional on the actual command result to aid debugging. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed all review feedback in 4d51fdc: Security (Copilot + Security Guard): Replaced Logging: Made success/failure messages conditional on the actual Integration test: Deferred to a follow-up — requires Docker/sudo setup and is better suited as a chroot integration test. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
Fixes self-hosted (root-runner) chroot-mode failures where gh-aw-created runtime directories are root-owned on the host, preventing the unprivileged chroot user from reading MCP config and writing safe-outputs.
Changes:
- Add a pre-privilege-drop ownership transfer (
chown -R) for/host/tmp/gh-awand/host/opt/gh-aw/safeoutputs. - Emit success/warn logs depending on whether the ownership transfer succeeds.
Show a summary per file
| File | Description |
|---|---|
| containers/agent/entrypoint.sh | Chowns host-mounted gh-aw runtime directories to the host UID/GID before dropping privileges in chroot mode. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 1/1 changed files
- Comments generated: 1
| # Transfer ownership of gh-aw config directories to the chroot user. | ||
| # On self-hosted runners these directories are created by the host-side | ||
| # gh-aw tooling as root, so the unprivileged chroot user cannot access them. | ||
| # We use chown (not chmod a+rwX) to avoid making these dirs world-writable, | ||
| # which would be a security risk on multi-user self-hosted runners. | ||
| if [ -d /host/tmp/gh-aw ]; then |
There was a problem hiding this comment.
PR title/description reference a chmod-based fix, but the implementation here uses chown -R "${HOST_UID}:${HOST_GID}" to transfer ownership. Please update the PR metadata (title/description) to match the actual behavior so reviewers/operators understand the host-side impact (ownership change vs permissions change).
|
Addressed: Updated PR title and description to reflect the |
Review FeedbackGood change overall — One significant issue: the safe-outputs path is stale. The second # Every lock file in this repo now uses:
GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonlZero lock files reference $ grep -r "/opt/gh-aw/safeoutputs" .github/workflows/
# (no results)Suggested fix: derive the safe-outputs directory from # Handle safe-outputs directory (path varies by gh-aw version)
if [ -n "${GH_AW_SAFE_OUTPUTS:-}" ]; then
# GH_AW_SAFE_OUTPUTS is e.g. /tmp/gh-aw/safeoutputs/outputs.jsonl — chown parent dir
_so_dir="/host$(dirname "$GH_AW_SAFE_OUTPUTS")"
if [ -d "$_so_dir" ]; then
if chown -R "${HOST_UID}:${HOST_GID}" "$_so_dir" 2>/dev/null; then
echo "[entrypoint] Transferred $_so_dir ownership to chroot user (${HOST_UID}:${HOST_GID})"
else
echo "[entrypoint][WARN] Failed to transfer $_so_dir ownership to chroot user"
fi
fi
fiThis is forward-compatible regardless of where gh-aw puts safe-outputs. Minor items:
|
Replace hardcoded /opt/gh-aw/safeoutputs with dynamic path derived
from the GH_AW_SAFE_OUTPUTS environment variable, making it
forward-compatible with gh-aw v0.67.0+ which moved safe-outputs
to ${RUNNER_TEMP}/gh-aw/safeoutputs/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed: replaced hardcoded /opt/gh-aw/safeoutputs with dynamic path derived from GH_AW_SAFE_OUTPUTS env var. Now forward-compatible regardless of where gh-aw puts safe-outputs. |
|
Smoke Test Results — run ✅ GitHub MCP: #1722 feat: propagate host.docker.internal to child containers, #1721 fix: enforce shared net namespace for child containers Overall: PASS
|
🤖 Smoke Test Results
PR: fix: chown gh-aw config dirs to agent user before privilege drop in entrypoint (#1463) Overall: PASS
|
Chroot Version Comparison Results
Overall: ❌ FAILED — Python and Node.js versions differ between host and chroot environment.
|
Smoke Test Status
|
Smoke Test: GitHub Actions Services Connectivity
Summary: All 3 checks failed.
|
Security ReviewThe overall direction of this PR is correct — using One minor hardening concern worth addressing:
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
Summary
On self-hosted runners where the GitHub Actions runner runs as root, AWF drops privileges to a non-root user (e.g.,
ec2-user, UID 1000) before executing the agent command. Two directories created by root during workflow setup are not accessible by the chroot user:/tmp/gh-aw/mcp-config/— MCP server discovery config (config.toml,mcp-servers.json)$GH_AW_SAFE_OUTPUTS) — safe-output write target (outputs.jsonl)This PR adds a
chown -R "${HOST_UID}:${HOST_GID}"block inentrypoint.shbefore the privilege drop (capsh) to transfer ownership of these directories to the agent user. Only the specific user gets access (not world-writable).Changes
containers/agent/entrypoint.sh(before LD_PRELOAD construction, before capsh)/host/tmp/gh-aw(covers MCP config and other gh-aw runtime files)$GH_AW_SAFE_OUTPUTSenv var (forward-compatible with any gh-aw version)[WARN]on failureTest plan
Closes #1463
🤖 Generated with Claude Code