From 864a988f7c859251f52bb34a1c10a0032695d67b Mon Sep 17 00:00:00 2001 From: Barakeinav1 Date: Sun, 10 May 2026 15:26:50 +0000 Subject: [PATCH] key-provider-build: pin transitive deps for reproducible mr_enclave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upstream Dockerfile pins gramine (by digest), git, build-essential, Rust major-minor, and the upstream key-provider source commit. It does not pin transitive apt deps, the Rust patch version, or the rustup-init installer script. Each is a drift channel that silently changes the bytes hashed into the Gramine manifest's trusted_files list, which changes mr_enclave — breaking any attestation flow that pins to a specific expected value. In practice, builds done weeks apart on the same Dockerfile already diverge today: a rebuild produced 98f735d1… instead of the expected 6b5ed02e… because Ubuntu shipped a libcurl3-gnutls security update between the two build dates (7.81.0-1ubuntu1.23 → 1ubuntu1.24). This change parameterizes and pins all three drift channels: 1. APT_SNAPSHOT build-arg routes apt at https://snapshot.ubuntu.com/ so all transitive deps resolve to whatever Ubuntu had on the named date. Default = 20260423T000000Z. Each consumer overrides to match their expected mr_enclave: docker build --build-arg APT_SNAPSHOT=YYYYMMDDT000000Z ... 2. RUST_TOOLCHAIN build-arg with default 1.85.1 (was --default-toolchain 1.85, which resolves to whatever 1.85.x is current at install time). 3. RUSTUP_VERSION + RUSTUP_INIT_SHA256 build-args replace the live `curl https://sh.rustup.rs | sh` invocation with a pinned download from static.rust-lang.org's archive, verified against a sha256. Verified: with APT_SNAPSHOT=20260423T000000Z, the build reproduces the canonical mr_enclave 6b5ed02e549a1c30aaa8e3171a045f1f449b0017353ef595e78e39c348c98d01 that consumers (e.g. NEAR's MPC TDX nodes) currently expect. --- key-provider-build/Dockerfile.key-provider | 43 +++++++++++++++++++--- key-provider-build/docker-compose.yaml | 10 ++++- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/key-provider-build/Dockerfile.key-provider b/key-provider-build/Dockerfile.key-provider index 0a66bc7ab..ba2975788 100644 --- a/key-provider-build/Dockerfile.key-provider +++ b/key-provider-build/Dockerfile.key-provider @@ -3,12 +3,45 @@ # SPDX-License-Identifier: Apache-2.0 FROM gramineproject/gramine:1.9-jammy@sha256:84b3d222e0bd9ab941f0078a462af0dbc5518156b99b147c10a7b83722ac0c38 -RUN apt-get update && apt-get install -y \ - git=1:2.34.1-1ubuntu1.17 \ - build-essential=12.9ubuntu3 \ - && rm -rf /var/lib/apt/lists/* +# Pin Ubuntu apt sources to a fixed snapshot date so transitive dependencies +# (e.g. libcurl3-gnutls, libc6) don't drift between builds. Without this, +# Ubuntu point updates to packages pulled in by `build-essential` change the +# bytes of files referenced by the Gramine manifest's trusted_files list, +# which changes mr_enclave — breaking any attestation flow that pins to a +# specific expected value. +# +# Each consumer should override APT_SNAPSHOT to the date that matches their +# expected mr_enclave: +# +# docker build --build-arg APT_SNAPSHOT=YYYYMMDDT000000Z ... +ARG APT_SNAPSHOT=20260423T000000Z +RUN { \ + echo "deb https://snapshot.ubuntu.com/ubuntu/${APT_SNAPSHOT} jammy main universe restricted multiverse"; \ + echo "deb https://snapshot.ubuntu.com/ubuntu/${APT_SNAPSHOT} jammy-updates main universe restricted multiverse"; \ + echo "deb https://snapshot.ubuntu.com/ubuntu/${APT_SNAPSHOT} jammy-security main universe restricted multiverse"; \ + } > /etc/apt/sources.list \ + && rm -rf /etc/apt/sources.list.d/* \ + && apt-get update && apt-get install -y \ + git=1:2.34.1-1ubuntu1.17 \ + build-essential=12.9ubuntu3 \ + && rm -rf /var/lib/apt/lists/* -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain 1.85 -y +# Pin rustup-init by sha256 instead of `curl https://sh.rustup.rs | sh` so the +# installer bytes are deterministic. Pin --default-toolchain to the exact patch +# version (1.85 alone resolves to the latest 1.85.x at install time). +# +# To bump RUSTUP_VERSION, fetch the matching sha256 with: +# curl -sf https://static.rust-lang.org/rustup/archive//x86_64-unknown-linux-gnu/rustup-init.sha256 +ARG RUSTUP_VERSION=1.28.2 +ARG RUSTUP_INIT_SHA256=20a06e644b0d9bd2fbdbfd52d42540bdde820ea7df86e92e533c073da0cdd43c +ARG RUST_TOOLCHAIN=1.85.1 +RUN curl --proto '=https' --tlsv1.2 -fsSL \ + "https://static.rust-lang.org/rustup/archive/${RUSTUP_VERSION}/x86_64-unknown-linux-gnu/rustup-init" \ + -o /tmp/rustup-init \ + && echo "${RUSTUP_INIT_SHA256} /tmp/rustup-init" | sha256sum -c - \ + && chmod +x /tmp/rustup-init \ + && /tmp/rustup-init -y --default-toolchain "${RUST_TOOLCHAIN}" \ + && rm /tmp/rustup-init ENV PATH="/root/.cargo/bin:${PATH}" # Set environment variables diff --git a/key-provider-build/docker-compose.yaml b/key-provider-build/docker-compose.yaml index 25e7e4ef1..3194ce26e 100644 --- a/key-provider-build/docker-compose.yaml +++ b/key-provider-build/docker-compose.yaml @@ -25,13 +25,21 @@ services: - "./sgx_default_qcnl.conf:/etc/sgx_default_qcnl.conf" - "aesmd:/var/run/aesmd/" network_mode: "host" - + gramine-sealing-key-provider: <<: *common-config container_name: gramine-sealing-key-provider build: context: . dockerfile: Dockerfile.key-provider + # Defaults reproduce a canonical build; consumers that pin to a specific + # mr_enclave (e.g. attestation flows) override these via environment + # variables, e.g. `APT_SNAPSHOT=YYYYMMDDT000000Z ./run.sh`. + args: + APT_SNAPSHOT: ${APT_SNAPSHOT:-20260423T000000Z} + RUSTUP_VERSION: ${RUSTUP_VERSION:-1.28.2} + RUSTUP_INIT_SHA256: ${RUSTUP_INIT_SHA256:-20a06e644b0d9bd2fbdbfd52d42540bdde820ea7df86e92e533c073da0cdd43c} + RUST_TOOLCHAIN: ${RUST_TOOLCHAIN:-1.85.1} privileged: true devices: - "/dev/sgx_enclave:/dev/sgx_enclave"