diff --git a/.github/workflows/checks_docker.yaml b/.github/workflows/checks_docker.yaml index bb0a6b9b..212afe6d 100644 --- a/.github/workflows/checks_docker.yaml +++ b/.github/workflows/checks_docker.yaml @@ -15,6 +15,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Docker QEMU uses: docker/setup-qemu-action@v3 @@ -29,4 +31,5 @@ jobs: cache-to: type=gha,mode=max platforms: linux/amd64 context: . + target: rbuilder-runtime push: false diff --git a/.github/workflows/docker_build.yaml b/.github/workflows/docker_build.yaml index 1e619ca3..1f3a24c4 100644 --- a/.github/workflows/docker_build.yaml +++ b/.github/workflows/docker_build.yaml @@ -46,9 +46,18 @@ jobs: runner: warp-ubuntu-latest-x64-16x - target: linux/arm64 runner: warp-ubuntu-latest-arm64-16x + docker_target: + - name: regular + target: rbuilder-runtime + tag_suffix: "" + - name: reproducible + target: rbuilder-reproducible-runtime + tag_suffix: "-reproducible" steps: - name: checkout sources uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: docker qemu uses: docker/setup-qemu-action@v3 @@ -83,6 +92,9 @@ jobs: labels: ${{ steps.meta.outputs.labels }} platforms: ${{ matrix.configs.target }} push: true - tags: ${{ steps.meta.outputs.tags }} + target: ${{ matrix.docker_target.target }} + tags: | + ghcr.io/${{ github.repository }}/op-rbuilder:latest${{ matrix.docker_target.tag_suffix }} + ghcr.io/${{ github.repository }}/op-rbuilder:sha-${{ env.VERSION }}${{ matrix.docker_target.tag_suffix }} build-args: | RBUILDER_BIN=op-rbuilder diff --git a/.github/workflows/op_rbuilder_release.yaml b/.github/workflows/op_rbuilder_release.yaml index 7a16c95e..6795b516 100644 --- a/.github/workflows/op_rbuilder_release.yaml +++ b/.github/workflows/op_rbuilder_release.yaml @@ -100,8 +100,14 @@ jobs: run: | git config --global --add safe.directory "$(pwd)" . $HOME/.cargo/env - cargo build --release --features=${{ matrix.features }} --target ${{ matrix.configs.target }} --package op-rbuilder - + SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) \ + RUSTFLAGS="--C target-feature=+crt-static -C link-arg=-static-libgcc -C link-arg=-Wl,--build-id=none -C metadata='' --remap-path-prefix=$(pwd)=." \ + CARGO_INCREMENTAL=0 \ + LC_ALL=C \ + TZ=UTC \ + CFLAGS="-D__TIME__=\"\" -D__DATE__=\"\"" \ + CXXFLAGS="-D__TIME__=\"\" -D__DATE__=\"\"" \ + cargo build --release --features=${{ matrix.features }} --locked --target ${{ matrix.configs.target }} --package op-rbuilder - name: Upload op-rbuilder artifact uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/op_rbuilder_reproducible_verify.yaml b/.github/workflows/op_rbuilder_reproducible_verify.yaml new file mode 100644 index 00000000..a59a86ba --- /dev/null +++ b/.github/workflows/op_rbuilder_reproducible_verify.yaml @@ -0,0 +1,37 @@ +name: Verify Reproducible Build + +on: + workflow_dispatch: + schedule: + - cron: "0 2 */3 * *" + +jobs: + verify: + name: "[op-rbuilder] Verify reproducible builds" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Build twice and compare + run: | + export REPRO_FLAGS="--C target-feature=+crt-static -C link-arg=-static-libgcc -C link-arg=-Wl,--build-id=none -C metadata='' --remap-path-prefix=$(pwd)=." + export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) + + RUSTFLAGS="$REPRO_FLAGS" CARGO_INCREMENTAL=0 LC_ALL=C TZ=UTC \ + CFLAGS="-D__TIME__=\"\" -D__DATE__=\"\"" CXXFLAGS="-D__TIME__=\"\" -D__DATE__=\"\"" \ + cargo build --release --locked -p op-rbuilder --target x86_64-unknown-linux-gnu + mv target/x86_64-unknown-linux-gnu/release/op-rbuilder build1 + + cargo clean + + RUSTFLAGS="$REPRO_FLAGS" CARGO_INCREMENTAL=0 LC_ALL=C TZ=UTC \ + CFLAGS="-D__TIME__=\"\" -D__DATE__=\"\"" CXXFLAGS="-D__TIME__=\"\" -D__DATE__=\"\"" \ + cargo build --release --locked -p op-rbuilder --target x86_64-unknown-linux-gnu + mv target/x86_64-unknown-linux-gnu/release/op-rbuilder build2 + + if cmp -s build1 build2; then + echo "✅ Builds are reproducible" + else + echo "❌ Builds differ" + exit 1 + fi diff --git a/Dockerfile b/Dockerfile index 5e9258c0..e3549321 100644 --- a/Dockerfile +++ b/Dockerfile @@ -73,6 +73,23 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=$SCCACHE_DIR,sharing=locked \ cargo build --release --features="$FEATURES" --package=${RBUILDER_BIN} +# +# Reproducible builder container (deterministic source-date-epoch, no caching, no incremental builds) +# +FROM base AS rbuilder-reproducible +ARG RBUILDER_BIN +ARG FEATURES +WORKDIR /app +COPY . . +RUN SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) \ + RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-static-libgcc -C link-arg=-Wl,--build-id=none -C metadata='' --remap-path-prefix=/app=." \ + CARGO_INCREMENTAL=0 \ + LC_ALL=C \ + TZ=UTC \ + CFLAGS="-D__TIME__=\"\" -D__DATE__=\"\"" \ + CXXFLAGS="-D__TIME__=\"\" -D__DATE__=\"\"" \ + cargo build --release --locked --features="$FEATURES" --package=${RBUILDER_BIN} --target x86_64-unknown-linux-gnu + # Runtime container for rbuilder FROM gcr.io/distroless/cc-debian12 AS rbuilder-runtime ARG RBUILDER_BIN @@ -80,3 +97,9 @@ WORKDIR /app COPY --from=rbuilder /app/target/release/${RBUILDER_BIN} /app/rbuilder ENTRYPOINT ["/app/rbuilder"] +# Reproducible runtime container for rbuilder +FROM gcr.io/distroless/cc-debian12 AS rbuilder-reproducible-runtime +ARG RBUILDER_BIN +WORKDIR /app +COPY --from=rbuilder-reproducible /app/target/x86_64-unknown-linux-gnu/release/${RBUILDER_BIN} /app/rbuilder +ENTRYPOINT ["/app/rbuilder"] diff --git a/Makefile b/Makefile index 62625a93..0eb0c4ad 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,26 @@ GIT_TAG ?= $(shell git describe --tags --abbrev=0) FEATURES ?= +# Environment variables for reproducible builds +# Initialize RUSTFLAGS +RUST_BUILD_FLAGS = +# Enable static linking to ensure reproducibility across builds +RUST_BUILD_FLAGS += --C target-feature=+crt-static +# Set the linker to use static libgcc to ensure reproducibility across builds +RUST_BUILD_FLAGS += -C link-arg=-static-libgcc +# Remove build ID from the binary to ensure reproducibility across builds +RUST_BUILD_FLAGS += -C link-arg=-Wl,--build-id=none +# Remove metadata hash from symbol names to ensure reproducible builds +RUST_BUILD_FLAGS += -C metadata='' +# Set timestamp from last git commit for reproducible builds +SOURCE_DATE ?= $(shell git log -1 --pretty=%ct) +# Disable incremental compilation to avoid non-deterministic artifacts +CARGO_INCREMENTAL_VAL = 0 +# Set C locale for consistent string handling and sorting +LOCALE_VAL = C +# Set UTC timezone for consistent time handling across builds +TZ_VAL = UTC + ##@ Help .PHONY: help @@ -31,6 +51,15 @@ build: ## Build (debug version) op-rbuilder: ## Build op-rbuilder (debug version) cargo build -p op-rbuilder --bin op-rbuilder --features "$(FEATURES)" +.PHONY: build-reproducible +build-reproducible: ## Build the reth binary into `target` directory with reproducible builds + SOURCE_DATE_EPOCH=$(SOURCE_DATE) \ + RUSTFLAGS="${RUST_BUILD_FLAGS} --remap-path-prefix $$(pwd)=." \ + CARGO_INCREMENTAL=${CARGO_INCREMENTAL_VAL} \ + LC_ALL=${LOCALE_VAL} \ + TZ=${TZ_VAL} \ + cargo build -p op-rbuilder --bin op-rbuilder --features "$(FEATURES)" --profile "release" --locked --features "$(FEATURES)" --target x86_64-unknown-linux-gnu + .PHONY: tdx-quote-provider tdx-quote-provider: ## Build tdx-quote-provider (debug version) cargo build -p tdx-quote-provider --bin tdx-quote-provider --features "$(FEATURES)"