Skip to content
Closed
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
91 changes: 91 additions & 0 deletions .github/workflows/composable-python-layer-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,109 @@ on:
- scripts/build-python-runtime-layer.py
- scripts/test_build_python_runtime_layer.py
- crates/mvm-build/src/runtime_layer.rs
- crates/mvm-build/src/pr_test_synthetic_base.rs
- crates/mvm-cli/src/commands/vm/exec.rs
- crates/mvm-cli/src/commands/vm/exec/**
- crates/mvm-hostd/src/plan_admission/composition.rs
- crates/mvm-contract/src/plan/bundle.rs
- crates/mvm-core/src/registry_pack/runtime_layer.rs
- crates/mvm-core/examples/verify-runtime-layer-fixture.rs
- crates/mvm-build/src/guest_runtime/**
- crates/mvm-build/src/guest_runtime.rs
- crates/mvm-client/src/portable_boot_assets.rs
- crates/mvm-runtime/src/runtime_evidence.rs
workflow_dispatch:

permissions:
contents: read
id-token: write

jobs:
# Uses the ordinary unsigned guest builder, not a release signer. Signing
# happens only in the PR job below and nothing is published as a CLI release.
pr-guest-bins:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
uses: ./.github/workflows/guest-bins.yml
permissions:
contents: read

signed-pr-guest-runtime:
needs: pr-guest-bins
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
actions: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@1.97.1
- uses: sigstore/cosign-installer@v3
- uses: actions/download-artifact@v8
with:
name: mvm-guest-bins-attempt-${{ github.run_attempt }}
path: ${{ runner.temp }}/pr-runtime
- name: Sign and verify PR-only guest runtime inputs
env:
MVM_PR_TEST_RUNTIME_DIR: ${{ runner.temp }}/pr-runtime
MVM_PR_TEST_PULL_REQUEST: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
version="$(python3 -c 'import tomllib; print(tomllib.load(open("Cargo.toml", "rb"))["workspace"]["package"]["version"])')"
archive="$MVM_PR_TEST_RUNTIME_DIR/mvm-guest-bins-v${version}.tar.gz"
test -f "$archive" && test ! -L "$archive"
test -f "${archive}.sha256" && test ! -L "${archive}.sha256"
(cd "$MVM_PR_TEST_RUNTIME_DIR" && sha256sum --check "$(basename "${archive}.sha256")")
for blob in "$archive" "${archive}.sha256"; do
cosign sign-blob --yes --new-bundle-format --bundle "${blob}.bundle" "$blob"
done
cargo test --locked -p mvm-build --features pr-test-guest-runtime --lib \
guest_runtime::evidence::tests::signed_pr_runtime_authenticates_exact_identity_and_refuses_tampering \
-- --ignored --exact
cargo test --locked -p mvm-client --features pr-test-guest-runtime --lib \
portable_boot_assets::tests::pr_test_local_acquisition_checks_assembled_runtime_independently \
-- --ignored --exact
- name: Retain synthetic PR quartet for a builder-VM CLI witness
uses: actions/upload-artifact@v7
with:
name: pr-test-guest-runtime-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/pr-runtime/mvm-guest-bins-v*.tar.gz*
if-no-files-found: error
retention-days: 1

signed-pr-base-input:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@1.97.1
- uses: sigstore/cosign-installer@v3
- name: Authenticate only the PR's nonbootable synthetic base input
env:
MVM_EMBED: "0"
MVM_PR_TEST_BASE_DIR: ${{ runner.temp }}/pr-base
MVM_PR_TEST_PULL_REQUEST: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
cargo test --locked -p mvm-build --features pr-test-synthetic-base --lib \
pr_test_synthetic_base::tests::write_synthetic_pr_base_fixture_for_ci \
-- --ignored --exact
cosign sign-blob --yes --new-bundle-format \
--bundle "$MVM_PR_TEST_BASE_DIR/image-set.bundle" \
"$MVM_PR_TEST_BASE_DIR/image-set.json"
cargo test --locked -p mvm-build --features pr-test-synthetic-base --lib \
pr_test_synthetic_base::tests::real_pr_signature_accepts_only_unchanged_exact_identity \
-- --ignored --exact

signed-input:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ template-registry-s3 = [
# in-process) needs it turned on. `mvm-runtime` is named directly too since
# this crate depends on it straight, not only through `mvm-cli`.
test-support = ["mvm-cli/test-support", "mvm-runtime/test-support"]
pr-test-guest-runtime = ["mvm-cli/pr-test-guest-runtime"]
# Debug-only Apple Silicon warm-restore validation. The runtime additionally
# requires a debug build on macOS/aarch64 before advertising the capability.
hvf-live-validation = [
Expand Down
4 changes: 4 additions & 0 deletions crates/mvm-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ release-channel = []
# `builder_pack`'s tests can exercise `verify_pack_keyless_at` without
# every mvm-build build pulling the sigstore dependency tree.
manifest-verify = ["mvm-core/manifest-verify"]
# PR-only signed runtime inputs; never part of the release channel.
pr-test-guest-runtime = ["manifest-verify", "mvm-core/pr-test-guest-runtime"]
# Authenticate a synthetic PR base only as a local development input.
pr-test-synthetic-base = ["manifest-verify"]
# Exposes `published_image_set::fixture`, a signed-root-shaped image set served
# from a directory, to other crates' tests.
test-support = ["mvm-fs/test-support"]
Expand Down
38 changes: 29 additions & 9 deletions crates/mvm-build/src/guest_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,26 +182,46 @@ pub fn install_signed_guest_runtime_archive(
arch: GuestArch,
) -> Result<GuestRuntime, GuestRuntimeError> {
let verified = evidence::verify_release_archive(archive, checksum, bundle, version, arch)?;
let mut runtime = install_verified_archive(cache_root, &verified, arch)?;
runtime.release_proof = Some(evidence::RuntimeReleaseProof {
archive: archive.canonicalize()?,
checksum: checksum.canonicalize()?,
signature_bundle: bundle.canonicalize()?,
archive_sha256: verified.archive_sha256().to_string(),
version: version.to_string(),
arch,
});
Ok(runtime)
}

/// Install a PR-signed test archive; never mint release provenance.
#[cfg(feature = "pr-test-guest-runtime")]
pub fn install_pr_test_guest_runtime_archive(
cache_root: &Path,
proof: &mvm_core::protocol::runtime_release::PrTestRuntimeProof,
) -> Result<GuestRuntime, GuestRuntimeError> {
let verified =
evidence::verify_pr_test_runtime_proof(proof, &proof.input.version, proof.input.arch)?;
install_verified_archive(cache_root, &verified, proof.input.arch)
}

fn install_verified_archive(
cache_root: &Path,
verified: &evidence::VerifiedRuntimeManifest,
arch: GuestArch,
) -> Result<GuestRuntime, GuestRuntimeError> {
let base = cache_root.join("guest-runtime").join("v1");
fs::create_dir_all(&base)?;
let _lock = guest_agent_build::acquire_guest_build_lock(&base, "guest runtime")?;
let stage = stage_archive(&base, verified.archive_path())?;
let mut runtime = promote_staged_archive(
let runtime = promote_staged_archive(
&base,
stage,
verified.manifest().clone(),
verified.archive_sha256().to_string(),
arch,
ArchiveScope::Release,
)?;
runtime.release_proof = Some(evidence::RuntimeReleaseProof {
archive: archive.canonicalize()?,
checksum: checksum.canonicalize()?,
signature_bundle: bundle.canonicalize()?,
archive_sha256: verified.archive_sha256().to_string(),
version: version.to_string(),
arch,
});
Ok(runtime)
}

Expand Down
Loading