build: seal CoCo extension verity params into SE image

The IBM SEL composable image (boot-image-se-runtime-rs-tarball) was
sealing a bare `kata.extension.coco.verity_params` key into the kernel
command line with no value, leaving the CoCo extension unmeasured on
other architectures where the extension image carries a dm-verity hash
partition.

Now `root_hash_coco-extension.txt` is published by guest-components
as part of the `coco-extension-disk` OCI artifact (pinned in versions.yaml)
and is bundled inside `kata-static-rootfs-image-coco-extension.tar.zst`.

Changes:
- Makefile: add `rootfs-image-coco-extension-tarball` to the DEPS of
`boot-image-se-runtime-rs-tarball` so the root hash is available
before the SE image is sealed.
- build_se_image.sh: extract `root_hash_coco-extension.txt` from the
extension tarball (mirroring `_collect_root_hashes`) and export it as
COCO_VERITY_PARAMS when SE_COMPOSABLE=yes. Left unset when absent.
- lib_se.sh: emit `kata.extension.coco.verity_params=${COCO_VERITY_PARAMS}`
when set.
- lib.sh (k8s tests): export COCO_VERITY_PARAMS from the installed root
hash file before calling repack_secure_image.

The shim-v2-rust-tarball path already works via _collect_root_hashes
and the all-parallel → SHIM_TARBALLS build ordering.

Update docs to cover both the qemu-se and qemu-se-runtime-rs build
paths, and extend the composable-vm-images security model with the IBM
SE genprotimg sealing mechanism and a general unmeasured-extensions note.

Generated-by: IBM Bob
Signed-off-by: Hyounggyu Choi <Hyounggyu.Choi@ibm.com>
This commit is contained in:
Hyounggyu Choi
2026-08-04 08:55:48 +02:00
parent 86bba45db1
commit c8ce3e397b
7 changed files with 135 additions and 26 deletions

View File

