Build and run CoCo/Kata Container with RME Support
We are using Qemu Emulator here to serve as Kata Container host, and then launch the Kata containers with RME/CCA support inside it.
Host: Qemu Emulated, machine model: max, root filesystem is ubuntu 24.04.
Guest: VMM is Qemu with CCA/RME support.
Our Target
Launch Kata/CoCo now with CCA support and do the remote attestation inside the containers.
Current Limitation:
Use nerdctl to launch the container manually, the k0s support with Kata-deploy is still WIP.
The guest-image pull function is now supported without any external snapshotter such as Nydus.
Boot the encrypted image, doing the remote attestation, get the decryption key and decrypted is supported for this working flow.
Prepare Kata container Host and Qemu
Kata containers request a bunch of kernel configurations in order to serve as a Host. The kata-dev repository contains some scripts to build Host kernel, host firmware and Host filesystem (currently support ubuntu 24.04 and 22.04).
Host Kernel
git clone https://github.com/kevinzs2048/kata-dev
cd kata-dev
make kata_host_kernelHost Filesystem
cd kata-dev
OS_IMAGE_SIZE=20 IMAGE_NAME=ubuntu_24.img make ubuntu_imageIt will generate an 20GB image named ubuntu_24.img with ubuntu 24.04 installed in kata-dev directory. This image also automatically configures a network with static IP 192.168.122.22, assumes that your bridge network for libvirt is 192.168.122.1. If you install the libvirtd and virt-manager, it will automatically setup the bridge.
The login credential to this Ubuntu image is set to linaro/linaro.
Install essential kernel modules
cd kata-dev
make install_kernel_modulesThis command need sudo.
Build Host Firmware
make host_firmwareThis process will build tf-rmm, EDK2 and TF-A, then generates flash.bin under the kata-dev directory.
Build the Qemu command to launch CCA Host
make qemu_cca_hostThe Qemu version: https://git.codelinaro.org/linaro/dcap/qemu -b cca/2025-05-28
Launch the CCA Host
make run_hostYou can also run this command locally:
$QEMU_WORKDIR/build/qemu-system-aarch64 \
-M virt,virtualization=on,secure=on,gic-version=3 \
-M acpi=off -cpu max,x-rme=on,sme=off,pauth-impdef=on \
-m 3G -smp 4 \
-nographic \
-bios ../flash.bin \
-kernel $KERNEL_WORKDIR/arch/arm64/boot/Image \
-drive format=raw,if=none,file=../$IMAGE_NAME,id=hd0 \
-device virtio-blk-pci,drive=hd0 \
-append root=/dev/vda \
-nodefaults \
-serial tcp:localhost:54320 \
-serial tcp:localhost:54321 \
-chardev socket,mux=on,id=hvc0,port=54322,host=localhost \
-device virtio-serial-device \
-device virtconsole,chardev=hvc0 \
-chardev socket,mux=on,id=hvc1,port=54323,host=localhost \
-device virtio-serial-device \
-device virtconsole,chardev=hvc1 \
-append "root=/dev/vda rw earlycon console=hvc0 nokaslr" \
-net nic,macaddr=52:54:30:12:34:63 \
-net tap,ifname=tap1,script=no,downscript=no \
-device virtio-9p-device,fsdev=shr0,mount_tag=shr0 \
-fsdev local,security_model=none,path=../../,id=shr0The command above is taking reference from here: https://git.codelinaro.org/linaro/dcap/op-tee-4.2.0/build/-/blob/cca/v8/qemu_v8_cca.mk?ref_type=heads#L623
Connect to the Host via SSH
Assume that your environment already has a bridge to the NIC:
4: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 52:54:00:51:7b:99 brd ff:ff:ff:ff:ff:ff
inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
valid_lft forever preferred_lft foreverAdd tap1 to virbr0 and enable it.
sudo brctl addif virbr0 tap1
sudo ifconfig tap1 0.0.0.0 promisc upConnect to the Kata host:
ssh linaro@192.168.122.22Mount the shared directry:
Once in the the host, you can mount the kata-dev directory shared with the build machine:
sudo mount -t 9p shr0 /mnt/Install and Configure the Kata container
Build the Kata containers and CoCo components using Kata-deploy
cd kata-dev
make build_kataAfter this components, you will see the generated file as below:
# In the directory: kata-containers/tools/packaging/kata-deploy/local-build/build
kata-static-kernel-cca-confidential.tar.xz
kata-static-qemu-cca-confidential.tar.xz
kata-static-rootfs-cca-confidential-image.tar.xz
kata-static-rootfs-cca-confidential-initrd.tar.xz
kata-static-shim-v2.tar.xz
kata-static-ovmf-cca.tar.xzIf you don’t want to build the Kata related components yourself, you can simply get pre build files here:
https://people.linaro.org/~kevin.zhao/kata-cca/Install Kata/CoCo from tarball
TODO: Use kata-deploy and K0s
Currently manually: On the build machine, copy the binaries stored at: kata-containers/tools/packaging/kata-deploy/local-build/build to Kata Host Machines which mentioned at the above part.
rm -rf /opt/kata
cd binaries/
# only install below packages
file_list=("kata-static-kernel-cca-confidential.tar.xz"
"kata-static-qemu-cca-experimental.tar.xz"
"kata-static-rootfs-cca-confidential-image.tar.xz"
"kata-static-rootfs-cca-confidential-initrd.tar.xz"
"kata-static-shim-v2.tar.xz"
"kata-static-ovmf-cca.tar.xz")
for file in "${file_list[@]}" ; do
if [ -f "$file" ]; then
sudo tar -xJf "$file" -C /
fi
doneAfter installation, the rootfs/initrd/guest kernel are stored at /opt/kata/share, and containerd-shim-kata-v2/Kata-runtime/Kata-monitor are located at /opt/kata/bin
Configure Kata-containers and other container runtime configuration
Kata-containers configuration
Get the Kata container configuration from: /opt/kata/share/defaults/kata-containers/configuration-qemu-cca.toml, copy it to the path as below:
mkdir /etc/kata-containers/
cp /opt/kata/share/defaults/kata-containers/configuration-qemu-cca.toml /etc/kata-containers/configuration.tomlModify some essential items in /etc/kata-containers/configuration.toml:
[hypervisor.qemu]
path = "/opt/kata/bin/qemu-system-aarch64-cca-experimental"
kernel = "/opt/kata/share/kata-containers/vmlinuz-confidential.container"
image = "/opt/kata/share/kata-containers/kata-containers-confidential.img"Modify the timeout parameters for RME/CCA container
# Enlarge below in order to boot inside the emulator
dial_timeout = 500
create_container_timeout = 300Add the remote attestation configurations to the kernel params:
# Add the below parameters to kernel params
# agent.aa_kbc_params=cc_kbc::http://213.146.155.117:8080 agent.guest_components_rest_api=all
kernel_params = "cgroup_no_v1=all systemd.unified_cgroup_hierarchy=1 agent.aa_kbc_params=cc_kbc::http://213.146.155.117:8080 agent.guest_components_rest_api=all"You may need to load the vhost modules
sudo modprobe vhost_vsock
sudo modprobe vhost_net
echo -e "vhost-vsock\nvhost-net" | sudo tee /etc/modules-load.d/vhost.confConfigure the Containerd to use Kata-containers
Configure the Containerd to use Kata-container. Copy https://people.linaro.org/~kevin.zhao/config.toml.containerd to /etc/containerd/config.toml. Modify it to use the Kata configuration created above:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata.options]
ConfigPath = "/etc/kata-containers/configuration.toml"sudo apt install containerd -y
sudo systemctl daemon-reload
sudo systemctl restart containerdMore things for Kata with containerd, please refer here: https://github.com/kata-containers/kata-containers/blob/main/docs/install/container-manager/containerd/containerd-install.md
In particular you may need to make the binary accessible by containerd:
sudo ln -s /opt/kata/bin/containerd-shim-kata-v2 /usr/local/binInstall CNI - Container network interface
CNI is essential for container network. CNI can be installed either binaries or build from source, check : https://github.com/kata-containers/kata-containers/blob/main/docs/how-to/containerd-kata.md
Install nerdctl command line
Install nerdctl command line. nerdctl cmdline will automatically set up the CNI network with host-local, and it is compatible with docker command line. Just copy the binaries to the host and it is ready to go. See more for Nerdctl.
nerdctl is the docker-compatible OCI cmd line, it can also automatically configuration the CNI network.
Run command like below to launch Kata container:
nerdctl run --runtime io.containerd.kata.v2 -it docker.io/library/busybox:latest shRemote attestation: Configure and Launch the Trustee verification service
We need another node to running as the Trustee service node
Please refer this document to deploy Trustee verification service, and using the Veraison verification as the 3rd party verifier: https://github.com/confidential-containers/trustee/tree/main/deps/verifier/src/cca
After deployment, we get the kbs endpoint:
http://<Trustee Node IP>:8080Use case 1: Launch Kata containers and execute the remote attestation
No need to set up nydus
Before start, modify the items in /etc/kata-containers/configuration.toml (The default configure for share_fs is none, which means we use guest-pull features and don’t use the file share). In this case, we just use virtio-9p to share the container image from host to guest. There is no confidential guest image protection in this case, we will try the remote attestation after the container start. NOTE: `virtio-fs` is currently not supported with RME support.
shared_fs = "virtio-9p"Restart containerd service:
sudo systemctl restart containerdLaunch the Kata-containers with nerdctl.
sudo nerdctl run --runtime io.containerd.kata.v2 -it curlimages/curl:latest shAfter about 1 mins, the Kata container is ready and we are now in the sh cmdline. This command includes the curl command so that we can test get_token inside the container.
Manually Get Attestation Token
In the container sh do get_evidence cmd:
# in the container sh
curl http://127.0.0.1:8006/aa/token\?token_type\=kbsThis command above is the HTTP access to api-server-rest service which is the coco-guest-component binaries packaged inside the Kata container sandbox rootfs image. This http access will final call attestation-agent to get the evidence via linux-coco TSM api, and then send to the KBS service endpoint to get the remote attestation token.
Please also check the log for Trustee, you can get the logs.
Use Case 2: Launch the container with the encrypted image
This case will include 2 parts of work:
Image encryption and upload the decryption key to KBS
Kata container boot with
guest-pullfunction, and then doing remote attestation to get the decryptionWe will enable the force_guest_pull, and no image snapshotter service(like
Nydus)is needed.
Before start, make sure that the two configurations items is enabled:
# Do not share between host and guest
shared_fs = "none"
# Force enable the guest_pull features which will pull image in the guest
experimental_force_guest_pull = trueSetup the local container registry
This step we need to setup a local container registry, please note that the registry node should be different with the Kata container host.
In the registry node, install docker.
Then use the self signed key, and set the node DNS name as registry.com
mkdir -p /home/linaro/certs
openssl req -newkey rsa:4096 \
-addext "subjectAltName = DNS:registry.com" \
-nodes -sha256 -keyout /home/linaro/certs/domain.key \
-x509 -days 365 -out /home/linaro/certs/domain.crtNOTE when the command prompted with Common Name, please type into registry.com
Common Name (e.g. server FQDN or YOUR name) []:registry.com
Set the local registry host name IP:
vim /etc/hosts
127.0.0.1 registry.comAdd the domain.crt into registry node filesystem root cert, so that we can get access to the local container registry locally with https. In Ubuntu/Debian execute this:
cp certs/domain.crt /usr/local/share/ca-certificates/
sudo update-ca-certificatesIn Centos:
cat /home/linaro/certs/domain.crt >>/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
cat /home/linaro/certs/domain.crt >>/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crtStart the container registry
docker run -d -p 5000:5000 \
--restart=always \
--name registry.com \
-v /home/linaro/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-v /home/linaro/data:/var/lib/registry \
registry:2Write the domain.crt to the Kata Guest rootfs
Copy the domain.crt to the Kata Container Host node. This process either do like below or we can pre-set when we do the Kata rootfs build process.
mkdir mnt
qemu-nbd --connect=/dev/nbd0 /opt/kata/share/defaults/kata-containers-confidential.img
mount /dev/nbd0p1 mnt/
cat domain.crt >> mnt/etc/ssl/certs/ca-certificates.crtAdd the DNS info for the Registry Node. Assume the Registry Node IP is: 192.168.122.1:
vim mnt/ect/hosts
# Add below to the file
192.168.122.1 registry.comUmount the image:
umount mnt/
qemu-nbd --disconnect /dev/nbd0Use Skopeo to Prepare the encrypted image
# Prepare the encrypted image
KEY_FILE="image_key"
head -c 32 /dev/urandom | openssl enc > "$KEY_FILE"
# create the directory for the key
mkdir output
# Use the coco-keyprovider container image to encrpyted image
# Parameters:
# -i : the Key storaged path in keybroker.
# -s : the image to be encrypted.
# -k : The key to encrypt the container image.
# -d : The path for the output images.
docker run -v "$PWD/output:/output" \
ghcr.io/confidential-containers/staged-images/coco-keyprovider:latest /encrypt.sh \
-k "$(base64 < image_key)" \
-i kbs:///default/image_key/ubuntu_enc \
-s docker://ubuntu:latest \
-d dir:/output# Push the image to local registry
skopeo copy dir:output docker://registry.com:5000/ubuntu_enc:latest
# Use the command below to see if the image is encrypted successfully
skopeo inspect docker://registry.com:5000/ubuntu_enc:latestIf the container image manifest shows like "MIMEType": "application/vnd.oci.image.layer.v1.tar+gzip+encrypted", that means the image is right encrypted.
The image_key generated above needs to copied to Trustee Service node, with the directory path:
/opt/confidential-containers/kbs/repository/default/image_key/ubuntu_encLaunch the container with the following command:
sudo nerdctl run --runtime io.containerd.kata.v2 --annotation io.kubernetes.cri.image-name=registry.com:5000/ubuntu-encrypt:noble --rm -it registry.com:5000/ubuntu-encrypt:nobleThe
--annotation io.kubernetes.cri.image-nameindicates the image to be pulled in the guest.The image name pass to
annotationshould be the same with the container image parameters in nerdctl command, otherwise the container can not start.
The totally working flow is: