Problem
`cybersandbox/Dockerfile:150` ends with:
```dockerfile
── Drop to non-root (use UID to avoid passwd lookup issues) ─
USER 1000
```
This was intended as a hardening step, but it breaks compatibility with the upstream entrypoint contract.
Upstream `/opt/gem/run.sh` (which our overlay `entrypoint-cyber.sh` `exec`s) expects root at entry so it can:
- `useradd` / `groupadd` the runtime user
- `chown -R $USER:$USER /opt/jupyter`
- Write `/etc/sudoers.d/$USER`
…and then it self-drops to `$USER` via `su -` before `exec`ing supervisord. Every supervised process (nginx, jupyter, code-server, mcp-hub, browser, VNC) ends up running as the non-root user anyway. The `USER 1000` directive in our Dockerfile is redundant hardening that actually breaks the container.
Current workaround
PR #16 works around this with a `user: "0:0"` override in `docker-compose.yaml`, plus a comment pointing at this issue.
Proper fix
- Delete line 150 (`USER 1000`) and the preceding comment in `cybersandbox/Dockerfile`.
- Rebuild the image, publish `:latest` via the existing `cybersandbox-build.yml` workflow (build + trivy + cosign + SBOM already wired up).
- Drop `user: "0:0"` from `docker-compose.yaml` in a follow-up PR.
- Verify: `docker compose up -d` → healthy → supervisord services list shows all processes still owned by `hunter`, not root.
Repro
On a clean WSL / Linux host pulling the current `:latest`:
```
docker pull ghcr.io/prowlrbot/cybersandbox:latest
docker run --rm ghcr.io/prowlrbot/cybersandbox:latest
... tool check ok ... "CyberSandbox Ready" ...
chown: changing ownership of '/opt/jupyter': Operation not permitted
container exits, docker restarts, loop forever
```
Acceptance
Related: #16
Problem
`cybersandbox/Dockerfile:150` ends with:
```dockerfile
── Drop to non-root (use UID to avoid passwd lookup issues) ─
USER 1000
```
This was intended as a hardening step, but it breaks compatibility with the upstream entrypoint contract.
Upstream `/opt/gem/run.sh` (which our overlay `entrypoint-cyber.sh` `exec`s) expects root at entry so it can:
…and then it self-drops to `$USER` via `su -` before `exec`ing supervisord. Every supervised process (nginx, jupyter, code-server, mcp-hub, browser, VNC) ends up running as the non-root user anyway. The `USER 1000` directive in our Dockerfile is redundant hardening that actually breaks the container.
Current workaround
PR #16 works around this with a `user: "0:0"` override in `docker-compose.yaml`, plus a comment pointing at this issue.
Proper fix
Repro
On a clean WSL / Linux host pulling the current `:latest`:
```
docker pull ghcr.io/prowlrbot/cybersandbox:latest
docker run --rm ghcr.io/prowlrbot/cybersandbox:latest
... tool check ok ... "CyberSandbox Ready" ...
chown: changing ownership of '/opt/jupyter': Operation not permitted
container exits, docker restarts, loop forever
```
Acceptance
docker compose up -dactually work #16's `user: "0:0"` comment + override removed in the follow-up PR.Related: #16