build: consume guest-components CoCo artefacts instead of building locally

guest-components' coco-extension-image workflow now publishes two
artefacts from the same assembled rootfs, and kata consumes each on the
matching path:

* Monolithic confidential rootfs (Go runtime): the scratch OCI container
  image from the "Publish OCI container image" step
  (ghcr.io/confidential-containers/guest-components/coco-extension).
  install_coco_guest_components() resolves the per-arch manifest digest,
  verifies provenance, and exports the filesystem into
  kata-static-coco-guest-components.tar.zst (binaries, cryptsetup, pause
  bundle, ocicrypt config). This replaces the local guest-components
  compile and removes the separate pause-image dependency from
  confidential rootfs targets.

* Composable extension (runtime-rs): the EROFS + dm-verity disk image
  from the "Publish disk image with ORAS" step
  (ghcr.io/confidential-containers/guest-components/coco-extension-disk).
  install_image_coco_extension() resolves the per-arch digest, verifies
  provenance, and oras-pulls that exact digest into kata-static.

Both paths pin the guest-components revision under
.externals.coco-guest-components in versions.yaml (version,
container_image, and extension_image must stay in sync). Provenance
verification uses gh attestation verify --bundle-from-oci and fails the
build by default (VERIFY_COCO_EXTENSION_PROVENANCE=no to bypass; skipped
on s390x where gh has no binary). The build container installs the GitHub
CLI and forwards GITHUB_TOKEN from the runner into the container.

Signed-off-by: Fabiano Fidêncio <ffidencio@nvidia.com>
Assisted-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Fabiano Fidêncio
2026-07-13 14:26:25 +02:00
committed by Mikko Ylinen
parent e49ad473bb
commit cd75c2fac5
10 changed files with 316 additions and 158 deletions

View File

