Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 52 additions & 14 deletions .github/workflows/amdsev-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -374,22 +374,47 @@ jobs:
echo "KATANA_ASSET_VARIANT=native"
} >> output/qemu/build-info.txt

- name: Build snp-tools
# snp-tools binaries are NOT shipped as release artifacts (separate
# release stream). We build them here only so the next step can run
# snp-digest to compute the published launch measurement.
# snp-tools/.cargo/config.toml pins build.target, so the binary lands
# under target/<triple>/release; locate it explicitly rather than
# hardcoding the triple here.
working-directory: misc/AMDSEV/snp-tools
- name: Obtain snp-digest
# Needed by the next step to compute the published launch measurement.
# Preferred source: the snp-tools-v* release pinned in build-config
# (SNP_DIGEST_RELEASE + SNP_DIGEST_SHA256, cut by the
# amdsev-snp-tools-release workflow), checksum-verified — the same
# binary install.sh uses on SNP hosts, so the publisher and the
# verifiers run identical code. Empty pins fall back to building the
# crate from this checkout (snp-tools/.cargo/config.toml pins
# build.target, so the binary lands under target/<triple>/release;
# locate it explicitly rather than hardcoding the triple).
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cargo build --release
SNP_DIGEST="$(find target -type f -name snp-digest -perm -u+x | head -n1)"
if [ -z "$SNP_DIGEST" ]; then
echo "snp-digest binary not found after build" >&2
exit 1
. ./build-config
if [ -n "${SNP_DIGEST_RELEASE:-}" ]; then
if [[ ! "${SNP_DIGEST_SHA256:-}" =~ ^[0-9a-f]{64}$ ]]; then
echo "SNP_DIGEST_RELEASE is pinned but SNP_DIGEST_SHA256 is not a sha256" >&2
exit 1
fi
echo "Downloading pinned snp-digest from ${SNP_DIGEST_RELEASE}"
gh release download "$SNP_DIGEST_RELEASE" --repo "$GITHUB_REPOSITORY" \
--pattern "snp-digest-${SNP_DIGEST_RELEASE}" --output snp-digest
actual="$(sha256sum snp-digest | awk '{print $1}')"
if [ "$actual" != "$SNP_DIGEST_SHA256" ]; then
echo "pinned snp-digest checksum mismatch: got $actual, want $SNP_DIGEST_SHA256" >&2
exit 1
fi
chmod +x snp-digest
./snp-digest --help >/dev/null
echo "SNP_DIGEST=$PWD/snp-digest" >> "$GITHUB_ENV"
else
echo "No SNP_DIGEST_RELEASE pin in build-config — building snp-tools from source"
cd snp-tools
cargo build --release
SNP_DIGEST="$(find target -type f -name snp-digest -perm -u+x | head -n1)"
if [ -z "$SNP_DIGEST" ]; then
echo "snp-digest binary not found after build" >&2
exit 1
fi
echo "SNP_DIGEST=$PWD/$SNP_DIGEST" >> "$GITHUB_ENV"
fi
echo "SNP_DIGEST=$PWD/$SNP_DIGEST" >> "$GITHUB_ENV"

- name: Compute sealed launch measurement
run: |
Expand Down Expand Up @@ -444,6 +469,8 @@ jobs:
tag="${VM_TAG}"
# shellcheck disable=SC2153
katana_ver="${KATANA_VER}"
# For SNP_DIGEST_RELEASE (the pinned verifier named in the notes).
. ./build-config
# Pull values from build-info.txt for the human-readable summary.
info_get() {
awk -F= -v k="$1" '$1 == k { sub(/^[^=]*=/, ""); print; exit }' output/qemu/build-info.txt
Expand Down Expand Up @@ -521,6 +548,17 @@ jobs:
echo "non-zero on any mismatch. For full from-source reproduction, see"
echo "\`misc/AMDSEV/reproduce-release.sh\`."
echo
if [ -n "${SNP_DIGEST_RELEASE:-}" ]; then
echo "The measurement above was computed with snp-digest from"
echo "\`${SNP_DIGEST_RELEASE}\` (pinned in \`misc/AMDSEV/build-config\`) —"
echo "hosts running \`misc/AMDSEV/install.sh\` download and checksum-verify"
echo "the same pinned binary."
else
echo "The measurement above was computed with snp-digest built from this"
echo "tag's \`misc/AMDSEV/snp-tools\` source (no prebuilt pinned in"
echo "\`misc/AMDSEV/build-config\`)."
fi
echo
echo "## Full build provenance"
echo
echo "<details>"
Expand Down
139 changes: 139 additions & 0 deletions .github/workflows/amdsev-snp-tools-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: amdsev-snp-tools-release

# Publishes a prebuilt snp-digest as a dedicated `snp-tools-v<X.Y.Z>` GitHub
# Release. snp-digest changes far less often than the TEE VM image, so it
# gets its own release stream instead of being rebuilt and re-attached on
# every tee-vm-v* release; consumers pin ONE snp-tools release:
#
# - misc/AMDSEV/build-config carries SNP_DIGEST_RELEASE + SNP_DIGEST_SHA256.
# The tee-vm release pipeline (amdsev-release.yml) downloads that pinned
# binary to compute the published launch measurement, and install.sh
# downloads it on SNP hosts so measurement verification needs no Rust
# toolchain. Empty pins mean both fall back to building from source.
#
# Runbook: dispatch this workflow with the new version, then open a PR
# bumping the two pins in build-config to the new tag + the SHA256 printed
# in the release notes. The prebuilt is a convenience copy, not the trust
# root — auditors rebuild snp-digest from the tagged snp-tools source
# (pinned rust-toolchain.toml + Cargo.lock) rather than trusting the asset.
#
# Cut a new release when the snp-tools crate changes in a way that affects
# snp-digest's output or interface (measurement algorithm inputs, CLI flags),
# then bump the pins. Runner-only build: snp-digest is pure userspace
# hashing; no SNP hardware involved.

on:
workflow_dispatch:
inputs:
version:
description: 'snp-tools version to publish (SemVer, no prefix; e.g. 0.1.0)'
required: true
type: string

concurrency:
group: amdsev-snp-tools-release
cancel-in-progress: false

defaults:
run:
working-directory: misc/AMDSEV/snp-tools

jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- name: Validate version and tag availability
id: ctx
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ inputs.version }}
run: |
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]]; then
echo "version must be plain SemVer (optionally -rc.N), got: $VERSION" >&2
exit 1
fi
tag="snp-tools-v${VERSION}"
if gh release view "$tag" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
echo "release $tag already exists" >&2
exit 1
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "sha=$GITHUB_SHA" >> "$GITHUB_OUTPUT"

