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:
+12
-2
@@ -9,8 +9,10 @@
|
|||||||
# 3000, so the app container stays up and the health endpoint succeeds. The
|
# 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;
|
# 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 —
|
# 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
|
# this image is unchanged), while read-only root filesystem (T06) and
|
||||||
# multi-arch targets remain later E00-S02 tasks — all out of scope here.
|
# 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
|
# 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
|
# §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/dist ./apps/server/dist
|
||||||
COPY --from=build /app/apps/server/package.json ./apps/server/package.json
|
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
|
EXPOSE 3000
|
||||||
CMD ["node", "apps/server/dist/index.js"]
|
CMD ["node", "apps/server/dist/index.js"]
|
||||||
|
|||||||
+8
-3
@@ -1,4 +1,4 @@
|
|||||||
# EPPP Docker Compose baseline — [E00-S02-T01/T02/T03/T04]
|
# EPPP Docker Compose baseline — [E00-S02-T01..T05]
|
||||||
#
|
#
|
||||||
# `docker compose up -d` starts both the database (PostgreSQL) and the
|
# `docker compose up -d` starts both the database (PostgreSQL) and the
|
||||||
# application (@personal-blog/server). Rollback: `docker compose down`.
|
# application (@personal-blog/server). Rollback: `docker compose down`.
|
||||||
@@ -18,8 +18,13 @@
|
|||||||
# `docker compose down` + `docker compose up -d` (recreate, which discards the
|
# `docker compose down` + `docker compose up -d` (recreate, which discards the
|
||||||
# container filesystem). Reset the data with `docker compose down -v`.
|
# container filesystem). Reset the data with `docker compose down -v`.
|
||||||
#
|
#
|
||||||
# Explicitly out of scope for T01..T04 (land in later E00-S02 tasks):
|
# Non-root execution (T05): the app image's runtime stage runs as the official
|
||||||
# - non-root execution (T05), read-only root filesystem (T06)
|
# Node image's non-root `node` user (see apps/server/Dockerfile — `USER node`),
|
||||||
|
# so the app container does not run with root privileges. No Compose-level
|
||||||
|
# `user:` override is needed: the image's USER is inherited by the container.
|
||||||
|
#
|
||||||
|
# Explicitly out of scope for T01..T05 (land in later E00-S02 tasks):
|
||||||
|
# - read-only root filesystem (T06), multi-arch build targets (T07)
|
||||||
#
|
#
|
||||||
# All values have defaults so `docker compose up -d` works from a clean clone
|
# All values have defaults so `docker compose up -d` works from a clean clone
|
||||||
# without a .env file (a committed .env.example template lands in E00-S04).
|
# without a .env file (a committed .env.example template lands in E00-S04).
|
||||||
|
|||||||
Reference in New Issue
Block a user