fix: run tool terminal sessions as container user instead of root
- Remove compose-level user: 0:0 override from manifest_compiler.py so the entrypoint can start as root, fix mount ownership, and drop privileges to the container user internally. - Add get_manifest_container_user() helper to resolve the manifest-declared container user (with uid:gid fallback). - Pass container user through TerminalSession, TerminalManager, and the terminal WebSocket handler so docker exec is invoked with --user <user>. - Update and add unit tests for the manifest compiler and terminal session. - Record the additional root-user fix in the fix-pi-container-mount-permissions OpenSpec change/tasks. Quality gates: pytest tests/unit/ (226 passed), pytest tests/services/test_terminal_manager_multi.py (7 passed), ruff check on changed files (clean), mypy on changed files (clean)
This commit is contained in:
@@ -72,9 +72,10 @@ class TerminalManager:
|
||||
session_id,
|
||||
instance_id,
|
||||
)
|
||||
session = self._sessions.pop(key, None)
|
||||
if session:
|
||||
await session.close()
|
||||
idle_session = self._sessions.get(key)
|
||||
if idle_session is not None:
|
||||
del self._sessions[key]
|
||||
await idle_session.close()
|
||||
# Update DB status fire-and-forget
|
||||
asyncio.create_task(self._mark_closed_in_db(session_id))
|
||||
|
||||
@@ -141,6 +142,7 @@ class TerminalManager:
|
||||
startup_command: str | None = None,
|
||||
name: str | None = None,
|
||||
session_id: str | None = None,
|
||||
container_user: str | None = None,
|
||||
) -> TerminalSession:
|
||||
"""Create a new terminal session for an instance.
|
||||
|
||||
@@ -152,6 +154,8 @@ class TerminalManager:
|
||||
container_id: Docker container ID.
|
||||
startup_command: Optional startup command to run.
|
||||
name: Optional session name (auto-generated if omitted).
|
||||
session_id: Optional explicit session UUID.
|
||||
container_user: Optional container user for docker exec.
|
||||
|
||||
Returns:
|
||||
The newly created TerminalSession.
|
||||
@@ -177,6 +181,7 @@ class TerminalManager:
|
||||
container_id=container_id,
|
||||
startup_command=startup_command,
|
||||
name=name,
|
||||
container_user=container_user,
|
||||
)
|
||||
await session.start(startup_command=startup_command)
|
||||
|
||||
@@ -201,6 +206,7 @@ class TerminalManager:
|
||||
instance_id: uuid.UUID,
|
||||
container_id: str,
|
||||
startup_command: str | None = None,
|
||||
container_user: str | None = None,
|
||||
) -> TerminalSession:
|
||||
"""Get existing session or create a new one.
|
||||
|
||||
@@ -243,6 +249,7 @@ class TerminalManager:
|
||||
container_id=container_id,
|
||||
startup_command=startup_command,
|
||||
name="Session 1",
|
||||
container_user=container_user,
|
||||
)
|
||||
await session.start(startup_command=startup_command)
|
||||
self._sessions[key] = session
|
||||
@@ -358,6 +365,7 @@ class TerminalManager:
|
||||
startup_command: str | None = None,
|
||||
session_id: str | None = None,
|
||||
name: str | None = None,
|
||||
container_user: str | None = None,
|
||||
) -> TerminalSession:
|
||||
"""Reset a session by killing it and creating a new one.
|
||||
|
||||
@@ -367,6 +375,7 @@ class TerminalManager:
|
||||
startup_command: Optional startup command.
|
||||
session_id: Specific session to reset. If None, resets the default session.
|
||||
name: Optional name to preserve for the new session.
|
||||
container_user: Optional container user for docker exec.
|
||||
|
||||
Returns:
|
||||
The newly created TerminalSession.
|
||||
@@ -400,6 +409,7 @@ class TerminalManager:
|
||||
container_id=container_id,
|
||||
startup_command=startup_command,
|
||||
name=old_name or ("Session 1" if target_session_id == "default" else None),
|
||||
container_user=container_user,
|
||||
)
|
||||
await new_session.start(startup_command=startup_command)
|
||||
self._sessions[key] = new_session
|
||||
|
||||
Reference in New Issue
Block a user