mirror of
https://github.com/kata-containers/kata-containers.git
synced 2026-07-07 19:06:16 +00:00
agent: add systemd template unit for extension image mount/unmount
Add a systemd template unit kata-extension-mount@.service and companion helper scripts (kata-extension-mount.sh, kata-extension-umount.sh) that handle the guest-side setup of extension block device images. The mount script: - Discovers the block device by matching its virtio serial (extension-<name>) - Reads verity parameters from kata.extension.<name>.verity_params on the kernel command line - Creates a dm-verity device and mounts the EROFS filesystem read-only at /run/kata-extensions/<name>/ The unit uses ConditionKernelCommandLine=kata.extension.%i.verity_params so it only activates when that extension is configured for the VM, and is ordered before kata-agent.service so extension contents are available when the agent starts. A systemd generator (kata-extension-mount-generator) instantiates the template for every kata.extension.<name>.verity_params entry on the kernel command line. Because the runtime emits one such entry per configured guest_extension_images, extensions are enabled purely from the cmdline and adding a new one needs no change to the rootfs build. The agent Makefile is updated to install the unit, scripts and generator. Signed-off-by: Fabiano Fidêncio <ffidencio@nvidia.com> Assisted-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
Fabiano Fidêncio
parent
968d6e9431
commit
e85a19a8a6
@@ -76,6 +76,8 @@ INIT := no
|
||||
|
||||
# Path to systemd unit directory if installed as not init.
|
||||
UNIT_DIR := /usr/lib/systemd/system
|
||||
# Path to systemd system generator directory.
|
||||
GENERATOR_DIR := /usr/lib/systemd/system-generators
|
||||
|
||||
GENERATED_CODE = src/version.rs
|
||||
|
||||
@@ -100,6 +102,13 @@ ifeq ($(INIT),no)
|
||||
GENERATED_FILES += $(UNIT_FILES)
|
||||
# Target to be reached in systemd services
|
||||
UNIT_FILES += kata-containers.target
|
||||
# Template unit for mounting extension block devices (CoCo, GPU, etc.)
|
||||
UNIT_FILES += kata-extension-mount@.service
|
||||
# Helper scripts for extension mount/umount
|
||||
EXTENSION_SCRIPTS = kata-extension-mount.sh kata-extension-umount.sh
|
||||
# systemd generator that enables the mount template per extension declared
|
||||
# on the kernel command line, so new extensions need no rootfs build change
|
||||
EXTENSION_GENERATOR = kata-extension-mount-generator.sh
|
||||
endif
|
||||
|
||||
# Display name of command and it's version (or a message if not available).
|
||||
@@ -163,6 +172,10 @@ install-services: $(GENERATED_FILES)
|
||||
ifeq ($(INIT),no)
|
||||
@echo "Installing systemd unit files..."
|
||||
$(foreach f,$(UNIT_FILES),$(call INSTALL_FILE,$f,$(UNIT_DIR)))
|
||||
@echo "Installing extension helper scripts..."
|
||||
$(foreach f,$(EXTENSION_SCRIPTS),install -D -m 755 $f $(DESTDIR)/usr/libexec/$f || exit 1;)
|
||||
@echo "Installing extension mount generator..."
|
||||
@install -D -m 755 $(EXTENSION_GENERATOR) $(DESTDIR)$(GENERATOR_DIR)/kata-extension-mount-generator
|
||||
endif
|
||||
|
||||
show-header:
|
||||
|
||||
59
src/agent/kata-extension-mount-generator.sh
Executable file
59
src/agent/kata-extension-mount-generator.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2026 NVIDIA Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# systemd system generator that instantiates the kata-extension-mount@.service
|
||||
# template for every guest extension image declared on the kernel command line.
|
||||
#
|
||||
# The runtime emits one "kata.extension.<name>.verity_params=..." entry per
|
||||
# configured guest_extension_images, so this generator enables exactly the
|
||||
# extensions the VM is configured with -- without the rootfs build needing to
|
||||
# know any extension name in advance. Adding a new extension therefore requires
|
||||
# no change to the image build: it is wired up purely from the kernel cmdline.
|
||||
#
|
||||
# systemd invokes generators with three directory arguments; $1 is the "normal"
|
||||
# output directory, which is where dependency symlinks belong.
|
||||
|
||||
set -u
|
||||
|
||||
normal_dir="${1:-/tmp}"
|
||||
unit="/usr/lib/systemd/system/kata-extension-mount@.service"
|
||||
target="kata-containers.target"
|
||||
wants_dir="${normal_dir}/${target}.wants"
|
||||
|
||||
# /proc/cmdline is a single line; read it defensively so a missing /proc does
|
||||
# not abort the generator (which would only delay the boot, never fix it).
|
||||
read -r cmdline < /proc/cmdline || exit 0
|
||||
|
||||
for param in ${cmdline}; do
|
||||
# An extension may carry verity params ("...verity_params=<...>") or none
|
||||
# at all -- an unmeasured extension (e.g. on s390x) renders as a bare
|
||||
# "...verity_params" with no value. Match both forms so the extension is
|
||||
# activated either way; the mount helper decides verity vs. raw from the
|
||||
# parameter value.
|
||||
case "${param}" in
|
||||
kata.extension.*.verity_params=*)
|
||||
name="${param#kata.extension.}"
|
||||
name="${name%%.verity_params=*}"
|
||||
;;
|
||||
kata.extension.*.verity_params)
|
||||
name="${param#kata.extension.}"
|
||||
name="${name%.verity_params}"
|
||||
;;
|
||||
*)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
# Only accept names safe to use as a systemd instance and as a filename.
|
||||
# A stray space or '=' would mean a malformed cmdline entry; skip it rather
|
||||
# than create a bogus unit instance.
|
||||
[[ "${name}" =~ ^[a-zA-Z0-9_-]+$ ]] || continue
|
||||
|
||||
mkdir -p "${wants_dir}"
|
||||
ln -sf "${unit}" "${wants_dir}/kata-extension-mount@${name}.service"
|
||||
done
|
||||
|
||||
exit 0
|
||||
141
src/agent/kata-extension-mount.sh
Executable file
141
src/agent/kata-extension-mount.sh
Executable file
@@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2026 NVIDIA Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Mount a kata extension block device with optional dm-verity verification.
|
||||
# Usage: kata-extension-mount.sh <extension-name>
|
||||
#
|
||||
# The block device is discovered by scanning /sys/block/*/serial for
|
||||
# a device whose serial matches "extension-<name>".
|
||||
# Verity params are read from kernel cmdline: kata.extension.<name>.verity_params=...
|
||||
# The extension is mounted read-only (erofs) at /run/kata-extensions/<name>/.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
EXTENSION_NAME="${1:?extension name required}"
|
||||
SERIAL="extension-${EXTENSION_NAME}"
|
||||
MOUNT_DIR="/run/kata-extensions/${EXTENSION_NAME}"
|
||||
|
||||
find_block_dev_by_serial() {
|
||||
local wanted="$1"
|
||||
for s in /sys/block/*/serial; do
|
||||
[[ -f "${s}" ]] || continue
|
||||
local cur
|
||||
cur="$(cat "${s}" 2>/dev/null)" || continue
|
||||
if [[ "${cur}" == "${wanted}" ]]; then
|
||||
echo "/dev/$(basename "$(dirname "${s}")")"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
REAL_DEV="$(find_block_dev_by_serial "${SERIAL}")" || {
|
||||
echo "ERROR: no block device with serial ${SERIAL} found" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
get_verity_param() {
|
||||
local key="kata.extension.${EXTENSION_NAME}.verity_params"
|
||||
local cmdline
|
||||
cmdline="$(cat /proc/cmdline)"
|
||||
|
||||
local value=""
|
||||
for param in ${cmdline}; do
|
||||
case "${param}" in
|
||||
"${key}="*)
|
||||
value="${param#"${key}="}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
echo "${value}"
|
||||
}
|
||||
|
||||
parse_verity_field() {
|
||||
local params="$1"
|
||||
local field="$2"
|
||||
# Iterate without a pipeline: a `| while` loop runs in a subshell, so a
|
||||
# `return` there would only exit the subshell, never this function.
|
||||
local IFS=','
|
||||
for pair in ${params}; do
|
||||
local k="${pair%%=*}"
|
||||
local v="${pair#*=}"
|
||||
if [[ "${k}" == "${field}" ]]; then
|
||||
echo "${v}"
|
||||
return
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
VERITY_PARAMS="$(get_verity_param)"
|
||||
|
||||
PART_SEP=""
|
||||
[[ "${REAL_DEV}" =~ [0-9]$ ]] && PART_SEP="p"
|
||||
DATA_DEV="${REAL_DEV}${PART_SEP}1"
|
||||
HASH_DEV="${REAL_DEV}${PART_SEP}2"
|
||||
|
||||
# The image build encodes its integrity policy in the on-disk layout, and that
|
||||
# layout -- not the kernel command line -- is the source of truth for whether
|
||||
# this extension must be verified: a measured extension (MEASURED_ROOTFS=yes)
|
||||
# is built with a dm-verity hash partition (p2) next to the data partition (p1);
|
||||
# an unmeasured extension (MEASURED_ROOTFS=no, e.g. on s390x, where Secure
|
||||
# Execution protects the guest through a different mechanism) has only p1.
|
||||
#
|
||||
# We cross-check that layout against the verity params on the kernel command
|
||||
# line (which, in a confidential guest, is part of the measured/attested boot)
|
||||
# so we fail closed instead of silently downgrading a measured extension to an
|
||||
# unverified mount:
|
||||
#
|
||||
# hash device + params -> verify (normal measured extension)
|
||||
# hash device + NO params -> refuse: verity was stripped/disabled (tamper)
|
||||
# NO hash device + params -> refuse: params but nothing to verify (mismatch)
|
||||
# NO hash device + NO params -> raw mount (genuinely unmeasured extension)
|
||||
#
|
||||
# See "Integrity policy: measured vs. unmeasured, and failing closed" in
|
||||
# docs/design/proposals/composable-vm-images.md for the rationale.
|
||||
if [[ -b "${HASH_DEV}" ]]; then
|
||||
if [[ -z "${VERITY_PARAMS}" ]]; then
|
||||
echo "ERROR: extension ${EXTENSION_NAME} ships a dm-verity hash device but no verity params were provided on the kernel command line; refusing to mount it unverified" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ROOT_HASH="$(parse_verity_field "${VERITY_PARAMS}" "root_hash")"
|
||||
SALT="$(parse_verity_field "${VERITY_PARAMS}" "salt")"
|
||||
DATA_BLOCKS="$(parse_verity_field "${VERITY_PARAMS}" "data_blocks")"
|
||||
HASH_BLOCK_SIZE="$(parse_verity_field "${VERITY_PARAMS}" "hash_block_size")"
|
||||
DATA_BLOCK_SIZE="$(parse_verity_field "${VERITY_PARAMS}" "data_block_size")"
|
||||
|
||||
if [[ -z "${ROOT_HASH}" ]]; then
|
||||
echo "ERROR: extension ${EXTENSION_NAME} verity params carry no root_hash; refusing to mount" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DM_NAME="extension-${EXTENSION_NAME}"
|
||||
|
||||
veritysetup open "${DATA_DEV}" "${DM_NAME}" "${HASH_DEV}" "${ROOT_HASH}" \
|
||||
--no-superblock \
|
||||
--hash-block-size="${HASH_BLOCK_SIZE:-4096}" \
|
||||
--data-block-size="${DATA_BLOCK_SIZE:-4096}" \
|
||||
--data-blocks="${DATA_BLOCKS}" \
|
||||
--salt="${SALT}"
|
||||
|
||||
MOUNT_SRC="/dev/mapper/${DM_NAME}"
|
||||
else
|
||||
if [[ -n "${VERITY_PARAMS}" ]]; then
|
||||
echo "ERROR: extension ${EXTENSION_NAME} has verity params on the kernel command line but no dm-verity hash device; refusing to mount" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -b "${DATA_DEV}" ]]; then
|
||||
MOUNT_SRC="${DATA_DEV}"
|
||||
else
|
||||
MOUNT_SRC="${REAL_DEV}"
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "${MOUNT_DIR}"
|
||||
mount -t erofs -o ro "${MOUNT_SRC}" "${MOUNT_DIR}"
|
||||
|
||||
echo "Extension ${EXTENSION_NAME} mounted at ${MOUNT_DIR}"
|
||||
22
src/agent/kata-extension-mount@.service
Normal file
22
src/agent/kata-extension-mount@.service
Normal file
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# Copyright (c) 2026 NVIDIA Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
[Unit]
|
||||
Description=Mount Kata extension image %i
|
||||
DefaultDependencies=no
|
||||
Before=kata-agent.service
|
||||
After=local-fs-pre.target
|
||||
ConditionKernelCommandLine=kata.extension.%i.verity_params
|
||||
OnFailure=poweroff.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/libexec/kata-extension-mount.sh %i
|
||||
ExecStop=/usr/libexec/kata-extension-umount.sh %i
|
||||
|
||||
[Install]
|
||||
WantedBy=kata-containers.target
|
||||
30
src/agent/kata-extension-umount.sh
Executable file
30
src/agent/kata-extension-umount.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2026 NVIDIA Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Unmount a kata extension block device.
|
||||
# Usage: kata-extension-umount.sh <extension-name>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
EXTENSION_NAME="${1:?extension name required}"
|
||||
MOUNT_DIR="/run/kata-extensions/${EXTENSION_NAME}"
|
||||
DM_NAME="extension-${EXTENSION_NAME}"
|
||||
|
||||
# The extension is consumed in place (the agent reads binaries/config straight from
|
||||
# ${MOUNT_DIR} via the component manifest), so there are no bind mounts to undo
|
||||
# here -- we only need to unmount the extension filesystem and close dm-verity.
|
||||
|
||||
# Unmount the extension filesystem
|
||||
if mountpoint -q "${MOUNT_DIR}" 2>/dev/null; then
|
||||
umount "${MOUNT_DIR}"
|
||||
fi
|
||||
|
||||
# Close dm-verity device if present
|
||||
if [[ -e "/dev/mapper/${DM_NAME}" ]]; then
|
||||
veritysetup close "${DM_NAME}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "Extension ${EXTENSION_NAME} unmounted"
|
||||
Reference in New Issue
Block a user