markhoerth opened a new pull request, #12446:
URL: https://github.com/apache/gravitino/pull/12446
### What changes were proposed in this pull request?
Makes the MCP server image runnable as a non-root user, using the same setup
the other Gravitino images already have.
| Change | Detail |
| --- | --- |
| Move off `/root` | `/root/mcp-server` becomes `/opt/mcp-server`, updating
`WORKDIR`, both `COPY` destinations, and the `cd` in `start-mcp-server.sh` |
| Adopt the existing non-root setup | `COPY --chmod=775`, `useradd -u 1000
-g 0 -M -s /sbin/nologin gravitino`, and `USER 1000`, as in the `gravitino`,
`iceberg-rest-server` and `lance-rest-server` Dockerfiles |
| Recursive `chmod` | `chmod -R 775`, rather than the non-recursive form
those images use |
| Pin the uv cache | `ENV UV_CACHE_DIR=/opt/mcp-server/.cache/uv` |
Two of those differ from the sibling images, and both are required here
rather than stylistic.
The `chmod` is recursive because `uv venv`, `uv sync` and `uv pip install -e
.` run after the `COPY` and create `.venv` and the uv cache as root at mode
755. `COPY --chmod` cannot reach them, so a non-recursive `chmod 775` on the
top directory alone would leave both inaccessible to an arbitrary UID.
`UV_CACHE_DIR` is needed because uv defaults its cache to `$HOME/.cache/uv`.
A user with no passwd entry gets `HOME=/`, which is not writable, so even after
the working directory moved the entrypoint still failed:
```
error: Failed to initialize cache at `/.cache/uv`
Caused by: failed to create directory `/.cache/uv`: Permission denied
```
Pinning the cache inside the application directory means it is populated as
root at build time and picked up by the same `chmod`.
No application code changes, no change to the base image, no change to the
entrypoint's behaviour beyond the paths it depends on, and no Helm chart
changes.
### Why are the changes needed?
The image could only run as root. Setting `runAsUser: 1001` on the container
made it crash immediately:
```
start-mcp-server.sh: line 21: cd: /root/mcp-server: Permission denied
error: failed to open file `/root/mcp-server/uv.toml`: Permission denied (os
error 13)
```
`/root` is mode 0700 in the `python:3.10` base, so a process running as any
other user cannot enter its own working directory. A `USER` directive alone
would not have fixed it; the application had to move.
Kubernetes deployments increasingly require a non-root container, and
OpenShift enforces it: its default security context constraint assigns an
arbitrary UID and refuses an image that needs root. Membership of gid 0, with
group permissions matching user permissions, is what makes an arbitrary
assigned UID work, which is why the other Gravitino images are built that way.
The Helm charts set a non-root security context on the other components. The
MCP server is currently the exception that has to be left out.
### Does this PR introduce _any_ user-facing change?
Yes, two.
The image runs as uid 1000 by default instead of root. A deployment that
depends on running as root needs updating.
The application lives at `/opt/mcp-server` instead of `/root/mcp-server`. A
deployment that mounts a volume into the old path needs updating.
No API or configuration property changes.
### How was this patch tested?
Built locally and run as several users. All exited 0 and printed usage with
no permission error:
```shell
docker run --rm --user 1000 <image> --help
docker run --rm --user 1001 <image> --help
docker run --rm --user 4238721 <image> --help # simulates OpenShift's
arbitrary UID
docker run --rm <image> --help
```
The arbitrary high UID is the case the gid 0 group ownership exists for, and
the one a plain `--user 1000` does not exercise.
`--help` alone was not treated as sufficient, since startup touches paths it
does not. The server was also started for real in streamable-http mode as uid
4238721, and reached `Application startup complete` with no permission errors,
confirming `.venv` and the uv cache are reachable at runtime.
Inside the running container the identity is `uid=4238721 gid=0(root)`, and
`/opt/mcp-server`, `/opt/mcp-server/.venv` and `/opt/mcp-server/.cache/uv` are
all `drwxrwxr-x root root`.
The original failure was found on a Kubernetes cluster, by patching a
running MCP deployment with `runAsUser: 1001` and `runAsNonRoot: true`, which
produced the CrashLoopBackOff and the two lines quoted above.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]