Skip to content

Reproducible Kettle builds#36

Merged
indirect merged 16 commits into
mainfrom
reproducible-kettle
May 7, 2026
Merged

Reproducible Kettle builds#36
indirect merged 16 commits into
mainfrom
reproducible-kettle

Conversation

@indirect
Copy link
Copy Markdown
Collaborator

@indirect indirect commented May 5, 2026

Run bin/reproduce-build to build Kettle statically using musl libc inside Docker, using the fully-reproducible rustc provided by stagex.tools.

With Kettle fully reproducible by anyone on demand, we can publish our own builds and invite anyone to run their own build to verify Kettle itself. A clean build from a brand new repo checkout takes no more than 2 minutes on a machine with 16+ cores.

# dev-matcha server
ubuntu@dev-matcha:~/kettle$ uname -a
Linux dev-matcha 6.17.0-22-generic #22-Ubuntu SMP PREEMPT_DYNAMIC Fri Mar 13 12:04:44 UTC 2026 x86_64 GNU/Linux
ubuntu@dev-matcha:~/kettle$ sha256sum ./target/reproducible/kettle
6e0c13134ec5b341e4a61e8e2829347e5e0d55ad005d84528741d68f42b716f9  ./target/reproducible/kettle
# my MacBook Pro M4 Max
❯ uname -a
Darwin Kryze.local 25.4.0 Darwin Kernel Version 25.4.0: Thu Mar 19 19:33:25 PDT 2026; root:xnu-12377.101.15~1/RELEASE_ARM64_T6041 arm64
❯ sha256sum target/reproducible/kettle
6e0c13134ec5b341e4a61e8e2829347e5e0d55ad005d84528741d68f42b716f9  target/reproducible/kettle

@indirect indirect requested a review from AmeanAsad May 5, 2026 21:34
Copy link
Copy Markdown
Contributor

@AmeanAsad AmeanAsad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huge! 🚢

@indirect indirect force-pushed the reproducible-kettle branch from f96cd7f to e211b87 Compare May 5, 2026 22:01
@indirect
Copy link
Copy Markdown
Collaborator Author

indirect commented May 6, 2026

Okay, built a version with attestation enabled, attested something on the TEE using the reproducible build, and verified it:

lunaluser@andre-tee-vm-vm:~/kettle$ bin/reproduce-build
+ mkdir -p target/reproducible
++ git log -1 --pretty=%ct
+ SOURCE_DATE_EPOCH=1778026264
++ pwd
+ docker build . -t kettle-reproducible --platform linux/amd64 --build-arg SOURCE_DATE_EPOCH=1778026264 --target=artifact --output type=local,dest=/home/lunaluser/kettle/target/reproducible/
[+] Building 334.2s (13/13) FINISHED                                                                                                                                                                                     docker:default
 => [internal] load build definition from Dockerfile                                                                                                                                                                               0.0s
 => => transferring dockerfile: 1.29kB                                                                                                                                                                                             0.0s
 => [internal] load metadata for docker.io/stagex/user-tpm2-tss:latest                                                                                                                                                             0.4s
 => [internal] load metadata for docker.io/stagex/pallet-rust@sha256:2fbe7b164dd92edb9c1096152f6d27592d8a69b1b8eb2fc907b5fadea7d11668                                                                                              0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                  0.0s
 => => transferring context: 59B                                                                                                                                                                                                   0.0s
 => FROM docker.io/stagex/user-tpm2-tss:latest@sha256:caddb7d07a65bb3532117de5b74030941bae8dce0427109dc4b9c4c148af2b19                                                                                                             0.1s
 => => resolve docker.io/stagex/user-tpm2-tss:latest@sha256:caddb7d07a65bb3532117de5b74030941bae8dce0427109dc4b9c4c148af2b19                                                                                                       0.0s
 => [internal] load build context                                                                                                                                                                                                  0.1s
 => => transferring context: 134.73kB                                                                                                                                                                                              0.0s
 => [build 1/5] FROM docker.io/stagex/pallet-rust@sha256:2fbe7b164dd92edb9c1096152f6d27592d8a69b1b8eb2fc907b5fadea7d11668                                                                                                          0.0s
 => => resolve docker.io/stagex/pallet-rust@sha256:2fbe7b164dd92edb9c1096152f6d27592d8a69b1b8eb2fc907b5fadea7d11668                                                                                                                0.0s
 => CACHED [build 2/5] COPY --from=docker.io/stagex/user-tpm2-tss . /                                                                                                                                                              0.0s
 => [build 3/5] WORKDIR /tmp/kettle                                                                                                                                                                                                1.1s
 => [build 4/5] COPY . .                                                                                                                                                                                                           0.3s
 => [build 5/5] RUN cargo build --bin kettle --features attest   --release --locked   --target x86_64-unknown-linux-musl                                                                                                         331.9s
 => [artifact 1/1] COPY --from=build /tmp/kettle/target/x86_64-unknown-linux-musl/release/kettle /kettle                                                                                                                           0.1s
 => exporting to client directory                                                                                                                                                                                                  0.1s
 => => copying files 17.00MB                                                                                                                                                                                                       0.1s


