mirror of
https://github.com/kata-containers/kata-containers.git
synced 2026-07-26 15:55:24 +00:00
Add a self-contained "devkit" guest extension: a minimal Ubuntu (glibc + busybox + apt) rootfs with common debug tools prebaked (strace, ltrace, iproute2, procps, lsof, tcpdump, pciutils, util-linux, ...), built as a measured erofs+dm-verity image mounted at /run/kata-extensions/devkit. The production guest rootfs is minimal (and, for some bases, shell-less), so the agent debug console has no rich interactive shell. Rather than rebuilding the whole rootfs with debug tooling, this optional extension can be cold-plugged alongside any base image. At runtime the guest helper scripts overlay a writable tmpfs on the read-only extension and chroot in, so apt and every tool run natively against a normal root filesystem; `apt install <pkg>` inside the debug shell pulls anything else into the overlay on demand. busybox-static (/usr/bin/busybox.static) bootstraps the overlay/chroot from the shell-less base (the guest's dynamic loader is not present there yet); it is installed at a dedicated path so it never clobbers the extension's own busybox or /bin/sh, which are needed unclobbered inside the chroot. The rootfs is built through osbuilder (rootfs.sh, with a ROOTFS_ONLY mode that skips the agent/systemd setup) using mmdebstrap, the same tool the base guest rootfs uses, so the devkit needs no bespoke chroot or docker-export logic. The resulting tree is handed to image_builder.sh (mirroring the CoCo extension): it ships no kata-agent, kernel or driver userspace. No runtime wiring is added here; that follows in later commits. Nothing consumes the image yet. Signed-off-by: Fabiano Fidêncio <ffidencio@nvidia.com> Assisted-by: Cursor <cursoragent@cursor.com>
117 lines
4.2 KiB
Bash
117 lines
4.2 KiB
Bash
#!/run/kata-extensions/devkit/bin/busybox.static sh
|
|
# shellcheck shell=dash
|
|
#
|
|
# Copyright (c) Kata Containers Community
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
# Shared devkit guest runtime library, sourced by devkit-enter.
|
|
#
|
|
# The devkit extension (read-only at ${DEVKIT}) is a full minimal Ubuntu rootfs.
|
|
# We overlay a writable, exec-enabled tmpfs on it and chroot in, so apt and the
|
|
# prebaked tools run natively against a normal root filesystem.
|
|
#
|
|
# BB is a statically-linked busybox (busybox-static, /bin/busybox.static), the
|
|
# ONLY binary we can exec on the shell-less guest base before the glibc dynamic
|
|
# loader is reachable there. It is installed at a dedicated path so it never
|
|
# clobbers the rootfs's own /bin/busybox or GNU coreutils.
|
|
|
|
DEVKIT_INIT_VERSION=1
|
|
|
|
DEVKIT="${DEVKIT:-/run/kata-extensions/devkit}"
|
|
WRITABLE="${WRITABLE:-/run/kata-devkit-writable}"
|
|
BB="${BB:-${DEVKIT}/bin/busybox.static}"
|
|
|
|
UPPER="${WRITABLE}/upper"
|
|
WORK="${WRITABLE}/work"
|
|
MERGED="${WRITABLE}/merged"
|
|
|
|
devkit_is_mounted() {
|
|
"${BB}" grep -q "[[:space:]]${1}[[:space:]]" /proc/mounts 2>/dev/null
|
|
}
|
|
|
|
# Mount an exec-enabled tmpfs at ${WRITABLE}: /run is typically noexec, so the
|
|
# overlay upper/work (and the fallback rootfs copy) must live on our own tmpfs
|
|
# for the prebaked and apt-installed binaries to exec.
|
|
devkit_mount_writable() {
|
|
"${BB}" mkdir -p "${WRITABLE}"
|
|
devkit_is_mounted "${WRITABLE}" && return 0
|
|
"${BB}" mount -t tmpfs -o mode=755,size=2G tmpfs "${WRITABLE}"
|
|
}
|
|
|
|
# Overlay (devkit ro lower + tmpfs rw upper), falling back to a plain copy of the
|
|
# rootfs into the tmpfs if this kernel refuses an overlay mount.
|
|
devkit_mount_root() {
|
|
"${BB}" mkdir -p "${UPPER}" "${WORK}" "${MERGED}"
|
|
devkit_is_mounted "${MERGED}" && return 0
|
|
"${BB}" test -f "${WRITABLE}/.copied" && return 0
|
|
|
|
local ovl_err
|
|
if ovl_err="$("${BB}" mount -t overlay overlay "${MERGED}" \
|
|
-o "lowerdir=${DEVKIT},upperdir=${UPPER},workdir=${WORK}" 2>&1)" \
|
|
&& devkit_is_mounted "${MERGED}"; then
|
|
return 0
|
|
fi
|
|
|
|
# Fallback: overlay unavailable on this kernel - copy the rootfs into tmpfs.
|
|
"${BB}" echo "devkit: overlay mount unavailable (${ovl_err:-unknown}), copying rootfs into tmpfs" >&2
|
|
"${BB}" cp -a "${DEVKIT}/." "${MERGED}/"
|
|
"${BB}" touch "${WRITABLE}/.copied"
|
|
}
|
|
|
|
# Bind the kernel virtual filesystems so apt and the debug tools behave; /dev is
|
|
# rbind'd to bring in pts/shm for interactive shells.
|
|
devkit_bind_mounts() {
|
|
"${BB}" mkdir -p "${MERGED}/proc" "${MERGED}/sys" "${MERGED}/dev"
|
|
|
|
devkit_is_mounted "${MERGED}/proc" || "${BB}" mount -t proc proc "${MERGED}/proc"
|
|
devkit_is_mounted "${MERGED}/sys" || "${BB}" mount -t sysfs sysfs "${MERGED}/sys"
|
|
|
|
if ! devkit_is_mounted "${MERGED}/dev"; then
|
|
if ! "${BB}" mount -o rbind /dev "${MERGED}/dev" 2>/dev/null; then
|
|
"${BB}" mount -t devtmpfs devtmpfs "${MERGED}/dev" 2>/dev/null \
|
|
|| "${BB}" mount -t tmpfs tmpfs "${MERGED}/dev"
|
|
"${BB}" mkdir -p "${MERGED}/dev/pts"
|
|
"${BB}" mount -t devpts devpts "${MERGED}/dev/pts" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Give apt/curl working DNS by importing the guest resolver config.
|
|
devkit_seed_resolv_conf() {
|
|
"${BB}" test -e /etc/resolv.conf || return 0
|
|
"${BB}" mkdir -p "${MERGED}/etc"
|
|
"${BB}" cp -L /etc/resolv.conf "${MERGED}/etc/resolv.conf" 2>/dev/null || true
|
|
}
|
|
|
|
# Expose the guest's real root as /real_root so container rootfses and other
|
|
# guest state are reachable from the debug shell (e.g.
|
|
# /real_root/run/kata-containers/<id>/rootfs).
|
|
#
|
|
# A symlink to /proc/1/root, not a bind mount: proc is mounted in the chroot, so
|
|
# the kernel resolves it to PID 1's root regardless of the chroot, with no
|
|
# bind-mount recursion or teardown ordering to worry about.
|
|
devkit_link_real_root() {
|
|
"${BB}" test -e "${MERGED}/real_root" && return 0
|
|
"${BB}" ln -s /proc/1/root "${MERGED}/real_root" 2>/dev/null || true
|
|
}
|
|
|
|
# Idempotent: safe to call from every devkit-* invocation.
|
|
devkit_setup_chroot() {
|
|
devkit_mount_writable || return 1
|
|
devkit_mount_root || return 1
|
|
devkit_bind_mounts || return 1
|
|
devkit_seed_resolv_conf
|
|
devkit_link_real_root
|
|
"${BB}" echo "${DEVKIT_INIT_VERSION}" > "${WRITABLE}/.initialized" 2>/dev/null || true
|
|
return 0
|
|
}
|
|
|
|
devkit_chroot_exec() {
|
|
devkit_setup_chroot || {
|
|
"${BB}" echo "devkit: failed to set up chroot environment" >&2
|
|
return 1
|
|
}
|
|
exec "${BB}" chroot "${MERGED}" "$@"
|
|
}
|