@@ -647,6 +647,27 @@ Confidential Containers:
without changing the measurement, which would be detected during
attestation.
- IBM Secure Execution achieves equivalent integrity through a different mechanism.
`genprotimg` seals the full kernel command line — including
`kata.extension.coco.verity_params=root_hash=...,salt=...,data_blocks=...,...`
— into the SE image header at build time, encrypted under the host key. The sealed
contents are integrity-protected and cannot be altered without rebuilding the image.
The build-time dependency on
`rootfs-image-coco-extension-tarball` ensures the root hash published at the
pinned `versions.yaml` commit is locked into the SE image at creation time.
Substituting the extension image at runtime would be detected by dm-verity verification
during boot.
- **Unmeasured extensions** — when `kata.extension.coco.verity_params` carries no
value (a bare key), the mount script performs a raw mount without dm-verity
verification. This is the correct path when the extension image was intentionally
built without a hash partition (e.g. because the surrounding platform already
provides a different integrity guarantee). The mount script cross-checks the
cmdline against the on-disk partition layout to prevent a measured extension from
being silently downgraded: if a hash partition is present on disk but
`verity_params` is bare, the mount is refused and the VM shuts down. See the
[integrity policy table](#mount-script) above.
- **Mount failure = VM shutdown** — `OnFailure=poweroff.target` ensures the
VM does not proceed with missing or tampered components.

View File

@@ -70,16 +70,12 @@ kata-ubuntu-20.04-confidential.initrd
kata-ubuntu-latest.image
kata-ubuntu-latest-confidential.image
vmlinux-6.1.62-121
vmlinux-6.1.62-121-confidential
vmlinux.container
vmlinux-confidential.container
vmlinuz-6.1.62-121
vmlinuz-6.1.62-121-confidential
vmlinuz.container
vmlinuz-confidential.container
```
The output indicates the deployment of the kernel (`vmlinux-6.1.62-121-confidential`, though the version
The output indicates the deployment of the kernel (`vmlinux-6.1.62-121`, though the version
may vary at the time of testing), rootfs-image (`kata-ubuntu-latest-confidential.image`), and rootfs-initrd (`kata-ubuntu-20.04-confidential.initrd`).
In this scenario, the available kernel and initrd can be utilized for a secure image.
However, if any of these components are absent, they must be built from the
@@ -89,18 +85,18 @@ However, if any of these components are absent, they must be built from the
$ # Assume that the project is cloned at $GOPATH/src/github.com/kata-containers
$ cd $GOPATH/src/github.com/kata-containers/kata-containers
$ make rootfs-initrd-confidential-tarball
$ tar --zstd -tf build/kata-static-kernel-confidential.tar.zst | grep vmlinuz
./opt/kata/share/kata-containers/vmlinuz-confidential.container
./opt/kata/share/kata-containers/vmlinuz-6.7-136-confidential
$ tar --zstd -tf build/kata-static-kernel.tar.zst | grep vmlinuz
./opt/kata/share/kata-containers/vmlinuz.container
./opt/kata/share/kata-containers/vmlinuz-6.7-136
$ kernel_version=6.7-136
$ tar --zstd -tf build/kata-static-rootfs-initrd-confidential.tar.zst | grep initrd
./opt/kata/share/kata-containers/kata-containers-initrd-confidential.img
./opt/kata/share/kata-containers/kata-ubuntu-20.04-confidential.initrd
$ mkdir artifacts
$ tar --zstd -xvf build/kata-static-kernel-confidential.tar.zst -C artifacts ./opt/kata/share/kata-containers/vmlinuz-${kernel_version}-confidential
$ tar --zstd -xvf build/kata-static-kernel.tar.zst -C artifacts ./opt/kata/share/kata-containers/vmlinuz-${kernel_version}
$ tar --zstd -xvf build/kata-static-rootfs-initrd-confidential.tar.zst -C artifacts ./opt/kata/share/kata-containers/kata-ubuntu-20.04-confidential.initrd
$ ls artifacts/opt/kata/share/kata-containers/
kata-ubuntu-20.04-confidential.initrd vmlinuz-${kernel_version}-confidential
kata-ubuntu-20.04-confidential.initrd vmlinuz-${kernel_version}
```
3. Secure Image Generation Tool
@@ -150,13 +146,25 @@ and two certificates and one revocation list at `$HOME/certificates`:
- `DigiCert` intermediate CA certificate as `DigiCertCA.crt`
- IBM Z host key certificate revocation list as `ibm-z-host-key-gen2.crl`
There are two distinct image variants, corresponding to the two supported
runtime classes:
- **`qemu-se`** (Go runtime) — uses a monolithic confidential initrd that
bundles all CoCo guest components.
- **`qemu-se-runtime-rs`** (Rust runtime, composable) — uses a lean base
initrd plus a separately-attached
[CoCo extension image](../design/composable-vm-images.md).
The instructions below apply to both variants unless a variant is called out
explicitly.
you can construct a secure image using the following procedure:
```
$ # Change a directory to the project root
$ cd $GOPATH/src/github.com/kata-containers/kata-containers
$ host_key_document=$HOME/host-key-document/HKD-0000-0000000.crt
$ kernel_image=artifacts/opt/kata/share/kata-containers/vmlinuz-${kernel_version}-confidential
$ kernel_image=artifacts/opt/kata/share/kata-containers/vmlinuz-${kernel_version}
$ initrd_image=artifacts/opt/kata/share/kata-containers/kata-ubuntu-20.04-confidential.initrd
$ echo "panic=1 scsi_mod.scan=none swiotlb=262144 agent.log=debug" > parmfile
$ genprotimg --host-key-document=${host_key_document} \
@@ -183,10 +191,12 @@ $ genprotimg --host-key-document=${host_key_document} \
--cert=${cacert} --cert=${signcert} --crl=${crl} --parmfile=parmfile
```
The steps with no verification, including the dependencies for the kernel and initrd,
can be easily accomplished by issuing the following make target:
The steps with no verification, including the dependencies for the kernel and
initrd, can be easily accomplished by issuing the following make target.
```
=== `qemu-se` (Go runtime, monolithic)
```bash
$ cd $GOPATH/src/github.com/kata-containers/kata-containers
$ mkdir hkd_dir && cp $host_key_document hkd_dir
$ HKD_PATH=hkd_dir SE_KERNEL_PARAMS="agent.log=debug" make boot-image-se-tarball
@@ -194,7 +204,29 @@ $ ls build/kata-static-boot-image-se.tar.zst
build/kata-static-boot-image-se.tar.zst
```
`SE_KERNEL_PARAMS` could be used to add any extra kernel parameters. If no additional kernel configuration is required, this can be omitted.
=== `qemu-se-runtime-rs` (Rust runtime, composable)
This target automatically builds `rootfs-image-coco-extension-tarball` as a
prerequisite. The CoCo extension image is pinned to the commit in
`versions.yaml` (`.externals.coco-guest-components.version`), and its
dm-verity root hash (`root_hash_coco-extension.txt`) is bundled inside the
extension tarball and published by the [guest-components](https://github.com/confidential-containers/guest-components) project. The build
extracts this hash and seals it into the SE kernel command line as
`kata.extension.coco.verity_params=root_hash=...,salt=...,data_blocks=...,...`
via `genprotimg`. Any substitution of the extension image is therefore
detected at boot time — the sealed hash will not match.
```bash
$ cd $GOPATH/src/github.com/kata-containers/kata-containers
$ mkdir hkd_dir && cp $host_key_document hkd_dir
$ HKD_PATH=hkd_dir SE_KERNEL_PARAMS="agent.log=debug" make boot-image-se-runtime-rs-tarball
$ ls build/kata-static-boot-image-se-runtime-rs.tar.zst build/kata-static-rootfs-image-coco-extension.tar.zst
build/kata-static-boot-image-se-runtime-rs.tar.zst
build/kata-static-rootfs-image-coco-extension.tar.zst
```
`SE_KERNEL_PARAMS` can be used to inject extra kernel parameters into both
variants. It can be omitted if no additional configuration is needed.
In production, you could build an image by running the same command, but with the
following environment variables for key verification:
@@ -327,15 +359,19 @@ please refer to the
for confidential containers.
```
The following examples show the build commands for each runtime class.
=== `qemu-se` (Go runtime, monolithic)
```bash
$ cd $GOPATH/src/github.com/kata-containers/kata-containers
$ host_key_document=$HOME/host-key-document/HKD-0000-0000000.crt
$ mkdir hkd_dir && cp $host_key_document hkd_dir
$ # kernel-confidential and rootfs-initrd-confidential are built automactially by the command below
$ # kernel and rootfs-initrd-confidential are built automatically by the command below
$ HKD_PATH=hkd_dir SE_KERNEL_PARAMS="agent.log=debug" make boot-image-se-tarball
$ make qemu-tarball
$ make virtiofsd-tarball
$ make shim-v2-tarball
$ make shim-v2-go-tarball
$ mkdir kata-artifacts
$ build_dir=$(readlink -f build)
$ cp -r $build_dir/*.tar.zst kata-artifacts
@@ -343,12 +379,40 @@ $ ls -1 kata-artifacts
kata-static-agent.tar.zst
kata-static-boot-image-se.tar.zst
kata-static-coco-guest-components.tar.zst
kata-static-kernel-confidential-modules.tar.zst
kata-static-kernel-confidential.tar.zst
kata-static-kernel.tar.zst
kata-static-pause-image.tar.zst
kata-static-qemu.tar.zst
kata-static-rootfs-initrd-confidential.tar.zst
kata-static-shim-v2.tar.zst
kata-static-shim-v2-go.tar.zst
kata-static-virtiofsd.tar.zst
$ ./tools/packaging/kata-deploy/local-build/kata-deploy-merge-builds.sh kata-artifacts
```
=== `qemu-se-runtime-rs` (Rust runtime, composable)
`boot-image-se-runtime-rs-tarball` automatically builds the CoCo extension
tarball as a prerequisite, so `kata-static-rootfs-image-coco-extension.tar.zst`
is produced alongside the SE image and must be included in the payload.
```bash
$ cd $GOPATH/src/github.com/kata-containers/kata-containers
$ host_key_document=$HOME/host-key-document/HKD-0000-0000000.crt
$ mkdir hkd_dir && cp $host_key_document hkd_dir
$ # kernel and rootfs-initrd are built automatically by the command below
$ HKD_PATH=hkd_dir SE_KERNEL_PARAMS="agent.log=debug" make boot-image-se-runtime-rs-tarball
$ make qemu-tarball
$ make virtiofsd-tarball
$ make shim-v2-rust-tarball
$ mkdir kata-artifacts
$ build_dir=$(readlink -f build)
$ cp -r $build_dir/*.tar.zst kata-artifacts
$ ls -1 kata-artifacts
kata-static-boot-image-se-runtime-rs.tar.zst
kata-static-kernel.tar.zst
kata-static-qemu.tar.zst
kata-static-rootfs-image-coco-extension.tar.zst
kata-static-rootfs-initrd.tar.zst
kata-static-shim-v2-rust.tar.zst
kata-static-virtiofsd.tar.zst
$ ./tools/packaging/kata-deploy/local-build/kata-deploy-merge-builds.sh kata-artifacts
```

View File

@@ -416,6 +416,15 @@ set_metadata_annotation() {
fi
if [[ "${KATA_HYPERVISOR}" == "qemu-se-runtime-rs" ]]; then
export SE_COMPOSABLE="yes"
# Seed the CoCo extension verity params so the rebuilt SE image seals
# the actual root hash into its kernel cmdline. The file is absent on
# s390x (unmeasured extension); the bare-key fallback in lib_se.sh
# handles that case correctly.
local root_hash_file="/opt/kata/share/kata-containers/root_hash_coco-extension.txt"
if [[ -f "${root_hash_file}" ]]; then
COCO_VERITY_PARAMS="$(< "${root_hash_file}")"
export COCO_VERITY_PARAMS
fi
fi
repack_secure_image "${value}" "${IBM_SE_CREDS_DIR}" "true"
fi

View File

@@ -73,6 +73,21 @@ build_image() {
for tarball_id in kernel "${initrd_tarball_id}"; do
tar --zstd -xvf "kata-static-${tarball_id}.tar.zst" -C "${image_source_dir}"
done
# For the composable path, extract the CoCo extension root hash so the
# sealed SE kernel cmdline carries the verity params.
# COCO_VERITY_PARAMS must be set; lib_se.sh will die if it is absent.
if [[ "${SE_COMPOSABLE}" == "yes" ]]; then
local coco_ext_tarball="kata-static-rootfs-image-coco-extension.tar.zst"
local root_hash_path="./opt/kata/share/kata-containers/root_hash_coco-extension.txt"
tar --zstd -tf "${coco_ext_tarball}" "${root_hash_path}" >/dev/null 2>&1 \
|| die "root_hash_coco-extension.txt not found in ${coco_ext_tarball}"
local root_hash_tmp
root_hash_tmp="$(tar --zstd -xOf "${coco_ext_tarball}" "${root_hash_path}")"
root_hash_tmp="${root_hash_tmp%$'\r'}"
[[ -n "${root_hash_tmp}" ]] || die "Empty root_hash_coco-extension.txt in ${coco_ext_tarball}"
export COCO_VERITY_PARAMS="${root_hash_tmp}"
fi
popd
protimg_source_dir="${image_source_dir}${prefix}/share/kata-containers"

View File

@@ -74,10 +74,10 @@ EOF
return 1
fi
cmdline="${kernel_params} panic=1 scsi_mod.scan=none swiotlb=262144 agent.debug_console agent.debug_console_vport=1026"
if [[ "${SE_COMPOSABLE:-no}" == "yes" ]]; then
cmdline="${kernel_params} panic=1 scsi_mod.scan=none swiotlb=262144 agent.debug_console agent.debug_console_vport=1026 kata.extension.coco.verity_params"
else
cmdline="${kernel_params} panic=1 scsi_mod.scan=none swiotlb=262144 agent.debug_console agent.debug_console_vport=1026"
[[ -n "${COCO_VERITY_PARAMS:-}" ]] || die "COCO_VERITY_PARAMS must be set for composable SE image builds"
cmdline+=" kata.extension.coco.verity_params=${COCO_VERITY_PARAMS}"
fi
parmfile="$(mktemp --suffix=-cmdline)"
echo "${cmdline}" > "${parmfile}"

View File

@@ -319,7 +319,7 @@ boot-image-se-tarball: $(DEPS)
ifeq ($(FAKE_SE_IMAGE),true)
DEPS :=
else
DEPS := kernel-tarball rootfs-initrd-tarball
DEPS := kernel-tarball rootfs-initrd-tarball rootfs-image-coco-extension-tarball
endif
boot-image-se-runtime-rs-tarball: $(DEPS)
${MAKE} $@-build

View File

@@ -279,7 +279,7 @@ externals:
coco-guest-components:
description: "Provides attested key unwrapping for image decryption"
url: "https://github.com/confidential-containers/guest-components/"
version: "fff3a966b7b1d1654d172a7cd81c0f563721ff0b"
version: "d4dce5ce62294cfa741225f7e5b4527ea276f326"
# Scratch OCI container image (guest-components "Publish OCI container
# image" step). Consumed by monolithic confidential rootfs builds.
# yamllint disable-line rule:line-length