diff --git a/.github/workflows/_release-buildernet-image.yml b/.github/workflows/_release-buildernet-image.yml index 11ca556d..61c2a005 100644 --- a/.github/workflows/_release-buildernet-image.yml +++ b/.github/workflows/_release-buildernet-image.yml @@ -23,6 +23,10 @@ on: description: "R2 cache object name (e.g. 'cache' or 'cache-playground')" type: string required: true + portable-measurements: + description: "Generate portable measurements for the published GCP UKI" + type: boolean + default: true jobs: build: @@ -131,6 +135,21 @@ jobs: fi mkosi "${ARGS[@]}" + - name: Install portable measurement tool + if: inputs.portable-measurements + uses: dtolnay/rust-toolchain@stable + + - name: Generate portable measurements + if: inputs.portable-measurements + env: + ATTEST_REV: e7f59c78f9eabd5d1ac7c9e96da46027878d038c + run: | + cargo install --locked --git https://github.com/Easy-TEE/attest \ + --rev "${ATTEST_REV}" --package attest-cli --no-default-features + make measure-portable \ + UKI_FILE="mkosi.output/buildernet-gcp_${IMAGE_VERSION}.efi" \ + MEASUREMENTS_FILE="mkosi.output/buildernet-${IMAGE_VERSION}-portable-measurements.json" + - name: Prepare cache run: | sudo find . \( -name "mkosi.builddir" -o -name "mkosi.cache" -o -name "mkosi.tools" \) -type d -print0 | \ diff --git a/.github/workflows/release-playground.yml b/.github/workflows/release-playground.yml index 670cf4b7..dc6a93f0 100644 --- a/.github/workflows/release-playground.yml +++ b/.github/workflows/release-playground.yml @@ -16,4 +16,5 @@ jobs: checksum-globs: "buildernet-*.qcow2" r2-include: "buildernet-*.{qcow2,minisig,sha256}" cache-name: "cache-playground" + portable-measurements: false secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 919ccdb5..bcc45350 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: uses: ./.github/workflows/_release-buildernet-image.yml with: r2-path-prefix: "buildernet-images" - checksum-globs: "buildernet-*.{efi,tar.gz,vhd,qcow2}" - r2-include: "buildernet-*.{efi,tar.gz,vhd,qcow2,minisig,sha256}" + checksum-globs: "buildernet-*.{efi,tar.gz,vhd,qcow2,json}" + r2-include: "buildernet-*.{efi,tar.gz,vhd,qcow2,minisig,sha256,json}" cache-name: "cache" secrets: inherit diff --git a/.gitignore b/.gitignore index ba1ca9b7..5d053d6a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ tmp/ !/**/.gitkeep .vscode/ CLAUDE.local.md +/.bypass-lima diff --git a/Makefile b/Makefile index 8f6850df..0d2f719e 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ SHELL := /usr/bin/env bash WRAPPER := scripts/env_wrapper.sh +UKI_FILE ?= mkosi.output/buildernet-gcp_latest.efi +MEASUREMENTS_FILE ?= mkosi.output/portable_measurements.json # Lima build VM name, mirroring scripts/env_wrapper.sh: tee-builder-. ifndef LIMA_VM @@ -21,7 +23,7 @@ help: ## display their help. ##@ Build -.PHONY: build build-dev build-local smoke boot clean clean-vm stop-vm +.PHONY: build build-dev build-local smoke measure-portable boot clean clean-vm stop-vm build: ## build all BuilderNet images (azure, gcp, qemu) $(WRAPPER) mkosi $(ARCH) --force -I buildernet.conf build @@ -36,6 +38,12 @@ smoke: ## tiny quick image build to validate the toolchain $(WRAPPER) mkosi -C tests/smoke --force build @echo "Smoke build OK" +measure-portable: ## export portable measurements for the GCP UKI (override UKI_FILE=...) + @mkdir -p "$(dir $(MEASUREMENTS_FILE))" + @command -v attest >/dev/null || { echo "attest not found; install Easy-TEE/attest from main" >&2; exit 1; } + @attest measure portable --no-azure "$(UKI_FILE)" > "$(MEASUREMENTS_FILE)" + @echo "Portable measurements exported to $(MEASUREMENTS_FILE)" + ##@ Run boot: ## boot the built qemu UKI (no disk/TPM). root shell: `make console` in another terminal diff --git a/README.md b/README.md index 1aa4b3fd..644ec238 100644 --- a/README.md +++ b/README.md @@ -74,47 +74,47 @@ To verify that our official images are built from the corresponding git revision 4. Calculate the SHA256 hashsum of the *.efi image in the `mkosi.output` directory. It should match the hashsum from the *.sha256 file from https://downloads.buildernet.org # Measurements -Measurement are a way to verify that the image you built offline matches the one running as a VM. -To measure a Azure image: -1. Clone github.com/flashbots/measured-boot/ repo and build the `measured-boot` script -``` -go build -``` -2. Measure the image offline -``` -./measured-boot image.efi /dev/null --direct-uki +Portable measurements let you verify that a BuilderNet GCP VM is running the OS image +you audited and built locally, without depending on GCP-specific TDX measurement values. + +Build the images, install the `attest` CLI from +[`Easy-TEE/attest`](https://github.com/Easy-TEE/attest) `main` (pinned below to its current +head for reproducibility), and measure the GCP UKI: + +```bash +make build +cargo install --locked --git https://github.com/Easy-TEE/attest \ + --rev e7f59c78f9eabd5d1ac7c9e96da46027878d038c \ + --package attest-cli --no-default-features +make measure-portable ``` -3. Clone https://github.com/flashbots/attested-tls-proxy.git repo and run the `attested-get` tool + +This writes `mkosi.output/portable_measurements.json`. To measure another UKI or choose a +different output path, set `UKI_FILE` or `MEASUREMENTS_FILE`: + +```bash +make measure-portable \ + UKI_FILE=mkosi.output/buildernet-gcp_2.9.1-deadbeef.efi \ + MEASUREMENTS_FILE=mkosi.output/portable_measurements.json ``` -git clone https://github.com/flashbots/attested-tls-proxy.git -cd attested-tls-proxy -curl -fSsL https://measurements.buildernet.org > /tmp/measurements-buildernet.json +For a release build, compare this file with the +`buildernet--portable-measurements.json` file published alongside the image. + +To attest a running VM against those image hashes, build `attested-get` from +[`flashbots/attested-tls-proxy`](https://github.com/flashbots/attested-tls-proxy): + +```bash +git clone https://github.com/flashbots/attested-tls-proxy +cd attested-tls-proxy cargo run -- attested-get \ - --measurements-file /tmp/measurements-buildernet.json \ - https://_BUILDERNET_INSTANCE_:7936 + --measurements-file ../flashbots-images/mkosi.output/portable_measurements.json \ + https://:7936 ``` -1. Compare PCRs 4 and 11 of the offline image and the deployed one, they should match. -To measure a GCP image: -1. Clone github.com/flashbots/dstack-mr-gcp repo and build the `dstack-mr` tool -``` -go build -``` -2. Measure the image offline -``` -./dstack-mr -uki buildernet-gcp.efi -``` -3. Clone github.com/flashbots/cvm-reverse-proxy repo and build the `attested-get` tool -``` -go build cmd/attested-get/main.go -``` -4. Measure the deployed image -``` -./attested-get --addr=https://:7936 -``` -5. Compare PCRs 0-4 of the offline image and the deployed one, they should match. +Successful verification means the VM's attestation matches the hashes derived from your +locally built image. # Development - Use standard directory names of `mkosi` so that it can automatically pick it up. diff --git a/mkosi.images/buildernet/mkosi.build.d/15-attested-tls-proxy.sh.chroot b/mkosi.images/buildernet/mkosi.build.d/15-attested-tls-proxy.sh.chroot index ba69ee56..ce6dcefb 100755 --- a/mkosi.images/buildernet/mkosi.build.d/15-attested-tls-proxy.sh.chroot +++ b/mkosi.images/buildernet/mkosi.build.d/15-attested-tls-proxy.sh.chroot @@ -3,8 +3,8 @@ set -euo pipefail echo "Installing attested-tls-proxy..." -VERSION="v1.1.2" -EXPECTED_SHA256=3b1b1868a5fcb1c0a0b07cfa32a76d2c609c99b9705596cc4a528659208d0349 +VERSION="v2.0.1" +EXPECTED_SHA256=2c8f9f720a103f9d9cc1b53a0726b77f9310175057394cc4b2d5c565e49d849f curl -sSfL https://github.com/flashbots/attested-tls-proxy/releases/download/${VERSION}/attested-tls-proxy_1.${VERSION}_amd64.deb \ -o $PACKAGEDIR/attested-tls-proxy.deb echo "${EXPECTED_SHA256}" $PACKAGEDIR/attested-tls-proxy.deb | sha256sum --check