fix: chown staged mount sources to container user

Config-profile and git mounts staged under instance_dir were created
by the API process (root), so when bind-mounted over ~/.config the
container user could not write. Recursively chown staged sources to
the resolved container uid/gid before compose up.

Quality gates: python3 -m py_compile passed; ruff/pytest skipped
(test tooling not available in this shell, helper smoke tested
with a temporary directory).
This commit is contained in:
Developer
2026-06-12 22:08:57 +00:00
parent 3b772ac239
commit 72bf4ed962
@@ -1,6 +1,7 @@
"""Tool instance service functions."""
import asyncio
import contextlib
import glob as glob_module
import logging
import os
@@ -71,6 +72,41 @@ from src.auth.dependencies import _get_owned_project, _get_user
logger = logging.getLogger(__name__)
_event_bus = InstanceEventBus()
def _chown_path(path: str, uid: int, gid: int) -> None:
"""Recursively chown a path, suppressing permission errors."""
try:
if os.path.isdir(path):
for root, dirs, files in os.walk(path):
for name in dirs + files:
full = os.path.join(root, name)
with contextlib.suppress(OSError):
os.chown(full, uid, gid)
with contextlib.suppress(OSError):
os.chown(path, uid, gid)
except Exception as exc:
logger.warning("Failed to chown %s to %s:%s: %s", path, uid, gid, exc)
def _chown_staged_mounts(
extra_volumes: list[dict],
instance_dir: str,
uid: int,
gid: int,
) -> None:
"""Recursively chown staged mount sources to the container user.
Config-profile mounts, git mounts, and SSH key mounts are staged under
instance_dir by the API process (root). Without this, the container
user cannot write into bind-mounted directories such as ~/.config.
"""
for vol in extra_volumes:
source = vol.get("source", "")
if not source or not source.startswith(instance_dir):
continue
_chown_path(source, uid, gid)
async def resolve_git_mounts(
session: AsyncSession,
resolved: ResolvedProfile,
@@ -1308,6 +1344,10 @@ async def start_tool_instance(
ssh_target,
)
# Ensure staged bind-mount sources are owned by the container user so
# directories like ~/.config remain writable inside the container.
_chown_staged_mounts(extra_volumes, instance_dir, container_uid, container_gid)
# ── MANIFEST-BASED FLOW ──────────────────────────────────────
resolved_manifest = None