lunaluser@andre-tee-vm-vm:~/kettle$ ./target/reproducible/kettle attest ../kex
Building project in: "../kex"
Found Cargo project
Running `cargo build --locked --release`
Build in "../kex" complete, output located in `kettle-build`
Running on platform: az-snp
Attesting build provenance.json with checksum 41de1e1a04582001fc9ac265136b91d7d1288319798e70c6933b402a070f6bdd
Attestation complete! Evidence written to file `evidence.json`


lunaluser@andre-tee-vm-vm:~/kettle$ ./target/reproducible/kettle verify ../kex/kettle-build
┌───────────────────────────────────────────────────────┐
│                                                       │
│           Verifying build dir kettle-build            │
│                                                       │
├────────────┬──────────────────────────────────────────┤
│  Build ID  │ build-20260506-003504-fda1204c           │
├────────────┼──────────────────────────────────────────┤
│  Built at  │ 2026-05-06T00:35:04.683998+00:00         │
├────────────┼──────────────────────────────────────────┤
│ Built with │ rustc 1.93.1 (01f6ddf75 2026-02-11)      │
├────────────┼──────────────────────────────────────────┤
│ Git commit │ 0fc9cfa2d7cf98bf1d6c370a5359cd4ac451540a │
└────────────┴──────────────────────────────────────────┘

┌───────────────────────────────────────────┐
│           Verification Results            │
├────┬──────────────────────────────────────┤
│ ✅ │ Attestation hardware signature valid │
├────┼──────────────────────────────────────┤
│ ✅ │ Provenance is valid SLSA v1.2        │
├────┼──────────────────────────────────────┤
│ ✅ │ Provenance checksum match            │
├────┼──────────────────────────────────────┤
│ ✅ │ Checksum match for binary `kex`      │
├────┼──────────────────────────────────────┤
│ ✅ │ Verification PASSED                  │
└────┴──────────────────────────────────────┘

@indirect indirect force-pushed the reproducible-kettle branch from 93cfb44 to bd029b0 Compare May 6, 2026 07:59
@indirect indirect force-pushed the reproducible-kettle branch 2 times, most recently from 8c11434 to 2f53302 Compare May 6, 2026 08:09
indirect added 2 commits May 6, 2026 08:35
These three crates are vendored as-is from crates.io (versions 0.6.0,
7.7.0, 0.8.1 respectively) so we can patch them in-tree to bypass the
tctildr dlopen-based TCTI loader, which doesn't work in our static
reproducible build.

These files are unmodified, and the patches are in later commits.
@indirect indirect force-pushed the reproducible-kettle branch from 2f53302 to f53df4d Compare May 6, 2026 09:22
In a static binary that includes glibc, libtss2-tctildr can't use dlopen
to load any TCTI plugins at runtime, so attestation::detect() always
fails with NoPlatformDetected even when the host has /dev/tpm0 wired up.

This commit patches the three vendored crates so a Device TCTI can be
initialized directly, without going through tctildr:

  * tss-esapi-sys: also pkg_config-probe tss2-tcti-device, and declare
    Tss2_Tcti_Device_Init in the FFI surface.
  * tss-esapi: add TctiContext::initialize_device_direct and
    Context::new_device_direct, which malloc the TCTI buffer and call
    Tss2_Tcti_Device_Init in place. Track init mode in TctiContext so
    Drop calls the matching finalizer — Tss2_TctiLdr_Finalize for
    loader-allocated contexts, and the TCTI's own finalize fn pointer
    + libc free for direct-init contexts (TctiLdr_Finalize would cast
    the buffer to a TSS2_TCTILDR_CONTEXT and dereference loader-only
    fields that don't exist, which is very bad).
  * az-cvm-vtpm: switch the four Context::new(TctiNameConf::Device(..))
    callsites in vtpm/mod.rs to Context::new_device_direct.
@indirect indirect force-pushed the reproducible-kettle branch from f53df4d to ac42f09 Compare May 6, 2026 09:49
@indirect indirect force-pushed the reproducible-kettle branch from 75b4a80 to 10bdf2b Compare May 6, 2026 23:37
@indirect indirect force-pushed the reproducible-kettle branch from 9966c8f to fef4065 Compare May 7, 2026 00:16
@indirect indirect merged commit 75f8f8d into main May 7, 2026
16 checks passed
@indirect indirect deleted the reproducible-kettle branch May 7, 2026 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants