fix: run manifest containers as root and drop privileges in entrypoint
The compose file was forcing the container to run as uid 1001, so the entrypoint could not create /workspace even with sudo configured. - Remove Dockerfile USER directive so containers start as root - Make compile_compose use user: 0:0 when the manifest declares a user - Make the entrypoint drop to the container user via after setup, preserving environment variables and command arguments - Update unit tests to assert root startup and privilege drop Quality gates: - pytest tests/unit: 210 passed - ruff: clean on changed files - mypy: clean on changed files
This commit is contained in:
@@ -252,9 +252,10 @@ def compile_dockerfile(manifest: dict) -> str:
|
||||
lines.append("RUN chmod +x /usr/local/bin/headquarter-entrypoint")
|
||||
lines.append("")
|
||||
|
||||
# Switch to runtime user
|
||||
if user:
|
||||
lines.append(f"USER {user['name']}")
|
||||
# Do not switch to the runtime user in the Dockerfile. The entrypoint
|
||||
# starts as root so it can create the /workspace compatibility symlink
|
||||
# (which lives under /) and fix mount ownership, then it drops privileges
|
||||
# to the container user before exec-ing the real command.
|
||||
|
||||
# Set WORKDIR to the configured home directory unless runtime.working_dir
|
||||
# explicitly overrides it.
|
||||
@@ -373,7 +374,16 @@ def compile_entrypoint(manifest: dict) -> str:
|
||||
lines.append(script)
|
||||
lines.append("")
|
||||
|
||||
lines.append('exec "$@"')
|
||||
# Drop from root to the container user before running the real command.
|
||||
# The Dockerfile no longer sets USER, so the entrypoint has root for the
|
||||
# setup above. Use `su -c` to preserve environment variables (HOME, PATH,
|
||||
# WORKSPACE_NAME, etc.) and keep the container user as the running user.
|
||||
if user:
|
||||
name = user["name"]
|
||||
lines.append("# Drop privileges to the container user")
|
||||
lines.append(f'exec su -s /bin/bash -c "exec \\"$@\\"" {name} -- "$@"')
|
||||
else:
|
||||
lines.append('exec "$@"')
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@@ -414,9 +424,12 @@ def compile_compose(manifest: dict, variables: dict[str, Any]) -> str:
|
||||
runtime["working_dir"], home_dir
|
||||
)
|
||||
|
||||
# User override
|
||||
# The entrypoint starts as root (Dockerfile does not set USER) so it can
|
||||
# create the /workspace compatibility symlink and fix mount ownership. It
|
||||
# drops privileges to the container user internally before exec-ing the
|
||||
# real command, so do not set compose-level user override here.
|
||||
if user:
|
||||
service["user"] = f"{user['uid']}:{user['gid']}"
|
||||
service["user"] = "0:0"
|
||||
|
||||
# Ports for web tools
|
||||
default_port = manifest.get("default_port")
|
||||
|
||||
Reference in New Issue
Block a user