feat: run the app image as a non-root user (E00-S02-T05)

The runtime stage of apps/server/Dockerfile now drops root privileges with
'USER node' — the non-root user (uid/gid 1000) the official Node image ships
with — so the app container does not run with root privileges. The server
binds port 3000 (>= 1024) and only reads the root-owned files copied above,
so no extra user creation or ownership changes are required. compose.yaml
header updated: T05 is in scope; T06 (read-only rootfs) and T07 (multi-arch)
remain out of scope.
This commit is contained in:
implementer
2026-08-29 00:41:10 +00:00
parent a634168d9c
commit a4cf365098
2 changed files with 20 additions and 5 deletions
+12 -2
View File
@@ -9,8 +9,10 @@
# 3000, so the app container stays up and the health endpoint succeeds. The
# Fastify 5 application shell (and the real HTTP API) lands in a later story;
# DB volume persistence (T04) is a Compose-level concern (see compose.yaml —
# this image is unchanged), while non-root/read-only hardening (T05/T06) and
# multi-arch targets remain later E00-S02 tasks — all out of scope here.
# this image is unchanged), while read-only root filesystem (T06) and
# multi-arch build targets (T07) remain later E00-S02 tasks — all out of scope
# here. Since T05 the runtime stage drops root privileges (runs as the
# image's non-root `node` user).
#
# Image base: node:24.19.0-bookworm-slim (glibc Debian) per Technology-Stack
# §5.4 — argon2 is a native dependency and musl/Alpine causes native-module
@@ -50,5 +52,13 @@ COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/apps/server/dist ./apps/server/dist
COPY --from=build /app/apps/server/package.json ./apps/server/package.json
# T05: run as the image's non-root `node` user (uid/gid 1000, shipped with the
# official Node image) so the app container does not run with root privileges.
# The server binds port 3000 (>= 1024, no privileged port needed) and only
# reads the root-owned application files copied above, so no extra user
# creation or ownership changes are required. USER is the last instruction
# before EXPOSE so every COPY above lands before the privilege drop.
USER node
EXPOSE 3000
CMD ["node", "apps/server/dist/index.js"]