mirror of
https://github.com/kata-containers/kata-containers.git
synced 2026-07-25 06:00:18 +00:00
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:
committed by
Mikko Ylinen
parent
e49ad473bb
commit
cd75c2fac5
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
52
tools/packaging/kata-deploy/local-build/dockerbuild/install_gh.sh
Executable file
52
tools/packaging/kata-deploy/local-build/dockerbuild/install_gh.sh
Executable 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}"
|
||||
@@ -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:-}" \
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user