- uses: Swatinem/rust-cache@v2
with:
workspaces: misc/AMDSEV/snp-tools

- name: Test
# Same gate as PR CI (amdsev-snp-tools.yml): snp-digest computes the
# measurement verifiers pin against — never ship an untested build.
run: cargo test --release --locked

- name: Build snp-digest
# snp-tools/.cargo/config.toml pins build.target, so the binary lands
# under target/<triple>/release; locate it explicitly rather than
# hardcoding the triple here.
run: |
cargo build --release --locked
SNP_DIGEST="$(find target -type f -name snp-digest -perm -u+x | head -n1)"
if [ -z "$SNP_DIGEST" ]; then
echo "snp-digest binary not found after build" >&2
exit 1
fi
"$SNP_DIGEST" --help >/dev/null
echo "SNP_DIGEST=$PWD/$SNP_DIGEST" >> "$GITHUB_ENV"

- name: Stage release artifacts
id: stage
env:
TAG: ${{ steps.ctx.outputs.tag }}
run: |
mkdir -p release
cp "$SNP_DIGEST" "release/snp-digest-${TAG}"
sha="$(sha256sum "release/snp-digest-${TAG}" | awk '{print $1}')"
printf '%s\n' "$sha" > "release/snp-digest-${TAG}.sha256"
echo "sha=$sha" >> "$GITHUB_OUTPUT"

- name: Generate release notes
env:
TAG: ${{ steps.ctx.outputs.tag }}
SHA: ${{ steps.stage.outputs.sha }}
run: |
toolchain="$(sed -n 's/^channel = "\(.*\)"$/\1/p' rust-toolchain.toml | head -n1)"
{
echo "# snp-tools ${TAG}"
echo
echo "Prebuilt \`snp-digest\` (x86_64 Linux, Rust ${toolchain}, \`--locked\`) — the"
echo "SEV-SNP launch-measurement calculator used by the TEE VM release pipeline"
echo "and by \`misc/AMDSEV/install.sh\` on SNP hosts."
echo
echo '```'
echo "snp-digest-${TAG} SHA-256: ${SHA}"
echo '```'
echo
echo "To adopt this release, bump both pins in \`misc/AMDSEV/build-config\`:"
echo
echo '```sh'
echo "SNP_DIGEST_RELEASE=\"${TAG}\""
echo "SNP_DIGEST_SHA256=\"${SHA}\""
echo '```'
echo
echo "This is a convenience copy, not the trust root: auditors should rebuild"
echo "\`snp-digest\` from this tag's \`misc/AMDSEV/snp-tools\` source (pinned"
echo "\`rust-toolchain.toml\` + \`Cargo.lock\`)."
} > release/release-notes.md

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.ctx.outputs.tag }}
target_commitish: ${{ steps.ctx.outputs.sha }}
prerelease: ${{ contains(inputs.version, '-rc.') }}
body_path: misc/AMDSEV/snp-tools/release/release-notes.md
files: |
misc/AMDSEV/snp-tools/release/snp-digest-*
12 changes: 12 additions & 0 deletions misc/AMDSEV/build-config
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,15 @@ LIBC6_DEV_PKG_SHA256="bbf5a155039042634961a61276650631ee47b9e721f91f8dbb731b0bbe
# KATANA_LUKS_UUID); their measurement will differ from the published one and
# they must recompute it themselves via sealed-cmdline.sh + snp-digest.
KATANA_CANONICAL_LUKS_UUID="00000000-0000-0000-0000-000000000001"

