feat: keep secrets out of the app image (E00-S02-T08)

- .dockerignore: exclude env + credential files (.npmrc, .netrc, .aws, .ssh,
  secrets/, *.pem, *.key, *.p12, *.pfx, *.jks, id_rsa, id_ed25519, ...) from
  the build context so a local secret file cannot be embedded in the image
- Dockerfile: document the T08 guarantee (no secret ARG/ENV, fixed non-secret
  COPY paths, runtime credentials via Compose environment)
- compose.yaml: T08 in scope; runtime credentials stay in service environment,
  never in the image
This commit is contained in:
implementer
2026-08-29 01:28:26 +00:00
parent 25d7dc836b
commit 0938da8a73
3 changed files with 36 additions and 3 deletions
+18
View File
@@ -14,5 +14,23 @@ coverage
.env .env
.env.* .env.*
# Secrets & credentials (E00-S02-T08) — never part of the build context, so a
# secret-bearing file cannot be embedded in the image even if a developer has
# one locally. Keep this list in sync with tests/secrets-not-embedded.test.mjs.
.npmrc
.netrc
.credentials
.aws
.ssh
secrets
secrets/
*.pem
*.key
*.p12
*.pfx
*.jks
id_rsa
id_ed25519
# Logs # Logs
*.log *.log
+9
View File
@@ -17,6 +17,15 @@
# T05 the runtime stage drops root privileges (runs as the image's non-root # T05 the runtime stage drops root privileges (runs as the image's non-root
# `node` user). # `node` user).
# #
# T08: the image embeds no secrets. The Dockerfile declares no secret-bearing
# ARG/ENV instruction (the only ENV is `NODE_ENV=production`) and every COPY
# copies a fixed, non-secret path (manifests, source, compiled dist) — never
# `.env` or credential files; `.dockerignore` additionally excludes env and
# credential files from the build context, so a local secret file cannot be
# embedded even by mistake. Runtime credentials (e.g. DATABASE_URL) are
# injected by Compose at run time (compose.yaml `app.environment`), never baked
# into the image. Tests: tests/secrets-not-embedded.test.mjs.
#
# 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
# build surprises, so the image must stay on a glibc base. # build surprises, so the image must stay on a glibc base.
+9 -3
View File
@@ -1,4 +1,4 @@
# EPPP Docker Compose baseline — [E00-S02-T01..T07] # EPPP Docker Compose baseline — [E00-S02-T01..T08]
# #
# `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`.
@@ -37,8 +37,14 @@
# so local runs and the T01..T06 real-stack probes are unaffected. Rollback: # so local runs and the T01..T06 real-stack probes are unaffected. Rollback:
# drop the `platforms` list from the `app` build config. # drop the `platforms` list from the `app` build config.
# #
# Explicitly out of scope for T01..T07 (land in a later E00-S02 task): # Secrets not embedded (T08): the app image carries no secrets — the committed
# - secrets not embedded (T08) # apps/server/Dockerfile declares no secret-bearing ARG/ENV instruction and
# copies only fixed, non-secret paths, and the committed .dockerignore excludes
# env and credential files from the build context. Runtime credentials are
# injected here, at run time, via service `environment` values (the app's
# DATABASE_URL, the db service's POSTGRES_* defaults) — they live in
# Compose/deploy config, never in the image. Rollback: rebuild the image after
# removing any embedded secret. Tests: tests/secrets-not-embedded.test.mjs.
# #
# 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).