@@ -57,7 +57,6 @@ jobs:
- ovmf
- ovmf-sev
- ovmf-tdx
- pause-image
- qemu
- qemu-snp-experimental
- qemu-tdx-experimental
@@ -108,6 +107,8 @@ jobs:
RELEASE: ${{ inputs.stage == 'release' && 'yes' || 'no' }}
KBUILD_SIGN_PIN: ${{ contains(matrix.asset, 'nvidia') && secrets.KBUILD_SIGN_PIN || '' }}
USE_DEVMAPPER: ${{ matrix.asset == 'agent' && 'yes' || 'no' }}
GH_TOKEN: ${{ github.token }}
VERIFY_COCO_EXTENSION_PROVENANCE: "yes"
- name: Parse OCI image name and digest
id: parse-oci-segments
@@ -266,6 +267,9 @@ jobs:
TARGET_BRANCH: ${{ inputs.target-branch }}
RELEASE: ${{ inputs.stage == 'release' && 'yes' || 'no' }}
KBUILD_SIGN_PIN: ${{ contains(matrix.asset, 'nvidia') && secrets.KBUILD_SIGN_PIN || '' }}
# Used to verify the guest-components CoCo extension provenance.
GH_TOKEN: ${{ github.token }}
VERIFY_COCO_EXTENSION_PROVENANCE: "yes"
- name: store-artifact ${{ matrix.asset }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0

View File

@@ -55,7 +55,6 @@ jobs:
- kernel-nvidia-gpu
- nydus
- ovmf
- pause-image
- qemu
- virtiofsd
concurrency:
@@ -101,6 +100,8 @@ jobs:
RELEASE: ${{ inputs.stage == 'release' && 'yes' || 'no' }}
KBUILD_SIGN_PIN: ${{ contains(matrix.asset, 'nvidia') && secrets.KBUILD_SIGN_PIN || '' }}
USE_DEVMAPPER: ${{ matrix.asset == 'agent' && 'yes' || 'no' }}
GH_TOKEN: ${{ github.token }}
VERIFY_COCO_EXTENSION_PROVENANCE: "yes"
- name: Parse OCI image name and digest
id: parse-oci-segments
@@ -255,6 +256,8 @@ jobs:
TARGET_BRANCH: ${{ inputs.target-branch }}
RELEASE: ${{ inputs.stage == 'release' && 'yes' || 'no' }}
KBUILD_SIGN_PIN: ${{ contains(matrix.asset, 'nvidia') && secrets.KBUILD_SIGN_PIN || '' }}
GH_TOKEN: ${{ github.token }}
VERIFY_COCO_EXTENSION_PROVENANCE: "yes"
- name: store-artifact ${{ matrix.asset }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
@@ -275,7 +278,6 @@ jobs:
- busybox
- coco-guest-components
- kernel-nvidia-gpu-modules
- pause-image
concurrency:
group: ${{ github.workflow }}-${{ github.job }}-${{ github.event.pull_request.number || github.ref }}-arm-${{ toJSON(matrix) }}
cancel-in-progress: true

View File

@@ -47,7 +47,6 @@ jobs:
- coco-guest-components
- fake-boot-image-se
- kernel
- pause-image
- qemu
- virtiofsd
concurrency:
@@ -99,6 +98,7 @@ jobs:
TARGET_BRANCH: ${{ inputs.target-branch }}
RELEASE: ${{ inputs.stage == 'release' && 'yes' || 'no' }}
USE_DEVMAPPER: ${{ matrix.asset == 'agent' && 'yes' || 'no' }}
GH_TOKEN: ${{ github.token }}
- name: Parse OCI image name and digest
id: parse-oci-segments
@@ -237,6 +237,7 @@ jobs:
ARTEFACT_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
TARGET_BRANCH: ${{ inputs.target-branch }}
RELEASE: ${{ inputs.stage == 'release' && 'yes' || 'no' }}
GH_TOKEN: ${{ github.token }}
- name: store-artifact ${{ matrix.asset }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0

View File

@@ -568,27 +568,61 @@ arrive as packages — they are copied into the base layout explicitly:
`veritysetup` and its library closure unconditionally, and the
`mke2fs`/`mkfs.ext4`/`dd`/`mke2fs.conf` plain-storage tooling alongside it.
#### CoCo guest components (monolithic confidential images)
The Go runtime still boots a **monolithic** confidential image that bakes the
CoCo guest components directly into the rootfs. Rather than compiling
guest-components on the kata side, `install_coco_guest_components` pulls the
scratch OCI **container** image guest-components publishes in the
`coco-extension-image` workflow ("Publish OCI container image" step) at
`ghcr.io/confidential-containers/guest-components/coco-extension`
(`.externals.coco-guest-components.container_image`), verifies its provenance,
and exports the filesystem into `kata-static-coco-guest-components.tar.zst`.
This is distinct from the EROFS disk image (`extension_image`) used by the
runtime-rs composable extension path. The container payload includes the
guest-component binaries, `cryptsetup`, the pause bundle, and
`etc/ocicrypt_config.json`, so confidential rootfs builds no longer need a
separate `pause-image` tarball or a local guest-components compile.
#### CoCo guest extension image
The `kata-containers-coco-extension.img` is built by:
The `kata-containers-coco-extension.img` is built and published by
[guest-components](https://github.com/confidential-containers/guest-components)
(`tools/coco-extension/`) as a multi-arch OCI index at
`ghcr.io/confidential-containers/guest-components/coco-extension-disk`. Kata pins
the guest-components revision in `versions.yaml` under
`.externals.coco-guest-components` (`version`, `container_image`, and
`extension_image` must stay in sync) and, at build time, `install_image_coco_extension`:
1. Unpacking the CoCo guest components tarball into a temporary rootfs. Besides
the agent-launched binaries (attestation-agent, attestation-agent-nv,
confidential-data-hub, api-server-rest) this tarball also carries:
- `cryptsetup` under `usr/sbin` — the encrypted-storage binary for CDH
`secure_mount` (its shared libraries are resolved against the base; see
"Runtime dependencies").
- the NVIDIA attester libraries under `usr/local/lib``libnvat.so` plus its
non-glibc transitive closure (libxml2, zlib, lzma, the C++ runtime).
2. Unpacking the pause image tarball into the same rootfs.
3. Writing the component manifest to `etc/kata-extensions/components.toml`.
4. Running the image builder with:
- `FS_TYPE=erofs` — EROFS filesystem for compact, read-only storage.
- `MEASURED_ROOTFS=yes` — creates a dm-verity hash partition.
- `SKIP_DAX_HEADER=yes` — no DAX header (virtio-blk, not NVDIMM).
- `SKIP_ROOTFS_CHECK=yes` — the extension has no `/sbin/init`.
- `BUILD_VARIANT=coco-extension` — produces a correctly named root hash file
(`root_hash_coco-extension.txt`).
1. Resolves the per-architecture manifest digest from the index
(`oras resolve --platform linux/<arch>`).
2. Verifies the provenance attestation guest-components published for that digest
(`gh attestation verify --repo confidential-containers/guest-components
--bundle-from-oci`). On amd64/arm64 CI sets
`VERIFY_COCO_EXTENSION_PROVENANCE=yes` to require it when `GH_TOKEN` is
available; locally it is skipped when no GitHub token is set. s390x always
skips because the GitHub CLI ships no s390x build.
3. Pulls that exact digest (`oras pull`) so the installed image is byte-for-byte
the artefact whose provenance was verified.
!!! note
Provenance verification runs when `GH_TOKEN` or `GITHUB_TOKEN` is available
(CI forwards the runner token automatically). Local builds without a token
skip verification with a warning. Set `VERIFY_COCO_EXTENSION_PROVENANCE=yes`
to require it, or `no` to disable it explicitly.
The guest-components build assembles a rootfs that carries the agent-launched
binaries (attestation-agent, attestation-agent-nv, confidential-data-hub,
api-server-rest), `cryptsetup` under `usr/sbin`, the NVIDIA attester libraries
under `usr/local/lib`, the pause bundle, and the component manifest at
`etc/kata-extensions/components.toml`, then formats it as:
- `FS_TYPE=erofs` — EROFS filesystem for compact, read-only storage.
- `MEASURED_ROOTFS=yes` — creates a dm-verity hash partition (except on s390x).
- `SKIP_DAX_HEADER=yes` — no DAX header (virtio-blk, not NVDIMM).
- `SKIP_ROOTFS_CHECK=yes` — the extension has no `/sbin/init`.
- `BUILD_VARIANT=coco-extension` — produces a correctly named root hash file
(`root_hash_coco-extension.txt`).
The resulting image is a two-partition disk:

View File

@@ -77,7 +77,6 @@ NVGPU_BASE_TARBALLS = \
ovmf-sev-tarball \
ovmf-tdx-tarball \
ovmf-tarball \
pause-image-tarball \
qemu-snp-experimental-tarball \
qemu-tdx-experimental-tarball \
qemu-tarball \
@@ -91,7 +90,6 @@ NVGPU_BASE_TARBALLS = \
$(PUBLISH_COMPONENT_TARBALLS) \
kernel-nvidia-gpu-tarball \
ovmf-tarball \
pause-image-tarball \
qemu-tarball \
virtiofsd-tarball \
serial-targets
@@ -254,19 +252,18 @@ DEPS := agent-tarball
rootfs-image-tarball: $(DEPS)
${MAKE} $@-build
DEPS := agent-tarball pause-image-tarball coco-guest-components-tarball kernel-tarball
DEPS := agent-tarball coco-guest-components-tarball kernel-tarball
rootfs-image-confidential-tarball: $(DEPS)
${MAKE} $@-build
DEPS := pause-image-tarball coco-guest-components-tarball
rootfs-image-coco-extension-tarball: $(DEPS)
rootfs-image-coco-extension-tarball:
${MAKE} $@-build
DEPS := agent-tarball
rootfs-image-mariner-tarball: $(DEPS)
${MAKE} $@-build
DEPS := agent-tarball pause-image-tarball coco-guest-components-tarball kernel-tarball
DEPS := agent-tarball coco-guest-components-tarball kernel-tarball
rootfs-initrd-confidential-tarball: $(DEPS)
${MAKE} $@-build
@@ -278,7 +275,7 @@ DEPS := agent-tarball busybox-tarball kernel-nvidia-gpu-tarball
rootfs-image-nvidia-gpu-tarball: $(DEPS)
${MAKE} $@-build
DEPS := agent-tarball busybox-tarball pause-image-tarball coco-guest-components-tarball kernel-nvidia-gpu-tarball
DEPS := agent-tarball busybox-tarball coco-guest-components-tarball kernel-nvidia-gpu-tarball
rootfs-image-nvidia-gpu-confidential-tarball: $(DEPS)
${MAKE} $@-build

View File

@@ -10,10 +10,11 @@ ENV TZ=Etc/UTC
COPY install_yq.sh /usr/bin/install_yq.sh
COPY install_oras.sh /usr/bin/install_oras.sh
COPY install_gh.sh /usr/bin/install_gh.sh
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Install yq, oras, and docker
# Install yq, oras, gh, and docker
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
@@ -25,7 +26,8 @@ RUN apt-get update && \
sudo && \
apt-get clean && rm -rf /var/lib/apt/lists/ && \
install_yq.sh && \
install_oras.sh
install_oras.sh && \
install_gh.sh
ARG IMG_USER=kata-builder
ARG UID=1000

View File

@@ -0,0 +1,52 @@
#!/bin/bash
#
# Copyright (c) 2026 Kata Containers contributors
#
# SPDX-License-Identifier: Apache-2.0
#
# Install the GitHub CLI, used to verify the provenance attestation of the
# guest-components CoCo extension image (see kata-deploy-binaries.sh).
set -o errexit
set -o nounset
set -o pipefail
install_dest="/usr/local/bin"
# Keep in sync with a version that ships "gh attestation verify".
gh_required_version="2.62.0"
if command -v gh &>/dev/null; then
echo "gh is already installed in the system"
exit 0
fi
arch=$(uname -m)
case "${arch}" in
x86_64) arch="amd64" ;;
aarch64) arch="arm64" ;;
s390x)
# The GitHub CLI publishes no linux/s390x build, so provenance
# verification is skipped on s390x by the caller.
echo "gh CLI has no s390x build; skipping installation"
exit 0
;;
*)
echo "Unsupported architecture for gh CLI: ${arch}"
exit 1
;;
esac
gh_tarball="gh_${gh_required_version}_linux_${arch}.tar.gz"
echo "Downloading gh ${gh_required_version}"
tmp_dir="$(mktemp -d)"
curl -fsSL -o "${tmp_dir}/${gh_tarball}" \
"https://github.com/cli/cli/releases/download/v${gh_required_version}/${gh_tarball}"
echo "Installing gh to ${install_dest}"
tar -C "${tmp_dir}" -xzf "${tmp_dir}/${gh_tarball}"
sudo install -D --mode 0755 \
"${tmp_dir}/gh_${gh_required_version}_linux_${arch}/bin/gh" \
"${install_dest}/gh"
rm -rf "${tmp_dir}"

