[Go-Live Checklist][Container] Docker hardening: non-root USER, --no-install-recommends, disable curl/wget redirects (SonarQube hotspots S6471/S6500/S6506) #659

Open
opened 2026-07-17 15:27:18 +00:00 by spikerj · 2 comments
Owner

From the SonarQube security-hotspot review (2026-07-17). After clearing all code hotspots (90 reviewed SAFE) and the recursive-COPY hotspots (29 — mitigated by .dockerignore, verified), these remaining docker hotspots are REAL hardening gaps (not false positives), deferred to a dedicated pre-production container-hardening pass rather than churned blind during active development:

  • S6471 — runs as root (27 Dockerfiles): the final image has no USER directive, so containers run as root. Real defense-in-depth gap. NOT a drive-by fix: several services legitimately need care — e.g. NodeAgent runs nsenter for host commands, some bind low ports, some write to mounted volumes — so adding 'USER nonroot' must be done and TESTED per service (volume perms, port binds, privileged ops), not bulk-applied.
  • S6500 — apt installs recommended packages (4): add --no-install-recommends to shrink image / attack surface.
  • S6506 — HTTP client redirects not disabled (3 docker + 1 shell): curl/wget follow redirects; add --max-redirs 0 / -fsSL discipline where fetching from fixed URLs.

