Skip to content

harden(deploy): run SMCP container as non-root (uid 10001)#6

Merged
taferh merged 1 commit into
swiss-rocketsfrom
feat/smcp-nonroot
Jun 12, 2026
Merged

harden(deploy): run SMCP container as non-root (uid 10001)#6
taferh merged 1 commit into
swiss-rocketsfrom
feat/smcp-nonroot

Conversation

@taferh

@taferh taferh commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

What

Run the SMCP container as a non-root user (uid 10001) instead of root.

Why

The image had no USER directive, so it ran as root. The target FluxCD/registry deploy enforces the restricted PodSecurity Standard, which requires runAsNonRoot: true — a root image is rejected at admission. This is a prerequisite for that deploy path (builds on the AL2023 rebase, #5 / ADR-0011).

Changes

  • deploy/Dockerfile — after every root-owned build step:
    • Create uid/gid 10001(app) by appending to /etc/passwd + /etc/group directly. AL2023's minimal base has no shadow-utils (no useradd); the manual entry adds zero packages, preserving the minimal CVE surface that motivated the AL2023 rebase.
    • Relocate the TU workspace from /root/.tooluniverse (mode 0700, root-only) to /home/app/.tooluniverse; set HOME + TU_DATA_DIR accordingly.
    • USER 10001 (numeric, so runAsNonRoot validates the non-zero uid without name resolution).
  • deploy/docker-compose.yml — volume mount follows to /home/app/.tooluniverse.

Verification (local podman build)

  • PID 1 runs as uid=10001(app)
  • TU_DATA_DIR writable; /app read-only to the app user ✓
  • 76 served-skill bodies readable (cp'd world-readable, no chown needed) ✓
  • Server boots, binds :8000 in ~10s, no permission/read-only errors ✓

Deploy follow-ups (not fixable in-image)

  • K8s pod spec needs securityContext: { runAsNonRoot: true, runAsUser: 10001, fsGroup: 10001 }fsGroup makes the PVC mounted at /home/app/.tooluniverse writable (a fresh PVC mounts root-owned, unlike a compose named volume which inherits the image dir's 10001 ownership).
  • sempart host .env must repoint TU_DATA_DIR to /home/app/.tooluniverse/cache, since env_file overrides the image ENV. The existing volume holds only ~72 KB of disposable cache, so the path switch loses nothing.

The SMCP image ran as root (no USER directive). The target cluster
(FluxCD/registry deploy) enforces the restricted PodSecurity Standard,
which requires runAsNonRoot — the root image would be rejected outright.

Changes:
- Create a uid/gid 10001 account by appending to /etc/passwd directly.
  AL2023's minimal base ships no shadow-utils (no useradd); the manual
  entry adds zero packages, keeping the CVE surface minimal per the
  ADR-0011 AL2023 rebase.
- Relocate the TU workspace from /root/.tooluniverse (mode 0700,
  root-only) to the app HOME; set HOME + TU_DATA_DIR accordingly.
- Add USER 10001 after every root-owned build step (pip install,
  served-skills COPY/RUN write to /app). served-skills are cp'd
  world-readable, so no chown is needed there.
- docker-compose volume mount follows to /home/app/.tooluniverse.

Verified with a local podman build: PID 1 runs as uid 10001, the
workspace is writable, /app is read-only, all 76 served-skill bodies are
readable, and the server binds :8000 with no permission errors.

Deploy follow-ups (cannot be fixed in-image):
- K8s pods need securityContext runAsUser: 10001 + fsGroup: 10001 so the
  PVC mounted at /home/app/.tooluniverse is writable.
- sempart's host .env must repoint TU_DATA_DIR to
  /home/app/.tooluniverse/cache, since env_file overrides the image ENV.
@taferh taferh merged commit ea73316 into swiss-rockets Jun 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants