From 0cd3bfe34ce03de0de8e25b61c8338b5b27a340a Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sun, 1 Nov 2026 19:47:25 -0800 Subject: [PATCH 1/3] fix(build): normalize copied file modes in component images Signed-off-by: Kevin Wang --- dstack/gateway/dstack-app/builder/Dockerfile | 10 +++++----- dstack/gateway/dstack-app/builder/README.md | 5 +++++ dstack/kms/dstack-app/builder/Dockerfile | 8 ++++---- dstack/kms/dstack-app/builder/README.md | 5 +++++ dstack/verifier/builder/Dockerfile | 10 +++++----- dstack/verifier/builder/README.md | 5 +++++ 6 files changed, 29 insertions(+), 14 deletions(-) diff --git a/dstack/gateway/dstack-app/builder/Dockerfile b/dstack/gateway/dstack-app/builder/Dockerfile index 1c2294da6..8a0e7dec0 100644 --- a/dstack/gateway/dstack-app/builder/Dockerfile +++ b/dstack/gateway/dstack-app/builder/Dockerfile @@ -3,8 +3,8 @@ # SPDX-License-Identifier: Apache-2.0 FROM rust:1.92.0@sha256:48851a839d6a67370c9dbe0e709bedc138e3e404b161c5233aedcf2b717366e4 AS gateway-builder -COPY --from=build-shared pin-packages.sh /build/ -COPY ./shared/*-pinned-packages.txt /build/ +COPY --chmod=0755 --from=build-shared pin-packages.sh /build/ +COPY --chmod=0644 ./shared/*-pinned-packages.txt /build/ ARG DSTACK_REV ARG DSTACK_SRC_URL=https://github.com/Dstack-TEE/dstack.git WORKDIR /build @@ -27,8 +27,8 @@ RUN cd repo/dstack && cargo build --release --locked -p dstack-gateway --target RUN echo "${DSTACK_REV}" > /build/.GIT_REV FROM debian:bookworm@sha256:0d8498a0e9e6a60011df39aab78534cfe940785e7c59d19dfae1eb53ea59babe -COPY --from=build-shared pin-packages.sh /build/ -COPY ./shared/pinned-packages.txt /build/ +COPY --chmod=0755 --from=build-shared pin-packages.sh /build/ +COPY --chmod=0644 ./shared/pinned-packages.txt /build/ WORKDIR /build RUN ./pin-packages.sh ./pinned-packages.txt && \ apt-get update -o APT::Update::Error-Mode=any && \ @@ -47,7 +47,7 @@ COPY --from=gateway-builder /build/.GIT_REV /etc/ ARG IMAGE_METADATA RUN mkdir -p /etc/dstack-gateway && printf '%s\n' "${IMAGE_METADATA}" > /etc/dstack-gateway/build-info WORKDIR /app -COPY entrypoint.sh /app/entrypoint.sh +COPY --chmod=0755 entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh ENTRYPOINT ["/app/entrypoint.sh"] CMD ["dstack-gateway", "-c", "/data/gateway/gateway.toml"] diff --git a/dstack/gateway/dstack-app/builder/README.md b/dstack/gateway/dstack-app/builder/README.md index b8aedad53..52e406be3 100644 --- a/dstack/gateway/dstack-app/builder/README.md +++ b/dstack/gateway/dstack-app/builder/README.md @@ -46,6 +46,11 @@ that tag, rather than building the branch selected in the workflow UI. ## Reproducing a released image +Files copied from the checkout have explicit permissions in the Dockerfile, +so the caller's clone-time umask does not affect their image layers. For older +release tags without these permissions, set `umask 0022` before cloning to +match the release checkout. + Release CI runs this same script, so a published image can be rebuilt and checked digest-for-digest. Use a clean checkout of the release commit so the Dockerfile, package lists, shared scripts and copied files also match the release. diff --git a/dstack/kms/dstack-app/builder/Dockerfile b/dstack/kms/dstack-app/builder/Dockerfile index 426dd6ab9..7b6a19507 100644 --- a/dstack/kms/dstack-app/builder/Dockerfile +++ b/dstack/kms/dstack-app/builder/Dockerfile @@ -3,8 +3,8 @@ # SPDX-License-Identifier: Apache-2.0 FROM rust:1.92.0@sha256:48851a839d6a67370c9dbe0e709bedc138e3e404b161c5233aedcf2b717366e4 AS kms-builder -COPY --from=build-shared /pin-packages.sh /build/ -COPY ./shared/*-pinned-packages.txt /build/ +COPY --chmod=0755 --from=build-shared /pin-packages.sh /build/ +COPY --chmod=0644 ./shared/*-pinned-packages.txt /build/ ARG DSTACK_REV ARG DSTACK_SRC_URL=https://github.com/Dstack-TEE/dstack.git WORKDIR /build @@ -27,8 +27,8 @@ RUN cd repo/dstack && cargo build --release --locked -p dstack-kms --target x86_ RUN echo "${DSTACK_REV}" > /build/.GIT_REV FROM debian:bookworm@sha256:0d8498a0e9e6a60011df39aab78534cfe940785e7c59d19dfae1eb53ea59babe -COPY --from=build-shared /pin-packages.sh /build/ -COPY ./shared/pinned-packages.txt /build/ +COPY --chmod=0755 --from=build-shared /pin-packages.sh /build/ +COPY --chmod=0644 ./shared/pinned-packages.txt /build/ WORKDIR /build # reqwest is built against rustls-platform-verifier, which reads the system # trust store and errors out when it finds none. The KMS builds its collateral diff --git a/dstack/kms/dstack-app/builder/README.md b/dstack/kms/dstack-app/builder/README.md index 39815e762..239343c25 100644 --- a/dstack/kms/dstack-app/builder/README.md +++ b/dstack/kms/dstack-app/builder/README.md @@ -45,6 +45,11 @@ that tag, rather than building the branch selected in the workflow UI. ## Reproducing a released image +Files copied from the checkout have explicit permissions in the Dockerfile, +so the caller's clone-time umask does not affect their image layers. For older +release tags without these permissions, set `umask 0022` before cloning to +match the release checkout. + Release CI runs this same script, so a published image can be rebuilt and checked digest-for-digest. Use a clean checkout of the release commit so the Dockerfile, package lists, shared scripts and copied files also match the release. diff --git a/dstack/verifier/builder/Dockerfile b/dstack/verifier/builder/Dockerfile index a62821e72..e8b5889a1 100644 --- a/dstack/verifier/builder/Dockerfile +++ b/dstack/verifier/builder/Dockerfile @@ -3,8 +3,8 @@ # SPDX-License-Identifier: Apache-2.0 FROM rust:1.92.0-bookworm@sha256:e90e846de4124376164ddfbaab4b0774c7bdeef5e738866295e5a90a34a307a2 AS verifier-builder -COPY --from=build-shared pin-packages.sh /build/ -COPY builder/shared/*-pinned-packages.txt /build/ +COPY --chmod=0755 --from=build-shared pin-packages.sh /build/ +COPY --chmod=0644 builder/shared/*-pinned-packages.txt /build/ ARG DSTACK_REV ARG DSTACK_SRC_URL=https://github.com/Dstack-TEE/dstack.git WORKDIR /build @@ -31,8 +31,8 @@ RUN cd repo/dstack && cargo build --release --locked -p dstack-verifier --target RUN echo "${DSTACK_REV}" > /build/.GIT_REV FROM debian:bookworm@sha256:0d8498a0e9e6a60011df39aab78534cfe940785e7c59d19dfae1eb53ea59babe -COPY --from=build-shared pin-packages.sh /build/ -COPY builder/shared/pinned-packages.txt /build/ +COPY --chmod=0755 --from=build-shared pin-packages.sh /build/ +COPY --chmod=0644 builder/shared/pinned-packages.txt /build/ WORKDIR /build RUN ./pin-packages.sh ./pinned-packages.txt && \ apt-get update -o APT::Update::Error-Mode=any && \ @@ -48,7 +48,7 @@ COPY --from=verifier-builder /build/.GIT_REV /etc/ ARG IMAGE_METADATA RUN mkdir -p /etc/dstack-verifier && printf '%s\n' "${IMAGE_METADATA}" > /etc/dstack-verifier/build-info RUN mkdir -p /etc/dstack -COPY dstack-verifier.toml /etc/dstack/dstack-verifier.toml +COPY --chmod=0644 dstack-verifier.toml /etc/dstack/dstack-verifier.toml WORKDIR /var/lib/dstack-verifier EXPOSE 8080 ENTRYPOINT ["/usr/local/bin/dstack-verifier"] diff --git a/dstack/verifier/builder/README.md b/dstack/verifier/builder/README.md index 728028156..a38c60fe0 100644 --- a/dstack/verifier/builder/README.md +++ b/dstack/verifier/builder/README.md @@ -46,6 +46,11 @@ that tag, rather than building the branch selected in the workflow UI. ## Reproducing a released image +Files copied from the checkout have explicit permissions in the Dockerfile, +so the caller's clone-time umask does not affect their image layers. For older +release tags without these permissions, set `umask 0022` before cloning to +match the release checkout. + Release CI runs this same script, so a published image can be rebuilt and checked digest-for-digest. Use a clean checkout of the release commit so the Dockerfile, package lists, shared scripts and copied files also match the release. From fdfcabd328d64a37fc1857e00ec76a71959260cd Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sun, 1 Nov 2026 19:48:24 -0800 Subject: [PATCH 2/3] fix(build): normalize copied modes in the Bun auth image Signed-off-by: Kevin Wang --- dstack/kms/auth-eth-bun/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dstack/kms/auth-eth-bun/Dockerfile b/dstack/kms/auth-eth-bun/Dockerfile index e5e96bae2..07a538bfc 100644 --- a/dstack/kms/auth-eth-bun/Dockerfile +++ b/dstack/kms/auth-eth-bun/Dockerfile @@ -6,13 +6,13 @@ # source and lockfile rather than a clone fetched at build time. FROM oven/bun:1.4.4-alpine@sha256:4cf82f5686b196504fe119db3fcd9185b191f82ece82c40680e597228ec6070a AS deps WORKDIR /app -COPY package.json bun.lock ./ +COPY --chmod=0644 package.json bun.lock ./ RUN bun install --frozen-lockfile --production --ignore-scripts FROM oven/bun:1.4.4-alpine@sha256:4cf82f5686b196504fe119db3fcd9185b191f82ece82c40680e597228ec6070a WORKDIR /app COPY --from=deps /app/node_modules ./node_modules -COPY package.json index.ts ./ +COPY --chmod=0644 package.json index.ts ./ ARG GIT_REV=unknown LABEL org.opencontainers.image.revision="${GIT_REV}" ENV NODE_ENV=production From b3ba20a4a254e2074d66e357b630deb839a4bdb5 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sun, 1 Nov 2026 19:58:40 -0800 Subject: [PATCH 3/3] fix(build): normalize Bun image timestamps and pin BuildKit Signed-off-by: Kevin Wang --- .github/workflows/kms-auth-eth-bun.yml | 9 +++++++-- dstack/kms/auth-eth-bun/README.md | 27 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/.github/workflows/kms-auth-eth-bun.yml b/.github/workflows/kms-auth-eth-bun.yml index bd2d94655..10cba37d5 100644 --- a/.github/workflows/kms-auth-eth-bun.yml +++ b/.github/workflows/kms-auth-eth-bun.yml @@ -111,7 +111,9 @@ jobs: persist-credentials: false - name: Resolve the revision to build - run: echo "GIT_REV=$(git rev-parse HEAD)" >> "$GITHUB_ENV" + run: | + echo "GIT_REV=$(git rev-parse HEAD)" >> "$GITHUB_ENV" + echo "SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)" >> "$GITHUB_ENV" - name: Log in to GitHub Container Registry if: env.PUBLISH == '1' @@ -123,6 +125,8 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + with: + driver-opts: image=moby/buildkit:v0.20.2@sha256:c457984bd29f04d6acc90c8d9e717afe3922ae14665f3187e0096976fe37b1c8 - name: Build and push Docker image id: build @@ -130,11 +134,12 @@ jobs: with: context: ${{ env.BACKEND_DIR }} platforms: linux/amd64 - push: ${{ env.PUBLISH == '1' }} + outputs: type=image,push=${{ env.PUBLISH == '1' }},oci-mediatypes=true,rewrite-timestamp=true provenance: false tags: ${{ env.IMAGE_REF }} build-args: | GIT_REV=${{ env.GIT_REV }} + SOURCE_DATE_EPOCH=${{ env.SOURCE_DATE_EPOCH }} labels: | org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.revision=${{ env.GIT_REV }} diff --git a/dstack/kms/auth-eth-bun/README.md b/dstack/kms/auth-eth-bun/README.md index cc8112428..9dcd4c955 100644 --- a/dstack/kms/auth-eth-bun/README.md +++ b/dstack/kms/auth-eth-bun/README.md @@ -95,6 +95,33 @@ to build it locally: docker build -t dstack-kms-auth-eth-bun . ``` +### Reproducing a release image + +Use a clean checkout of the release tag and BuildKit 0.20.2, matching the +workflow. COPY permissions are fixed independently of the checkout umask. +Export with the commit timestamp and timestamp rewriting; a plain +`docker build` does not reproduce the release digest. + +```bash +docker buildx create --name auth-repro --driver docker-container \ + --driver-opt image=moby/buildkit:v0.20.2@sha256:c457984bd29f04d6acc90c8d9e717afe3922ae14665f3187e0096976fe37b1c8 + +git_revision=$(git rev-parse HEAD) +source_date_epoch=$(git show -s --format=%ct HEAD) +docker buildx build --builder auth-repro --platform linux/amd64 \ + --provenance=false \ + --build-arg "GIT_REV=$git_revision" \ + --build-arg "SOURCE_DATE_EPOCH=$source_date_epoch" \ + --label org.opencontainers.image.source=https://github.com/Dstack-TEE/dstack \ + --label "org.opencontainers.image.revision=$git_revision" \ + --label 'org.opencontainers.image.description=dstack KMS auth webhook backed by the DstackKms contract' \ + --tag ghcr.io/dstack-tee/dstack-kms-auth-eth-bun:VERSION \ + --output type=oci,rewrite-timestamp=true,dest=/tmp/auth-eth-bun.oci.tar . +``` + +Replace `VERSION` with the release version. These settings apply to releases +that include timestamp normalization; earlier tags used a different workflow. + ## API endpoints ### GET /