From 6ae04c71f7f351138989014ea30b392760e70a25 Mon Sep 17 00:00:00 2001 From: CueCrux Date: Thu, 20 Aug 2026 22:10:15 +0100 Subject: [PATCH] chore(site): migrate scorecrux.com preview image to Chainguard nginx ExecPlan chainguard-image-migration, M2 (frontdoor / web-facing). site/Dockerfile: FROM nginx:alpine -> cgr.dev/chainguard/nginx:latest, and EXPOSE 80 -> EXPOSE 8080. Why the port moves: upstream nginx:alpine runs its master as root and its bundled server block listens on 80. The Chainguard image runs as uid 65532, which cannot bind a privileged port, so its /etc/nginx/conf.d/nginx.default.conf listens on 8080 instead. The choice is either to keep port 80 by shipping a custom config plus a UID change (giving back the non-root posture that is the point of the migration), or to follow the base and move the published port. This takes the second option; nothing in this repo or in a sibling compose/proxy consumes the image, so the only consumer is the documented local run command, updated in the header comment from `-p 8888:80` to `-p 8888:8080`. What deliberately did not change: the document root is /usr/share/nginx/html on both bases, so the COPY is untouched and no custom nginx config is needed. The base sets ENTRYPOINT ["/usr/sbin/nginx"] with a CMD carrying the config flags; both are left alone rather than overridden, since an added CMD would append to that entrypoint. The runtime has no shell, so any future addition here must be exec form. Supply-chain note: no digest pin was dropped -- the previous base was the floating tag `nginx:alpine`, already unpinned. Free-tier Chainguard is :latest-only, so the new base cannot be pinned either (plan policy, 2026-04-10). Verified locally: `docker build -f site/Dockerfile -t cg-test-scorecrux-site:probe site/` exit 0; container on -p 8091:8080 returned HTTP 200 for `/` with a body byte-identical to site/index.html (26485 bytes) and 404 for an unknown path; nginx 1.31.4, only the benign "user directive ignored" warning from the base's own nginx.conf. Co-Authored-By: Claude Opus 5 (1M context) --- site/Dockerfile | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/site/Dockerfile b/site/Dockerfile index 5ebe438..0a3554e 100644 --- a/site/Dockerfile +++ b/site/Dockerfile @@ -1,3 +1,20 @@ -FROM nginx:alpine +# Static preview image for scorecrux.com. +# +# Chainguard nginx (rebuilt daily, zero known CVEs) per CLAUDE.md container policy. +# Free-tier Chainguard is :latest-only, so this base cannot carry a digest pin. +# +# Two behavioural differences vs the previous nginx:alpine base: +# * it runs as non-root (uid 65532), so it cannot bind a privileged port; +# * its bundled conf.d/nginx.default.conf therefore listens on 8080, not 80. +# The document root is unchanged (/usr/share/nginx/html), so the COPY below is +# the same as before and no custom nginx config is needed. +# +# The base sets ENTRYPOINT ["/usr/sbin/nginx"] plus a CMD carrying the config +# flags; both are deliberately left alone. The runtime has no shell, so anything +# added here must be exec form. +# +# docker build -t scorecrux-site site/ +# docker run --rm -p 8888:8080 scorecrux-site # was -p 8888:80 +FROM cgr.dev/chainguard/nginx:latest COPY index.html /usr/share/nginx/html/index.html -EXPOSE 80 +EXPOSE 8080