From 581223e382237b0b0a70ce66924df54b7d3a3035 Mon Sep 17 00:00:00 2001 From: marcie Date: Sun, 30 Aug 2026 16:59:34 -0500 Subject: [PATCH] fix: preserve credential lineage in dogfood container (#1487) --- README.md | 3 ++- docker-compose.dogfood.yml | 3 +++ docker/gateway.Dockerfile | 6 +++++- scripts/verify-dogfood-compose.sh | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d18525a4..ea596eca 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,8 @@ The overlay removes the general shell tool for every session, including admins. File tools stay inside the mounted checkout. Two dedicated delivery tools stage explicit paths, run the CI queue guard, push through `git-credential-mosaic`, and open PRs through `pr-create.sh`. They resolve only the `stack-dogfood` slot and fail -if it is absent. +if it is absent. The overlay enables Docker's init process so the R4 helper can +establish the gateway's seat lineage below PID 1. This deployment route is separate from the local source-development restrictions below. diff --git a/docker-compose.dogfood.yml b/docker-compose.dogfood.yml index 266a1dc3..cc08567f 100644 --- a/docker-compose.dogfood.yml +++ b/docker-compose.dogfood.yml @@ -2,6 +2,9 @@ # Use with docker-compose.yml. The base stack remains credential-free. services: gateway: + # The R4 credential helper establishes ownership from process ancestry and + # intentionally does not trust PID 1. Keep gateway Node below Docker's init. + init: true environment: # Identity and credential layout match a fleet seat. This fixed name prevents # an operator from mounting one seat while attributing actions to another. diff --git a/docker/gateway.Dockerfile b/docker/gateway.Dockerfile index 06836050..9a713491 100644 --- a/docker/gateway.Dockerfile +++ b/docker/gateway.Dockerfile @@ -33,7 +33,6 @@ ENV NODE_ENV=production # bash/curl/python3 are runtime dependencies of the provider-neutral Mosaic git # wrappers. jq supports wrapper discovery for non-canonical Gitea hosts. RUN apk add --no-cache bash curl git jq python3 \ - && ln -sf /bin/bash /usr/bin/bash \ && mkdir -p /opt/mosaic/.workspaces \ && chown -R node:node /opt/mosaic /app ENV MOSAIC_ROOT=/opt/mosaic @@ -45,6 +44,11 @@ COPY --from=builder /app/packages/mosaic/framework/tools/git/ci-queue-wait.sh /o COPY --from=builder /app/packages/mosaic/framework/tools/git/detect-platform.sh /opt/mosaic/tools/git/detect-platform.sh COPY --from=builder /app/packages/mosaic/framework/tools/git/repo-decl.sh /opt/mosaic/tools/git/repo-decl.sh COPY --from=builder /app/packages/mosaic/framework/tools/git/git-credential-mosaic /opt/mosaic/tools/git/git-credential-mosaic +# R4 hardening (P0-SEC, brain 15f6979a): the credential helper is a pair. +# python entrypoint (allowlist envp, execve boundary) + the bash implementation +# it execs. The entrypoint derives the .impl path from its own directory, so the +# pair sits side by side; system gitconfig keeps pointing at the entrypoint. +COPY --from=builder /app/packages/mosaic/framework/tools/git/git-credential-mosaic.impl /opt/mosaic/tools/git/git-credential-mosaic.impl COPY --from=builder /app/packages/mosaic/framework/tools/_lib/credentials.sh /opt/mosaic/tools/_lib/credentials.sh COPY --from=builder /app/packages/mosaic/framework/tools/structure/validate-repo-json.sh /opt/mosaic/tools/structure/validate-repo-json.sh RUN git config --system credential.helper /opt/mosaic/tools/git/git-credential-mosaic diff --git a/scripts/verify-dogfood-compose.sh b/scripts/verify-dogfood-compose.sh index 0e34a922..0163decd 100755 --- a/scripts/verify-dogfood-compose.sh +++ b/scripts/verify-dogfood-compose.sh @@ -57,6 +57,7 @@ import os config = json.loads(os.environ["CONFIG_JSON"]) gateway = config["services"]["gateway"] +assert gateway.get("init") is True, "gateway must run below an init process for R4 lineage" env = gateway["environment"] expected_env = {