# Prebuilt snp-digest — a snp-tools-v* GitHub release cut by the
# amdsev-snp-tools-release workflow. The tee-vm release pipeline downloads it
# (instead of rebuilding the crate) to compute the published measurement, and
# install.sh downloads it on SNP hosts so measurement verification needs no
# Rust toolchain; both verify the download against SNP_DIGEST_SHA256, which
# is trusted because THIS file is versioned in git at the consuming tag.
# Empty pins = build snp-digest from source (the prebuilt is a convenience
# copy, never the trust root). Bump both together, from the values printed
# in the snp-tools release notes.
SNP_DIGEST_RELEASE=""
SNP_DIGEST_SHA256=""
18 changes: 11 additions & 7 deletions misc/AMDSEV/docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ The installer needs no root; `run.sh` invokes `start-vm.sh` via `sudo`
host has another version (or none), the installer offers to build it from
source via the release's `scripts/build-qemu.sh` — locally under the
install root (default) or into `/usr/local`.
- **Rust (optional but recommended):** `cargo` is needed to build
`snp-digest`, which verifies the release's published launch measurement
and computes the expected measurement for your configuration. Without it
the install still completes (artifact checksums are always verified), but
attestation verification has no local expected value until you install
Rust and run `install.sh verify`.
- **Rust (optional):** measurement verification uses `snp-digest`. Each
release's `build-config` pins a prebuilt `snp-tools-v*` release (tag +
SHA-256) that the installer downloads and checksum-verifies automatically,
so `cargo` is only needed as a fallback — for releases predating the pin,
or if you prefer building the verifier from source (the prebuilt is a
convenience copy, not the trust root). Without either, the install still
completes (artifact checksums are always verified), but attestation
verification has no local expected value until you run `install.sh verify`
with one of them available.

## What gets installed

Expand Down Expand Up @@ -155,7 +158,8 @@ a fresh file to keep the old disk recoverable under the old release.
then reload `kvm_amd` (or reboot). The host kernel must support SNP.
- **QEMU build fails** — install the build deps first:
`sudo apt-get install -y build-essential ninja-build pkg-config libglib2.0-dev libpixman-1-dev python3-venv flex bison wget`.
- **`cargo` missing / measurement not computed** — install Rust
- **Measurement not computed** — the release pins no prebuilt `snp-digest`
(its `build-config` predates the pin) and `cargo` is missing. Install Rust
(https://rustup.rs), then `~/.katana/tee-vm/install.sh verify`.
- **vCPUs locked to 1** — the chosen release's launcher predates
configurable vCPUs; pick a newer `tee-vm-v*` release.
Expand Down
37 changes: 35 additions & 2 deletions misc/AMDSEV/docs/release-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,41 @@ fetched from the **repo source at the same tag**
`misc/AMDSEV` tree part of the published interface: renaming `start-vm.sh`,
its flags/env vars (`KATANA_VCPUS`, `KATANA_MEMORY`, `HOST_RPC_PORT`, ...),
`scripts/sealed-cmdline.sh`, `verify-build.sh`, the `snp-tools` crate path,
or the tarball layout changes what installed hosts download on upgrade — the
installer feature-detects where it can, but treat such renames as breaking.
the `build-config` pin keys, or the tarball layout changes what installed
hosts download on upgrade — the installer feature-detects where it can, but
treat such renames as breaking.

## The snp-tools release stream

`snp-digest` (the measurement calculator) is published on its own release
line, `snp-tools-v<X.Y.Z>`, cut on demand by the
[`amdsev-snp-tools-release`](../../../.github/workflows/amdsev-snp-tools-release.yml)
workflow (assets: `snp-digest-<tag>` for x86_64 Linux + a bare-hex
`.sha256`). It changes far less often than the VM image, so one snp-tools
release serves many tee-vm releases instead of being rebuilt per release.

Consumers pin it in `build-config` (`SNP_DIGEST_RELEASE` +
`SNP_DIGEST_SHA256`):

- **This pipeline** downloads the pinned binary (checksum-verified) to
compute the published measurement — no cargo build in the release path.
- **`install.sh`** reads the pins from the installed tag's `build-config`
and downloads the same binary on SNP hosts, so measurement verification
needs no Rust toolchain. The checksum is trusted because `build-config` is
versioned in git at the consuming tag.
- **Empty pins** mean both fall back to building `snp-tools` from source
(also the behavior for tags predating the pins).

The prebuilt is a plain runner build — a convenience copy, **not** the trust
root, and not part of the reproducibility story (its checksum stays out of
`build-info.txt`, so `reproduce-release.sh` is unaffected). Auditors verify
it by rebuilding from the snp-tools tag's pinned source
(`rust-toolchain.toml` + `Cargo.lock`).

Runbook for a new snp-tools release: dispatch `amdsev-snp-tools-release`
with the next version, then open a PR bumping both `build-config` pins to
the tag + SHA-256 printed in its release notes. Cut one whenever the crate
changes in a way that affects `snp-digest`'s output or interface.

## What moves the measurement between releases

Expand Down
Loading
Loading