Recommend a single 'container hardening' PR per service (or a batch with a matrix test) before production. Dev-environment-acceptable today (same posture as #633 secret rotation). Left as status=TO_REVIEW in SonarQube deliberately — these are acknowledged real items, not cleared.

From the SonarQube security-hotspot review (2026-07-17). After clearing all code hotspots (90 reviewed SAFE) and the recursive-COPY hotspots (29 — mitigated by .dockerignore, verified), these remaining docker hotspots are REAL hardening gaps (not false positives), deferred to a dedicated pre-production container-hardening pass rather than churned blind during active development: - **S6471 — runs as root (27 Dockerfiles):** the final image has no USER directive, so containers run as root. Real defense-in-depth gap. NOT a drive-by fix: several services legitimately need care — e.g. NodeAgent runs nsenter for host commands, some bind low ports, some write to mounted volumes — so adding 'USER nonroot' must be done and TESTED per service (volume perms, port binds, privileged ops), not bulk-applied. - **S6500 — apt installs recommended packages (4):** add --no-install-recommends to shrink image / attack surface. - **S6506 — HTTP client redirects not disabled (3 docker + 1 shell):** curl/wget follow redirects; add --max-redirs 0 / -fsSL discipline where fetching from fixed URLs. Recommend a single 'container hardening' PR per service (or a batch with a matrix test) before production. Dev-environment-acceptable today (same posture as #633 secret rotation). Left as status=TO_REVIEW in SonarQube deliberately — these are acknowledged real items, not cleared.
Author
Owner

Current state on origin/master (partial — leaving open):

  • Non-root USER (S6471): 19 of 32 Dockerfiles now declare a USER directive; 13 still have none and their final images run as root. Those 13 are the remaining work for this part.
  • --no-install-recommends (S6500): present in some (e.g. SpikerSoft.Api, SpikerSoft.AI.MCPServer, SpikerSoft.EventHandlers.ArtPipeProcessor) but not audited across all apt-using images.
  • curl/wget redirect hardening (S6506): not evidently applied.

So the non-root migration is well underway but not complete, and the S6500/S6506 legs are unfinished. Not closeable yet. (Note: no check-dockerfile-nonroot guard script exists on master, despite an earlier assumption one had landed — a CI guard would prevent regressions once the 13 stragglers are fixed.)

Current state on `origin/master` (partial — leaving open): - **Non-root USER (S6471):** 19 of 32 Dockerfiles now declare a `USER` directive; **13 still have none** and their final images run as root. Those 13 are the remaining work for this part. - **`--no-install-recommends` (S6500):** present in some (e.g. `SpikerSoft.Api`, `SpikerSoft.AI.MCPServer`, `SpikerSoft.EventHandlers.ArtPipeProcessor`) but not audited across all apt-using images. - **curl/wget redirect hardening (S6506):** not evidently applied. So the non-root migration is well underway but not complete, and the S6500/S6506 legs are unfinished. Not closeable yet. (Note: no `check-dockerfile-nonroot` guard script exists on master, despite an earlier assumption one had landed — a CI guard would prevent regressions once the 13 stragglers are fixed.)
Author
Owner

Audited against origin/masterPARTIAL, and substantially further along than the ticket suggests. Concrete numbers so the remainder is a checklist rather than a sweep.

Non-root USER directive: 20 of 34 Dockerfiles. The 14 still running as root:

SpikerSoft.EventHandlers.BlogMediaProcessor/Dockerfile
SpikerSoft.EventHandlers.BookManagement/Dockerfile
SpikerSoft.EventHandlers.FileMovement/Dockerfile
SpikerSoft.EventHandlers.GameEvents/Dockerfile
SpikerSoft.EventHandlers.GpuCoordinator/Dockerfile
SpikerSoft.EventHandlers.ImageDescription.Python/Dockerfile
SpikerSoft.EventHandlers.LessonVideoProcessor/Dockerfile
SpikerSoft.EventHandlers.MetadataExtractor/Dockerfile
SpikerSoft.EventHandlers.PhotographProcessor/Dockerfile
SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile
SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile.old
SpikerSoft.GameServer.Initializer/Dockerfile
SpikerSoft.GameServer/Dockerfile
SpikerSoft.NxCacheServer/Dockerfile

--no-install-recommends: 19 of 34.

Two observations worth acting on:

1. Dockerfile.old is in the list and shouldn't be. SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile.old is dead weight — it should be deleted rather than hardened, and it's inflating the S6471 count. Worth checking whether the SonarQube finding total includes it.

2. The regression guard from the earlier api-side work does not exist here. My notes recorded a check-dockerfile-nonroot.sh guard landing with the api.spikersoft.com hotspot review (PR #433). Searching this repo for nonroot/non-root returns nothing — so whatever guard exists isn't in spikersoft-backend, and nothing prevents the next new Dockerfile from arriving as root. Given 20 of 34 are already done, adding the guard now is what makes that progress durable; without it this ticket will partially regress.

Suggested split: the 14 are not equivalent. GameServer, GameServer.Initializer and NxCacheServer are .NET services where a USER line is near-trivial. The two Python ones (ImageDescription, Trellis3D) and the GPU-adjacent workers may need care around device access and volume ownership, so they're a separate slice. Doing the easy eleven plus the guard would take this from 20/34 to 31/34 with the ratchet in place.

The curl/wget redirect item (S6506) I did not check — flagging that as unverified rather than implying it's covered.

Audited against `origin/master` — **PARTIAL, and substantially further along than the ticket suggests.** Concrete numbers so the remainder is a checklist rather than a sweep. **Non-root `USER` directive: 20 of 34 Dockerfiles.** The 14 still running as root: ``` SpikerSoft.EventHandlers.BlogMediaProcessor/Dockerfile SpikerSoft.EventHandlers.BookManagement/Dockerfile SpikerSoft.EventHandlers.FileMovement/Dockerfile SpikerSoft.EventHandlers.GameEvents/Dockerfile SpikerSoft.EventHandlers.GpuCoordinator/Dockerfile SpikerSoft.EventHandlers.ImageDescription.Python/Dockerfile SpikerSoft.EventHandlers.LessonVideoProcessor/Dockerfile SpikerSoft.EventHandlers.MetadataExtractor/Dockerfile SpikerSoft.EventHandlers.PhotographProcessor/Dockerfile SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile.old SpikerSoft.GameServer.Initializer/Dockerfile SpikerSoft.GameServer/Dockerfile SpikerSoft.NxCacheServer/Dockerfile ``` **`--no-install-recommends`: 19 of 34.** Two observations worth acting on: **1. `Dockerfile.old` is in the list and shouldn't be.** `SpikerSoft.EventHandlers.Trellis3D.Python/Dockerfile.old` is dead weight — it should be deleted rather than hardened, and it's inflating the S6471 count. Worth checking whether the SonarQube finding total includes it. **2. The regression guard from the earlier api-side work does not exist here.** My notes recorded a `check-dockerfile-nonroot.sh` guard landing with the api.spikersoft.com hotspot review (PR #433). Searching this repo for `nonroot`/`non-root` returns **nothing** — so whatever guard exists isn't in spikersoft-backend, and nothing prevents the next new Dockerfile from arriving as root. Given 20 of 34 are already done, adding the guard now is what makes that progress durable; without it this ticket will partially regress. **Suggested split:** the 14 are not equivalent. `GameServer`, `GameServer.Initializer` and `NxCacheServer` are .NET services where a `USER` line is near-trivial. The two Python ones (`ImageDescription`, `Trellis3D`) and the GPU-adjacent workers may need care around device access and volume ownership, so they're a separate slice. Doing the easy eleven plus the guard would take this from 20/34 to 31/34 with the ratchet in place. The curl/wget redirect item (S6506) I did not check — flagging that as unverified rather than implying it's covered.
Sign in to join this conversation.