Tools for confidential virtual machines
Project description
cvmutils -- Prepare OS Images for Confidential Environments
Overview
cvmutils is a toolkit for preparing Linux OS images to run in confidential virtual machine (CVM) environments. It provides two CLI tools:
cvm-encrypt-image-- Pre-encrypts the root volume and seals the encryption key to a target vTPM using predicted PCR values. Also supports creating immutable, integrity-protected images with dm-verity.cvm-reseal(experimental) -- Re-seals LUKS volume keys on a running system when PCR measurements change (e.g., after a kernel or UKI update).
Previously named encrypt-rhel-image, the toolkit was originally targeted at Fedora/RHEL but now supports multiple other distributions.
Supported Distributions
- RHEL 9.4+, RHEL 10.0+
- Fedora 41+
- Azure Linux 3
- CentOS Stream 9, CentOS Stream 10
Other Linux distributions using UKI boot scheme with GPT partitioning should also work.
Image Formats
cvm-encrypt-image accepts image files in the following formats:
- VHD (
.vhd) - QCOW2 (
.qcow2) - Raw (
.raw) - Block devices directly (e.g.,
/dev/nbd0)
Image files are attached via qemu-nbd; block devices are used as-is.
Installation
From source (pip)
pip install .
or via the Makefile:
make install
To uninstall:
make uninstall
RPM packages
Three RPM subpackages are provided:
| Package | Contents |
|---|---|
python3-cvmutils |
cvm-encrypt-image CLI, Python library, man page, utility scripts |
cvmutils-reseal (experimental) |
cvm-reseal CLI |
cvmutils-dracut (experimental) |
Dracut module, cvmutils-rquote, systemd service |
To build RPMs locally:
rm -rf dist
make -f .copr/Makefile srpm outdir=. spec=.distro/python-cvmutils.spec
mock python-cvmutils-0.3.2-1.fc43.src.rpm
Container
See Running in a Container below.
Image Prerequisites
- Partition Table: GUID Partition Table (GPT).
- ESP Partition: Image must contain an ESP partition with GUID
C12A7328-F81F-11D2-BA4B-00A0C93EC93B. - Root Partition: Image must contain a "Linux root (x86-64)" partition with GUID
4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709. - LUKS Conversion: The root partition must be convertible to LUKS. To accommodate the LUKS header, the tool supports:
- Resizing the root partition (requires available space after root).
- Shrinking an
ext4filesystem.
- PCR7: The expected value can be specified explicitly (
--pcr7 <sha256value>) or predicted (--pcr7 auto) using one of the following profiles:- UEFI profile (
--uefi-profile) - Azure Disk Profile (
--az-disk-profile) - EFI vars profile (
--efivars-profile)Example templates are present in
tests/test-data.
- UEFI profile (
- PCR4: Can optionally be included in the set of PCRs for root volume key sealing. Its value can be specified directly (
--pcr4 <sha256value>) or predicted (--pcr4 auto).
How It Works
Image preparation is a two-phase workflow:
-
Encrypt -- The root partition is converted to LUKS2 in-place using
cryptsetup reencrypt --encrypt. A temporary cleartext password (derived from the root partition UUID) is used during this phase. -
Deploy -- The ESP is mounted and boot chains are discovered from
BOOTX64.CSV(UTF-16LE file enumerating the bootloader and UKI paths). For each boot chain, the expected PCR4 and/or PCR7 values are predicted by:- Parsing the Secure Boot configuration (PK, KEK, db, dbx) from a UEFI profile, Azure disk profile, or efivars
- Computing Authenticode hashes of PE binaries in the boot chain (shim, bootloader, UKI, addons)
- Simulating TPM PCR extend operations
The root volume key is then sealed to the target vTPM via
systemd-cryptenrollwith the predicted PCR policy. The temporary cleartext password is removed.
An optional third mode, makeverity, creates a dm-verity partition and a signed UKI addon containing the root hash instead of encrypting.
After deployment, the reseal tool (cvm-reseal) can be used on a running system to update TPM2 tokens when the boot chain changes.
TPM2
The SRK (Storage Root Key) public key is required for the deploy phase. It can be obtained from the target vTPM with:
systemd-analyze srk > public.srk
or
tpm2_readpublic -c 0x81000001 -o public.srk -t primary.handle
Usage: cvm-encrypt-image
Actions
| Action | Description |
|---|---|
encrypt |
Encrypt the root partition (LUKS2 conversion) |
encrypt-resume |
Resume an interrupted encryption |
deploy |
Seal the encryption key to a target vTPM |
makeverity (experimental) |
Create a dm-verity partition and signed UKI addon |
Basic Example
cvm-encrypt-image encrypt /path/to/image.vhd
cvm-encrypt-image deploy \
-s /path/to/public.srk \
--pcr7 auto \
--uefi-profile uefi_profile.json \
/path/to/image.vhd
Common Options
| Option | Description |
|---|---|
-n, --nbddev N |
NBD device number (default: 0) |
-v, --verbose |
Print additional info |
--encryption-password |
Use the specified temporary password for LUKS encryption instead of a randomly generated one |
Encrypt Options
| Option | Description |
|---|---|
-g, --growpart |
Grow root partition to the size of the volume before encryption |
--no-cloud-init |
Do not create /cc_growpart_keydata and LUKS keyslot for root volume resize |
--encrypt-progress-json FILE |
Write encryption progress to a file or - for stdout |
Deploy Options
| Option | Description |
|---|---|
-s, --srkpub FILE |
SRK public key (required) |
--pcr7 VALUE |
Expected PCR7 sha256 value, or auto for prediction |
--pcr4 VALUE |
Expected PCR4 sha256 value, or auto for prediction |
--uefi-profile FILE |
UEFI profile JSON for --pcr7 auto (mutually exclusive with other profile options) |
--az-disk-profile FILE |
Azure disk profile JSON for --pcr7 auto (mutually exclusive with other profile options) |
--efivars-profile DIR |
Efivars-format directory for --pcr7 auto (mutually exclusive with other profile options) |
--efivars-profile-no-attrs |
Efivars files lack the 4-byte attribute header (--efivars-profile only) |
--nosecureboot |
Predict PCRs with Secure Boot disabled |
-r, --recovery-key FILE |
Add a recovery key passphrase to the root volume |
--recovery-key-type {binary,text,both} |
Recovery key format (default: binary) |
Makeverity Options (Experimental)
| Option | Description |
|---|---|
--secureboot-cert FILE |
SecureBoot certificate (PEM) to sign UKI addon (required) |
--secureboot-key FILE |
SecureBoot key (PEM) to sign UKI addon (required) |
--volatile-overlay |
Add systemd.volatile=overlay for read-write overlay |
Example:
cvm-encrypt-image makeverity \
--secureboot-key=custom_db.key \
--secureboot-cert=custom_db.pem \
--volatile-overlay vol.raw
The signed root hash is placed on the ESP as /loader/addons/roothash.addon.efi.
Usage: cvm-reseal
cvm-reseal runs on a live system to update TPM2 tokens when the boot chain or Secure Boot configuration changes (e.g., after a kernel update that installs a new UKI).
It reads the current Secure Boot state from /sys/firmware/efi/efivars/, discovers boot chains from the ESP, and compares predicted PCR policies against existing LUKS tokens. New tokens are added and stale tokens can be removed.
Basic Example
cvm-reseal
Dry-run to see what would change:
cvm-reseal --dry-run
Options
| Option | Description |
|---|---|
-d, --dry-run |
List volumes needing re-seal without making changes |
-p, --pcrs PCRS |
PCRs to seal against, e.g., 4,7 or 4+7 (default: auto, reuses existing scheme) |
-r, --remove {none,matching,all} |
Remove unused tokens: none (default), matching PCR set, or all |
-u, --unlock {tpm,key-file} |
How to unlock LUKS for re-enrollment (default: tpm) |
--unlock-keyfile FILE |
Key file for --unlock key-file |
--tpm-srk-pub FILE |
TPM SRK public key (default: /run/systemd/tpm2-srk-public-key.tpm2b_public) |
-v, --verbose |
Print additional info |
Running in a Container
Three Containerfiles are provided:
| Containerfile | Base Image | Supported Actions |
|---|---|---|
Containerfile.fedora |
Fedora Rawhide | encrypt, deploy, makeverity |
Containerfile.c10s |
CentOS Stream 10 | encrypt, deploy, makeverity |
Containerfile.ubi10 |
UBI 10 | deploy only (no e2fsprogs, veritysetup, or ukify) |
Example (assuming data is in /data on the host):
docker build -f Containerfile.fedora -t cvmutils-fedora .
modprobe nbd max_part=8
qemu-nbd -c /dev/nbd0 vol.raw
docker run -it --privileged \
--mount type=bind,source=/data,target=/data \
--mount type=bind,source=/run/udev,target=/run/udev \
--mount type=bind,source=/dev,target=/dev \
cvmutils-fedora:latest \
cvm-encrypt-image encrypt -v /dev/nbd0
docker run -it --privileged \
--mount type=bind,source=/data,target=/data \
--mount type=bind,source=/run/udev,target=/run/udev \
--mount type=bind,source=/dev,target=/dev \
cvmutils-fedora:latest \
cvm-encrypt-image deploy \
-s /data/public.srk \
--pcr7 auto \
--uefi-profile /data/uefi-profile.json \
-v /dev/nbd0
qemu-nbd --disconnect /dev/nbd0
Utility Scripts
The scripts/ directory contains helper tools:
-
tpm_eventlog_to_uefi_profile.py-- Converts a TPM2 event log (YAML format) to a UEFI profile JSON suitable for--uefi-profile. RequiresPyYAML.python3 scripts/tpm_eventlog_to_uefi_profile.py /path/to/eventlog.yaml -o uefi_profile.json
-
print-recovery-key.py-- Converts a binary recovery key file to a human-readable dash-separated numeric format.python3 scripts/print-recovery-key.py /path/to/recovery.key
Dracut Module
The cvmutils-dracut RPM package installs a dracut module (99cvmutils) for use inside Azure CVM guests during initrd. It includes:
cvmutils-rquote-- A shell script that reads Azure attestation data from TPM2 NV indices, generates a nonce, and obtains a TPM2 quote. The results are stored under/run/cvmutils/.cvmutils-initrd.service-- A systemd oneshot service that runscvmutils-rquoteaftertpm2.targetand beforecryptsetup.target. Only activates whenConditionSecurity=measured-ukiis met.
The dracut module depends on the tpm2-tss dracut module.
Dependencies
Basic:
- python3.x
- setuptools (for Python < 3.13)
Encrypt phase:
- qemu-nbd (for image files; not needed for block devices)
- e2fsprogs (
e2fsck,resize2fs) - cryptsetup
- util-linux (
blkid,sfdisk)
Deploy phase additionally requires:
- openssl
- systemd-cryptenroll >= 255
Makeverity phase additionally requires:
- veritysetup
- ukify
Reseal (cvm-reseal) requires:
- cryptsetup
- systemd-cryptenroll
- bootctl (to locate ESP)
- lsblk
Optional (scripts):
- PyYAML (for
scripts/tpm_eventlog_to_uefi_profile.py)
Testing
The tool comes with unit tests which can be executed with pytest. The test-data submodule must be checked out first.
pip install pytest
git submodule update --init
pytest
Or via the Makefile:
make test
Out of tree end-to-end tests are available at https://gitlab.com/vkuznets/cvmutils-tests-e2e.
Development
Prerequisites
Install the development tools as needed:
pip install pylint build twine pytest
Additional tools required by specific targets: help2man (man), pyp2rpm, rpmbuild, and createrepo (rpm).
Makefile Targets
| Target | Description |
|---|---|
make lint, make pylint |
Run pylint on the cvmutils package |
make test, make check |
Run pytest |
make dist, make tarball |
Build source distribution and run twine check |
make rpm |
Build RPM packages via pyp2rpm and rpmbuild |
make man |
Regenerate the man page with help2man |
make install |
Install with pip install --user . |
make uninstall |
Uninstall the package |
make clean |
Remove build artifacts |
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
qemu-nbd fails with "No such file or directory" |
The nbd kernel module is not loaded |
Run modprobe nbd max_part=8 before using image files |
| Device or mount errors inside a container | Container lacks device access | Run the container with --privileged (see Running in a Container) |
"Permission denied reading …" from cvm-reseal |
Reading /sys/firmware/efi/efivars/ requires root |
Run cvm-reseal as root or with sudo |
| NBD device is busy | A previous run did not disconnect | Run qemu-nbd --disconnect /dev/nbd<N> to release the device |
| Encryption was interrupted | Power loss or process killed during cryptsetup reencrypt |
Resume with cvm-encrypt-image encrypt-resume |
udevadm settle fails in a container |
No udev daemon running | Ensure /run/udev from the host is bind-mounted into the container |
Unused Features Queued for Deprecation
ESP may contain an efivars.json file in Azure format:
{
"type": "Microsoft.Compute/disks",
"properties": {
"uefiSettings": {
"Boot0004": {
"guid": "Yd/ki8qT0hGqDQDgmAMrjA==",
"attributes": "Bw==",
"value": "AQAAAGIAUwBoAGkAbQAgAGIAbwBvAHQAIAB0AG8AIAA1AC4AMQA0AC4AMAAtADIAMwA4AF8AdQBrAGkAXwB0AGUAcwB0ADEAOQAuAGUAbAA5AC4AeAA4ADYAXwA2ADQAAAAEASoAAgAAAAAoAAAAAAAAAOAHAAAAAABibF3EAi9J4o1TPhDbQRiuAgIEBDQAXABFAEYASQBcAHIAZQBkAGgAYQB0AFwAcwBoAGkAbQB4ADYANAAuAGUAZgBpAAAAf/8EAFwARQBGAEkAXABMAGkAbgB1AHgAXAB2AG0AbABpAG4AdQB6AC0ANQAuADEANAAuADAALQAyADMAOABfAHUAawBpAF8AdABlAHMAdAAxADkALgBlAGwAOQAuAHgAOAA2AF8ANgA0AC0AdgBpAHIAdAAuAGUAZgBpAAAA"
}
}
}
}
In case it does, its size and content will be written to a special 'Linux reserved' GUID 8DA63339-0007-60C0-C436-083AC8230908 partition starting at offset 2048 * 512 = 1048576.
License
cvmutils is licensed under the GNU Lesser General Public License v2.1 or later (LGPL-2.1-or-later).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cvmutils-0.3.2.tar.gz.
File metadata
- Download URL: cvmutils-0.3.2.tar.gz
- Upload date:
- Size: 55.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fe5e2c5e17b2f5672d5bd2da185697fb3a3f03eede0d1c902583c414f13d51f
|
|
| MD5 |
63edb90a0ac816db39831e7539ea3a66
|
|
| BLAKE2b-256 |
b46bb525cf8914476594f39a229f4e2f779d9ab427c9e8ee4fa07b7c68cfdaae
|
File details
Details for the file cvmutils-0.3.2-py3-none-any.whl.
File metadata
- Download URL: cvmutils-0.3.2-py3-none-any.whl
- Upload date:
- Size: 49.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65c0e2b864012bbc12534a2ceef9b2d0e5f94d44d14759aecadb8abe1128d08f
|
|
| MD5 |
946a60f9fda4afea4cf247fc17d0dbee
|
|
| BLAKE2b-256 |
1940944759c69139d10077c38ee7ed57af2176831336a444bae0ef6a008a2d50
|