View File

@@ -131,6 +131,8 @@ docker run \
--env REPO_URL_X86_64="${REPO_URL_X86_64}" \
--env REPO_COMPONENTS="${REPO_COMPONENTS}" \
--env AGENT_POLICY="${AGENT_POLICY}" \
--env GH_TOKEN="${GH_TOKEN:-${GITHUB_TOKEN:-}}" \
--env VERIFY_COCO_EXTENSION_PROVENANCE="${VERIFY_COCO_EXTENSION_PROVENANCE:-}" \
--env USE_DEVMAPPER="${USE_DEVMAPPER}" \
--env RUNTIME_CHOICE="${RUNTIME_CHOICE}" \
--env STATIC_RUNTIME="${STATIC_RUNTIME:-}" \

View File

@@ -29,7 +29,6 @@ readonly versions_yaml="${repo_root_dir}/versions.yaml"
readonly busybox_builder="${static_build_dir}/busybox/build.sh"
readonly agent_builder="${static_build_dir}/agent/build.sh"
readonly coco_guest_components_builder="${static_build_dir}/coco-guest-components/build.sh"
readonly clh_builder="${static_build_dir}/cloud-hypervisor/build-static-clh.sh"
readonly firecracker_builder="${static_build_dir}/firecracker/build-static-firecracker.sh"
readonly kernel_builder="${static_build_dir}/kernel/build.sh"
@@ -352,15 +351,144 @@ get_coco_guest_components_tarball_path() {
}
get_latest_coco_guest_components_artefact_and_builder_image_version() {
local coco_guest_components_version
coco_guest_components_version=$(get_from_kata_deps ".externals.coco-guest-components.version")
local coco_guest_components_toolchain
coco_guest_components_toolchain=$(get_from_kata_deps ".externals.coco-guest-components.toolchain")
local latest_coco_guest_components_artefact="${coco_guest_components_version}-${coco_guest_components_toolchain}"
local latest_coco_guest_components_builder_image
latest_coco_guest_components_builder_image="$(get_coco_guest_components_image_name)"
echo "$(get_from_kata_deps ".externals.coco-guest-components.version")-$(get_coco_extension_oci_arch)"
}
echo "${latest_coco_guest_components_artefact}-${latest_coco_guest_components_builder_image}"
get_coco_extension_oci_arch() {
arch_to_golang "$(uname -m)"
}
# Multi-arch scratch OCI container image from guest-components' "Publish OCI
# container image" step (coco-extension-image.yml). This is the assembled guest
# components rootfs as a container, not the EROFS disk image (extension_image).
get_coco_guest_components_container_image_ref() {
local version image
version="$(get_from_kata_deps ".externals.coco-guest-components.version")"
image="$(get_from_kata_deps ".externals.coco-guest-components.container_image")"
[[ -n "${version}" ]] || die "Failed to get coco-guest-components version from versions.yaml"
[[ -n "${image}" ]] || die "Failed to get coco-guest-components container_image from versions.yaml"
echo "${image}:${version}"
}
# The extension disk image is published as a multi-arch OCI index tagged with the
# guest-components commit; the per-arch selection happens at pull time.
get_coco_extension_disk_image_ref() {
local version image
version="$(get_from_kata_deps ".externals.coco-guest-components.version")"
image="$(get_from_kata_deps ".externals.coco-guest-components.extension_image")"
[[ -n "${version}" ]] || die "Failed to get coco-guest-components version from versions.yaml"
[[ -n "${image}" ]] || die "Failed to get coco-guest-components extension_image from versions.yaml"
echo "${image}:${version}"
}
# GitHub "owner/repo" that owns the provenance attestation, derived from the
# guest-components URL so it never drifts from the pinned source.
get_coco_extension_provenance_repo() {
local url
url="$(get_from_kata_deps ".externals.coco-guest-components.url")"
url="${url%/}"
echo "${url#https://github.com/}"
}
get_latest_coco_extension_artefact_version() {
echo "$(get_from_kata_deps ".externals.coco-guest-components.version")-$(get_coco_extension_oci_arch)"
}
ensure_oras_installed() {
if command -v oras &>/dev/null; then
return 0
fi
local install_oras_script="${repo_root_dir}/tools/packaging/kata-deploy/local-build/dockerbuild/install_oras.sh"
[[ -f "${install_oras_script}" ]] || die "oras is required to pull the CoCo extension image"
"${install_oras_script}"
}
ensure_gh_installed() {
if command -v gh &>/dev/null; then
return 0
fi
local install_gh_script="${repo_root_dir}/tools/packaging/kata-deploy/local-build/dockerbuild/install_gh.sh"
[[ -f "${install_gh_script}" ]] || return 1
"${install_gh_script}" || return 1
command -v gh &>/dev/null
}
# Verify the guest-components provenance attestation bound to a per-arch OCI
# manifest digest (container or disk image).
#
# Verification policy:
# VERIFY_COCO_EXTENSION_PROVENANCE=no — always skip
# VERIFY_COCO_EXTENSION_PROVENANCE=yes — always verify (CI); fail if gh/token missing
# unset (default) — verify when GH_TOKEN/GITHUB_TOKEN is set,
# otherwise skip with a warning (local builds)
verify_guest_components_oci_provenance() {
local image="$1"
local digest="$2"
local token="${GH_TOKEN:-${GITHUB_TOKEN:-}}"
if [[ "${VERIFY_COCO_EXTENSION_PROVENANCE:-}" == "no" ]]; then
warn "guest-components provenance verification disabled (VERIFY_COCO_EXTENSION_PROVENANCE=no)"
return 0
fi
# The GitHub CLI has no s390x build; provenance cannot be verified there even
# when CI sets VERIFY_COCO_EXTENSION_PROVENANCE=yes.
if [[ "$(uname -m)" == "s390x" ]]; then
warn "skipping guest-components provenance verification on s390x for ${image}@${digest}"
return 0
fi
if [[ -z "${token}" ]]; then
if [[ "${VERIFY_COCO_EXTENSION_PROVENANCE:-}" == "yes" ]]; then
die "VERIFY_COCO_EXTENSION_PROVENANCE=yes requires GH_TOKEN or GITHUB_TOKEN"
fi
warn "No GH_TOKEN/GITHUB_TOKEN set; skipping guest-components provenance verification for ${image}@${digest}"
return 0
fi
if ! ensure_gh_installed; then
die "gh CLI is required to verify guest-components provenance (set VERIFY_COCO_EXTENSION_PROVENANCE=no to bypass)"
fi
local repo
repo="$(get_coco_extension_provenance_repo)"
info "Verifying provenance of ${image}@${digest} against ${repo}"
export GH_TOKEN="${token}"
# --bundle-from-oci reads the attestation guest-components pushed alongside the
# image (actions/attest push-to-registry) instead of the GitHub API.
gh attestation verify "oci://${image}@${digest}" \
--repo "${repo}" \
--bundle-from-oci \
|| die "Provenance verification failed for ${image}@${digest}"
}
# Pull the published scratch OCI container image (coco-extension), verify its
# provenance, and export the rootfs into destdir for monolithic confidential images.
install_coco_guest_components_from_oci() {
ensure_oras_installed
local image_ref image go_arch digest cid
image_ref="$(get_coco_guest_components_container_image_ref)"
image="${image_ref%%:*}"
go_arch="$(get_coco_extension_oci_arch)"
digest="$(oras resolve --platform "linux/${go_arch}" "${image_ref}")" \
|| die "Failed to resolve ${image_ref} for linux/${go_arch}; bump .externals.coco-guest-components.version in versions.yaml once guest-components has published the image"
verify_guest_components_oci_provenance "${image}" "${digest}"
info "Pull and extract CoCo guest components from ${image}@${digest} (linux/${go_arch})"
docker pull "${image}@${digest}"
# coco-extension is FROM scratch with no CMD/ENTRYPOINT; docker create needs
# an executable that exists in the image rootfs (we never run the container).
cid="$(docker create "${image}@${digest}" /usr/local/bin/attestation-agent --help)"
docker export "${cid}" | tar -xf - -C "${destdir}"
docker rm -f "${cid}" >/dev/null
}
get_pause_image_tarball_path() {
@@ -481,10 +609,8 @@ install_image() {
fi
# Both the standard and NVIDIA confidential images bake the CoCo
# guest components + pause image into the rootfs, so factor them
# into the cache key.
# guest components (including the pause bundle) into the rootfs.
latest_artefact+="-$(get_latest_coco_guest_components_artefact_and_builder_image_version)"
latest_artefact+="-$(get_latest_pause_image_artefact_and_builder_image_version)"
fi
if [[ "${variant}" == "nvidia-gpu" || "${variant}" == "nvidia-gpu-extension" ]]; then
@@ -534,15 +660,13 @@ install_image() {
if [[ -n "${variant}" ]]; then
# Both the standard confidential image and the NVIDIA confidential
# image bake the CoCo guest components + pause image into the
# rootfs, so each stays a usable standalone monolithic CoCo image.
# The runtime-rs split path instead ships these in the separate
# image bake the CoCo guest components (including the pause bundle)
# into the rootfs, so each stays a usable standalone monolithic CoCo image.
# The runtime-rs split path instead ships these in the separately published
# CoCo extension image (rootfs-image-coco-extension).
if [[ "${variant}" == *confidential ]]; then
COCO_GUEST_COMPONENTS_TARBALL="$(get_coco_guest_components_tarball_path)"
export COCO_GUEST_COMPONENTS_TARBALL
PAUSE_IMAGE_TARBALL="$(get_pause_image_tarball_path)"
export PAUSE_IMAGE_TARBALL
fi
fi
@@ -611,15 +735,15 @@ install_image_confidential() {
}
#Install CoCo extension image (erofs+verity, contains CoCo guest components + pause)
#
# The disk image is built and published by guest-components
# (ghcr.io/confidential-containers/guest-components/coco-extension-disk) as a
# multi-arch OCI index. Kata resolves the per-arch manifest digest, verifies its
# provenance attestation, and pulls exactly that digest into kata-static.
install_image_coco_extension() {
local component="rootfs-image-coco-extension"
local coco_last_commit
coco_last_commit="$(get_latest_coco_guest_components_artefact_and_builder_image_version)"
local pause_last_commit
pause_last_commit="$(get_latest_pause_image_artefact_and_builder_image_version)"
latest_artefact="$(get_kata_version)-coco-extension-${coco_last_commit}-${pause_last_commit}"
latest_artefact="$(get_kata_version)-coco-extension-$(get_latest_coco_extension_artefact_version)"
latest_builder_image=""
install_cached_tarball_component \
@@ -630,114 +754,50 @@ install_image_coco_extension() {
"${final_tarball_path}" \
&& return 0
info "Create CoCo extension image"
ensure_oras_installed
# Use a temp dir under the repo root so the path is valid both inside
# the outer build-kata-deploy container and in the nested image-builder
# container (Docker-in-Docker mounts use host paths).
local extension_rootfs
extension_rootfs="$(mktemp -d "${repo_root_dir}/.coco-extension-rootfs.XXXX")"
local disk_image_ref image go_arch
disk_image_ref="$(get_coco_extension_disk_image_ref)"
image="${disk_image_ref%%:*}"
go_arch="$(get_coco_extension_oci_arch)"
COCO_GUEST_COMPONENTS_TARBALL="$(get_coco_guest_components_tarball_path)"
PAUSE_IMAGE_TARBALL="$(get_pause_image_tarball_path)"
# Resolve the per-arch manifest digest out of the multi-arch index. This is the
# subject guest-components' provenance attestation is bound to, so we verify it
# and then pull that exact digest (avoiding a resolve/pull TOCTOU window).
local digest
digest="$(oras resolve --platform "linux/${go_arch}" "${disk_image_ref}")" \
|| die "Failed to resolve ${disk_image_ref} for linux/${go_arch}; bump .externals.coco-guest-components.version in versions.yaml once guest-components has published the image"
info "Unpacking CoCo guest components into extension rootfs"
tar --zstd -xvf "${COCO_GUEST_COMPONENTS_TARBALL}" -C "${extension_rootfs}"
verify_guest_components_oci_provenance "${image}" "${digest}"
info "Unpacking pause image into extension rootfs"
tar --zstd -xvf "${PAUSE_IMAGE_TARBALL}" -C "${extension_rootfs}"
info "Pull CoCo extension disk image from ${image}@${digest} (linux/${go_arch})"
# Data-driven extension manifest consumed by kata-agent. It describes the
# components shipped in this extension so the agent needs no per-bundle code
# changes. All paths are relative to the extension mount point
# (/run/kata-extensions/coco). The "${var}" tokens in the [[process]] entries are
# substituted by kata-agent from its runtime context.
info "Writing CoCo extension component manifest"
local manifest_dir="${extension_rootfs}/etc/kata-extensions"
mkdir -p "${manifest_dir}"
cat > "${manifest_dir}/components.toml" <<'EOF'
schema_version = 1
local pull_dir
pull_dir="$(mktemp -d)"
pushd "${pull_dir}" >/dev/null
if ! oras pull "${image}@${digest}" --no-tty; then
popd >/dev/null
rm -rf "${pull_dir}"
die "Failed to pull CoCo extension image ${image}@${digest}"
fi
[paths]
"ocicrypt-config" = "etc/ocicrypt_config.json"
"pause-bundle" = "pause_bundle"
[[process]]
id = "attestation-agent"
level = 1
args = ["--attestation_sock", "${aa_attestation_uri}"]
optional_args = [{ when = "initdata_toml_path", args = ["--initdata-toml", "${initdata_toml_path}"] }]
config = "${aa_config_path}"
wait_socket = "${aa_attestation_socket}"
timeout_secs = "${launch_process_timeout}"
# The extension ships both the stock attestation-agent and the NVIDIA-attester
# build; the consumer selects one via the "attester_variant" context value
# (kata-agent uses "default", NVRC uses "nvidia").
select = "${attester_variant}"
[process.variants.default]
path = "usr/local/bin/attestation-agent"
[process.variants.nvidia]
path = "usr/local/bin/attestation-agent-nv"
# attestation-agent-nv links libnvat.so (bundled in this CoCo extension under
# usr/local/lib), which dlopens libnvidia-ml.so.1 for GPU attestation evidence.
# Only NVAT's own libs need LD_LIBRARY_PATH: NVML lives in the GPU extension,
# which NVRC folds into the guest loader cache before starting kata-agent, so it
# resolves without a path here (see NVRC gpu::setup).
env = { LD_LIBRARY_PATH = "${extension_root}/usr/local/lib" }
[[process]]
id = "confidential-data-hub"
level = 2
path = "usr/local/bin/confidential-data-hub"
config = "${cdh_config_path}"
# CDH's secure_mount shells out (by PATH lookup) to cryptsetup for encrypted
# storage and to mke2fs/mkfs.ext4/dd for the filesystem. cryptsetup is CoCo-only
# and ships in this extension under usr/sbin (see
# build-static-coco-guest-components.sh); the plain mkfs/dd tooling lives in the
# nvidia base image's /sbin and /bin. The agent launches CDH with
# PATH=/bin:/sbin:/usr/bin:/usr/sbin, but setting any env here overrides it
# wholesale, so prepend the extension's usr/sbin and restore the base dirs.
env = { OCICRYPT_KEYPROVIDER_CONFIG = "${ocicrypt_config_path}", PATH = "${extension_root}/usr/sbin:/bin:/sbin:/usr/bin:/usr/sbin" }
wait_socket = "${cdh_socket}"
timeout_secs = "${launch_process_timeout}"
[[process]]
id = "api-server-rest"
level = 3
path = "usr/local/bin/api-server-rest"
args = ["--features", "${rest_api_features}"]
timeout_secs = "0"
EOF
[[ -f kata-containers-coco-extension.img ]] || die "CoCo extension pull did not contain kata-containers-coco-extension.img"
local install_dir="${destdir}/${prefix}/share/kata-containers/"
mkdir -p "${install_dir}"
install -D --mode 0644 kata-containers-coco-extension.img \
"${install_dir}/kata-containers-coco-extension.img"
local image_builder="${repo_root_dir}/tools/osbuilder/image-builder/image_builder.sh"
export USE_DOCKER="1"
export BUILD_VARIANT="coco-extension"
export FS_TYPE="erofs"
# Mirror the base/confidential images: s390x does not use a measured rootfs
# (Secure Execution measures the guest through a different mechanism), so the
# extension carries no dm-verity hash there and is mounted off its raw
# partition instead.
if [[ "${ARCH}" == "s390x" ]]; then
export MEASURED_ROOTFS="no"
else
export MEASURED_ROOTFS="yes"
fi
export SKIP_DAX_HEADER="yes"
export SKIP_ROOTFS_CHECK="yes"
"${image_builder}" -o "${install_dir}/kata-containers-coco-extension.img" "${extension_rootfs}"
if [[ -e "${install_dir}/root_hash_coco-extension.txt" ]]; then
if [[ -f root_hash_coco-extension.txt ]]; then
install -D --mode 0644 root_hash_coco-extension.txt \
"${install_dir}/root_hash_coco-extension.txt"
info "Root hash file: ${install_dir}/root_hash_coco-extension.txt"
elif [[ "${ARCH}" != "s390x" ]]; then
die "Expected root_hash_coco-extension.txt in ${disk_image_ref}"
fi
rm -rf "${extension_rootfs}"
popd >/dev/null
rm -rf "${pull_dir}"
}
#Install cbl-mariner guest image
@@ -792,7 +852,6 @@ install_initrd() {
latest_artefact+="-$(get_latest_kernel_artefact_and_builder_image_version)"
fi
latest_artefact+="-$(get_latest_coco_guest_components_artefact_and_builder_image_version)"
latest_artefact+="-$(get_latest_pause_image_artefact_and_builder_image_version)"
fi
if [[ "${variant}" == "nvidia-gpu" ]]; then
@@ -819,8 +878,6 @@ install_initrd() {
if [[ "${variant}" == *confidential ]]; then
COCO_GUEST_COMPONENTS_TARBALL="$(get_coco_guest_components_tarball_path)"
export COCO_GUEST_COMPONENTS_TARBALL
PAUSE_IMAGE_TARBALL="$(get_pause_image_tarball_path)"
export PAUSE_IMAGE_TARBALL
fi
else
# Vanilla initrd uses kata-agent as /sbin/init (no systemd).
@@ -1445,9 +1502,9 @@ install_agent() {
}
install_coco_guest_components() {
latest_artefact="$(get_from_kata_deps ".externals.coco-guest-components.version")-$(get_from_kata_deps ".externals.coco-guest-components.toolchain")"
latest_artefact="$(get_latest_coco_guest_components_artefact_and_builder_image_version)"
artefact_tag="$(get_from_kata_deps ".externals.coco-guest-components.version")"
latest_builder_image="$(get_coco_guest_components_image_name)"
latest_builder_image=""
install_cached_tarball_component \
"${build_target}" \
@@ -1457,8 +1514,8 @@ install_coco_guest_components() {
"${final_tarball_path}" \
&& return 0
info "build static coco-guest-components"
DESTDIR="${destdir}" "${coco_guest_components_builder}"
info "Pull published CoCo guest components OCI container image"
install_coco_guest_components_from_oci
}
install_pause_image() {

View File

@@ -272,8 +272,15 @@ externals:
coco-guest-components:
description: "Provides attested key unwrapping for image decryption"
url: "https://github.com/confidential-containers/guest-components/"
version: "da8d93f2797088a5f0636c8c1eeb31da73784fe8"
toolchain: "1.90.0"
version: "fff3a966b7b1d1654d172a7cd81c0f563721ff0b"
# Scratch OCI container image (guest-components "Publish OCI container image"
# step). Consumed by monolithic confidential rootfs builds.
# yamllint disable-line rule:line-length
container_image: "ghcr.io/confidential-containers/guest-components/coco-extension"
# EROFS + dm-verity disk image (guest-components "Publish disk image with ORAS"
# step). Consumed by the runtime-rs composable extension path.
# yamllint disable-line rule:line-length
extension_image: "ghcr.io/confidential-containers/guest-components/coco-extension-disk"
coco-trustee:
description: "Provides attestation and secret delivery components"