This guide explains how to verify the cryptographic build provenance attestations for CVM disk images.
- Overview
- Why Verify Attestations?
- Quick Start
- Verification for All Cloud Providers
- How Verification Works for Large Disk Images
- Understanding Certificate Verification
- What's Included in Attestations
- Inspecting Attestation Contents
- Integration with Deployment Pipelines
- Troubleshooting
- Verifying Binary Checksums
- Security Considerations
- Linking Build Attestation to Runtime Attestation
- Additional Resources
- Getting Help
All CVM disk images released from the cvm-image-builder CI/CD pipeline include SLSA Build Level 2 provenance attestations. These attestations cryptographically prove:
- ✅ The disk images were built by the official GitHub Actions workflow
- ✅ The exact source code (commit SHA) used to build the images
- ✅ The build environment, tools, and configuration
- ✅ The images haven't been tampered with since the build
Attestation verification protects against:
| Attack Scenario | Protection |
|---|---|
| Compromised release maintainer | ❌ Verification fails - not built by GitHub Actions |
| Mirror/CDN compromise | ❌ Verification fails - digest mismatch |
| Supply chain injection | ✅ Attestation shows exact source commit for auditing |
| Typosquatting/phishing | ❌ Certificate identity shows wrong repository |
| Post-build modification | ❌ SHA256 digest mismatch detected |
Install required tools:
# Install jq for JSON processing (required)
sudo apt-get install jq # Debian/Ubuntu
brew install jq # macOS
# Install openssl (usually pre-installed)
openssl version
# Optional: Install cosign for additional verification
# See: https://docs.sigstore.dev/cosign/installation/
brew install sigstore/tap/cosign # macOSThe easiest way to verify disk images (works with large files >128MB):
# Using the automata-linux repository
cd automata-linux
# Download disk image and build provenance
atakit get-disk aws
atakit download-build-provenance
# Verify the disk image
atakit verify-build-provenance aws_disk.vmdkThis method handles large disk images (>128MB) that exceed cosign's size limits by:
- Verifying the SHA256 hash matches the attestation
- Cryptographically verifying the certificate chain
- Checking the Rekor transparency log
- Displaying build metadata including source commit, binaries, and security configuration
# Download disk image from release
wget https://github.com/automata-network/automata-linux/releases/download/v1.0.0/aws_disk.vmdk
# Verify using GitHub Attestations API
gh attestation verify aws_disk.vmdk \
--owner automata-network \
--repo automata-linuxNote: GitHub CLI verification requires the repository to be public or GitHub Enterprise Cloud with repo access. Bundle-based verification is recommended for all scenarios.
# Using atakit (recommended)
atakit get-disk aws
atakit download-build-provenance
atakit verify-build-provenance aws_disk.vmdk# Using atakit (recommended)
atakit get-disk azure
atakit download-build-provenance
atakit verify-build-provenance azure_disk.vhd# Using atakit (recommended)
atakit get-disk gcp
atakit download-build-provenance
atakit verify-build-provenance gcp_disk.tar.gzCVM disk images (~200MB) exceed cosign's 128MB size limit for verify-blob-attestation. The verification process works around this by:
# Extract expected hash from attestation bundle
EXPECTED_HASH=$(cat aws_disk.vmdk.bundle | jq -r '.base64Signature' | base64 -d | \
jq -r '.payload' | base64 -d | jq -r '.subject[0].digest.sha256')
# Calculate actual hash of disk image
ACTUAL_HASH=$(sha256sum aws_disk.vmdk | awk '{print $1}')
# Compare hashes
if [ "$EXPECTED_HASH" == "$ACTUAL_HASH" ]; then
echo "✅ Hash matches - disk image integrity verified"
fi# Extract and verify certificate from GitHub Actions OIDC
CERT=$(cat aws_disk.vmdk.bundle | jq -r '.cert' | base64 -d)
# Verify issuer is GitHub Actions
openssl x509 -in <(echo "$CERT") -noout -text | \
grep "token.actions.githubusercontent.com"
# Verify workflow identity matches expected repository
openssl x509 -in <(echo "$CERT") -noout -text | \
grep -A1 "Subject Alternative Name" | \
grep "https://github.com/automata-network/"# Verify Rekor transparency log entry exists
cat aws_disk.vmdk.bundle | jq -e '.rekorBundle' > /dev/null
echo "✅ Rekor transparency log entry present"This approach provides the same security guarantees as cosign verification:
- ✅ Integrity: Hash proves disk image hasn't been modified
- ✅ Authenticity: Certificate proves attestation came from GitHub Actions
- ✅ Non-repudiation: Rekor log provides immutable public record
- ✅ Identity: Certificate identity proves it was built by the correct workflow
The verification process checks the cryptographic certificate embedded in the attestation bundle to ensure it was issued by GitHub Actions and matches the expected workflow identity.
The certificate contains a "Subject Alternative Name" that identifies which GitHub workflow created the attestation:
https://github.com/automata-network/cvm-image-builder/.github/workflows/build-and-release.yml@refs/tags/v1.0.0
This includes:
- Organization:
automata-network - Repository:
cvm-image-builder - Workflow:
.github/workflows/build-and-release.yml - Git ref:
refs/tags/v1.0.0
The verification script checks that this identity matches the pattern ^https://github.com/automata-network/.* to prevent accepting attestations from:
- ❌ Forked repositories
- ❌ Different organizations
- ❌ Malicious actors impersonating the workflow
The certificate is issued by GitHub Actions OIDC provider through Sigstore's Fulcio CA:
How it works:
- GitHub Actions requests an OIDC token from
https://token.actions.githubusercontent.com - Sigstore's Fulcio CA verifies the token and issues a short-lived signing certificate
- The certificate contains the OIDC issuer in its metadata
- During verification, the script confirms the issuer is
https://token.actions.githubusercontent.com
This ensures the signature came from GitHub Actions, not an impersonator.
Each attestation contains comprehensive build metadata:
- Main repository commit SHA
- Submodule commit SHAs (python-uefivars)
- Git ref (tag/branch that triggered the build)
- Workflow name and run ID
- cvm-agent binary: SHA256 of
attestation_agent - cvm-agent library: SHA256 of
libcvm.so - Kernel image: SHA256 of
kernel.img - Kernel certificate: SHA256 of
kernel.crt
These checksums can be cross-referenced with official builds from cvm-components-builder releases.
- Secure Boot key fingerprints: SHA256 fingerprints of PK, KEK, db, and kernel certificates
- dm-verity root hash: Root hash for rootfs partition integrity
- dm-verity hash file: SHA256 of the verity hash file
- Kernel version (e.g.,
6.15.11-automata) - Tool versions: QEMU, sbsign, Python, Make, GCC
- Build timestamp (ISO 8601 UTC)
- Runner OS and architecture
- Builder identity (GitHub Actions run URL)
- AWS VMDK SHA256 checksum
- Azure VHD SHA256 checksum
- GCP tar.gz SHA256 checksum
The verify-build-provenance command automatically displays key build metadata. To view the complete metadata:
# Extract full build metadata from bundle
cat aws_disk.vmdk.bundle | jq -r '.base64Signature' | base64 -d | \
jq -r '.payload' | base64 -d | jq '.predicate'Or save it to a file:
cat aws_disk.vmdk.bundle | jq -r '.base64Signature' | base64 -d | \
jq -r '.payload' | base64 -d | jq '.predicate' > build-metadata.jsonGet source repository and commit:
cat aws_disk.vmdk.bundle | jq -r '.base64Signature' | base64 -d | \
jq -r '.payload' | base64 -d | jq -r '.predicate.buildDefinition.resolvedDependencies[0].digest.gitCommit'Get build timestamp:
cat aws_disk.vmdk.bundle | jq -r '.base64Signature' | base64 -d | \
jq -r '.payload' | base64 -d | jq -r '.predicate.runDetails.metadata.startedOn'Get binary checksums:
cat aws_disk.vmdk.bundle | jq -r '.base64Signature' | base64 -d | \
jq -r '.payload' | base64 -d | jq '.predicate.buildDefinition.internalParameters.binary_checksums'Get security configuration (secure boot, dm-verity):
cat aws_disk.vmdk.bundle | jq -r '.base64Signature' | base64 -d | \
jq -r '.payload' | base64 -d | jq '{secure_boot, dm_verity: .buildDefinition.internalParameters | {secure_boot, dm_verity}}'# Extract and inspect the signing certificate
cat aws_disk.vmdk.bundle | jq -r '.cert' | base64 -d | openssl x509 -text -nooutLook for the "Subject Alternative Name" extension which contains the workflow identity.
| Attack Type | Protection |
|---|---|
| ✅ Binary substitution attacks | Prevents swapping disk images with malicious versions |
| ✅ Supply chain attacks | Verifies the build came from the official source |
| ✅ Tampering | Detects any modifications to disk images after build |
| ✅ Provenance tracking | Links images to specific source code commits |
| Limitation | Mitigation |
|---|---|
| ❌ Compromised source code | Attestations verify "what was built," not "what should be built" - review source code changes |
| ❌ Malicious workflow changes | Review workflow changes in pull requests before merging |
| ❌ Compromised dependencies | Verify submodule SHAs and binary sources against known good values |
| ❌ Runtime attacks | Use runtime attestation (TPM PCRs/RTMRs) for deployed VMs |
- Always verify attestations before deploying disk images
- Check binary checksums against known good values from official releases
- Verify secure boot key fingerprints match your trusted keys
- Review build metadata for unexpected tool versions or parameters
- Automate verification in your deployment pipelines
- Store bundle files with your disk images for offline verification
- Restrict certificate identity to specific repositories/workflows in production
The build attestation captures the dm-verity root hash which is measured into TPM PCRs at boot. This creates a chain of trust from build to runtime:
Source Code (git commit)
↓ (attested by GitHub Actions)
Disk Image Build (attestation)
↓ (includes dm-verity root hash)
Boot Process (measures rootfs)
↓ (extends TPM PCR)
Runtime Attestation (TPM quote)
To verify the chain:
- Verify build attestation (this guide)
- Extract dm-verity root hash from attestation
- Get runtime TPM quote from deployed VM
- Verify root hash in PCR matches attestation
# The verify-build-provenance command displays the dm-verity root hash
atakit verify-build-provenance aws_disk.vmdk
# Or extract it manually
DM_VERITY_HASH=$(cat aws_disk.vmdk.bundle | jq -r '.base64Signature' | base64 -d | \
jq -r '.payload' | base64 -d | \
jq -r '.predicate.buildDefinition.internalParameters.dm_verity.root_hash')
echo "Build attestation dm-verity hash: $DM_VERITY_HASH"
# Get runtime measurements from deployed VM
# (Commands depend on CSP and TEE type - TDX vs SEV-SNP)
# Compare with runtime PCR values to ensure integrity