CVM Base Image and Binary Building
This document explains how to build the prerequisite base images and binaries required by the CVM builder before running nvflare provision.
The image builder is shipped as part of the NVFlare source tree under nvflare/lighter/cc/image_builder/
and contains the cvm_build.sh script along with Ansible playbooks and helper scripts. It is typically
installed at ~/cc/image_builder, which is the path referenced by build_image_cmd in project.yml.
The following artifacts must be built and placed in the image builder directory:
base_images/ubuntu_base.qcow2— Ubuntu base disk imagebase_images/OVMF.amdsev.fd— Firmware withkernel-hashes=onsupportbinaries/snpguest— Tool for interacting with the TEEbinaries/kbs-client— Tool for communicating with Trustee KBS
Note
In examples below, <builder_root> refers to the directory containing the cvm_build.sh script.
Build Ubuntu Base Image
The Ubuntu base image must be built on a Ubuntu 25.04 host with an Ubuntu 24.04 guest.
Note
The OS versions differ between host and guest. This is the only tested combination.
The following instructions are adapted from NVIDIA’s Deployment Guide for SecureAI: https://docs.nvidia.com/cc-deployment-guide-snp.pdf
Download GPU Admin Tools
cd /shared/
git clone https://github.com/NVIDIA/gpu-admin-tools
Autoload VFIO
Create /etc/modules-load.d/vfio.conf with the following content:
vfio
vfio_pci
Restart the host to load the VFIO modules:
sudo reboot
Download Ubuntu Installation Image
Download the ISO file for Ubuntu 24.04.2:
cd /shared
wget https://releases.ubuntu.com/24.04.2/ubuntu-24.04.2-live-server-amd64.iso
Create a Drive Image
Create a drive image large enough to hold the OS. A minimum of 30GB is required to install Ubuntu and the GPU drivers. The builder will extend it as needed.
qemu-img create -f qcow2 /shared/ubuntu_base.qcow2 30G
Install Ubuntu Guest
Create the file /shared/launch_vm.sh with the following content:
#!/bin/bash
CORES=16
MEM=32
VDD_IMAGE=/shared/ubuntu_base.qcow2
FWDPORT=9899
CDROM=/shared/ubuntu-24.04.2-live-server-amd64.iso
doecho=false
docc=true
sev=""
while getopts "exp:c:" flag
do
case ${flag} in
e) doecho=true;;
x) docc=false;;
p) FWDPORT=${OPTARG};;
c) sev=${OPTARG};;
esac
done
NVIDIA_GPU=$(lspci -d 10de: | awk '/NVIDIA/{print $1}')
NVIDIA_PASSTHROUGH=$(lspci -n -s $NVIDIA_GPU | awk -F: '{print $4}' | awk '{print $1}')
if [ "$doecho" = true ]; then
echo 10de $NVIDIA_PASSTHROUGH > /sys/bus/pci/drivers/vfio-pci/new_id
fi
get_cbitpos() {
modprobe cpuid
EBX=$(dd if=/dev/cpu/0/cpuid ibs=16 count=32 skip=134217728 | tail -c 16 | od -An -t u4 -j 4 -N 4 | sed -re 's|^ *||')
CBITPOS=$((EBX & 0x3f))
}
if [ "$docc" = true ]; then
if [ -n "$sev" ]; then
case "$sev" in
sev|sev-es|sev-snp)
SEV_MODE="$sev"
USE_CC=true
get_cbitpos
;;
*)
echo "Error: unsupported SEV mode '$sev'."
echo "Use '-c' with valid options: sev, sev-es, sev-snp."
echo "Or use '-x' to boot without CC modes"
exit 1
;;
esac
fi
fi
qemu-system-x86_64 \
-bios /usr/share/ovmf/OVMF.fd \
-nographic \
${USE_CC:+ -machine confidential-guest-support=sev0,vmport=off} \
${USE_CC:+$( [ "$SEV_MODE" = sev ] && \
echo " -object sev-guest,id=sev0,cbitpos=${CBITPOS},reduced-phys-bits=1,policy=0x1" )} \
${USE_CC:+$( [ "$SEV_MODE" = sev-es ] && \
echo " -object sev-guest,id=sev0,cbitpos=${CBITPOS},reduced-phys-bits=1,policy=0x5" )} \
${USE_CC:+$( [ "$SEV_MODE" = sev-snp ] && \
echo " -object sev-snp-guest,id=sev0,cbitpos=${CBITPOS},reduced-phys-bits=1,policy=0x30000" )} \
-vga none \
-enable-kvm -no-reboot \
-cpu EPYC-v4 \
-machine q35 -smp $CORES -m ${MEM}G,slots=2,maxmem=512G \
-drive file=$VDD_IMAGE,if=none,id=disk0,format=qcow2 \
-device virtio-scsi-pci,id=scsi0,disable-legacy=on,iommu_platform=true,romfile= \
-device scsi-hd,drive=disk0 \
-netdev user,id=vmnic,hostfwd=tcp::$FWDPORT-:22 \
-cdrom $CDROM \
-device virtio-net-pci,disable-legacy=on,iommu_platform=true,netdev=vmnic,romfile= \
-object iommufd,id=iommufd0 \
-device pcie-root-port,id=pci.1,bus=pcie.0 \
-device vfio-pci,host=${NVIDIA_GPU},bus=pci.1,iommufd=iommufd0,romfile=
Launch the VM to start the Ubuntu installation:
chmod +x /shared/launch_vm.sh
sudo /shared/launch_vm.sh -ex
Install a minimal Ubuntu 24.04. All required software will be installed by the builder later. After the guest OS is installed, Ubuntu will prompt you to reboot — the VM will terminate and return you to the host.
Save Base Image
Copy the VM image to the base_images folder:
cp /shared/ubuntu_base.qcow2 <builder_root>/base_images
Fetch Firmware
Check if OVMF.amdsev.fd is already available in /usr/share/ovmf. If not, install it from the Ubuntu proposed repository:
echo 'deb http://archive.ubuntu.com/ubuntu plucky-proposed main restricted universe multiverse' | \
sudo tee /etc/apt/sources.list.d/plucky-proposed.list
sudo tee /etc/apt/preferences.d/99-plucky-proposed <<'EOF'
Package: *
Pin: release a=plucky-proposed
Pin-Priority: 100
EOF
sudo apt update
sudo apt install -t plucky-proposed ovmf
Copy the firmware to the base_images folder:
cp /usr/share/ovmf/OVMF.amdsev.fd <builder_root>/base_images
Build snpguest
The snpguest tool is needed to interact with the TEE (Trusted Execution Environment).
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
sudo apt install -y build-essential
# Checkout source code
git clone https://github.com/virtee/snpguest.git
cd snpguest
git checkout v0.9.2
cargo build -r
cp target/release/snpguest <builder_root>/binaries
Build kbs-client
The kbs-client tool is used to interact with Trustee. Its version must match the Trustee server version. The tested commit is a2570329cc33daf9ca16370a1948b5379bb17fbe.
# Install dependencies
sudo apt install -y pkg-config libtss2-dev
# Checkout source code
git clone https://github.com/confidential-containers/trustee.git
cd trustee/tools/kbs-client
git checkout a2570329cc33daf9ca16370a1948b5379bb17fbe
# Build
make -C ../../kbs cli
cp ../../target/release/kbs-client <builder_root>/binaries