Kata Containers

How c8s uses Kata Containers to run each pod as its own confidential VM (AMD SEV-SNP or Intel TDX) — measured direct-kernel boot, the sealed guest image, and the guest-pull model.

When you install c8s with --kata, every workload pod becomes its own confidential VMAMD SEV-SNP by default, Intel TDX with --hardware-platform=tdx. This is the pod-as-kata-cvm model: "each pod is its own confidential VM." Kata Containers provides the per-pod micro-VM; c8s provides the attestation, identity, and policy that make it confidential and verifiable.

What gets installed

c8s install --kata deploys the Kata runtime stack and registers four RuntimeClasses — the two non-confidential classes plus the declared platform's confidential (CPU, GPU) pair:

  • kata-deploy (DaemonSet, digest-pinned) installs the Kata runtime and containerd-shim-kata-v2 on every node — including the declared platform's confidential QEMU build, the guest kernel, guest image, and OVMF firmware. It restarts containerd/RKE2 after install; running pods survive via shim persistence.

  • RuntimeClasses:

    RuntimeClassHypervisorConfidential?
    kata-qemuQEMU micro-VMNo — VM isolation from the host only
    kata-clhCloud HypervisorNo — VM isolation only
    kata-qemu-snp / kata-qemu-tdxQEMU + SEV-SNP / Intel TDXYes — pod memory is encrypted against the host; schedules only on platform-labeled nodes
    kata-qemu-snp-nvidia / kata-qemu-tdx-nvidiaas above + VFIO GPU passthroughYes — a confidential VM with a dedicated NVIDIA GPU

    A cluster runs one platform's confidential classes: only the pair matching the install's --hardware-platform is rendered, and the webhook promotes confidential.ai/cw pods to its CPU class (and pods requesting nvidia.com/* to its GPU class) — mixed-TEE clusters are not supported. The rest of this page describes the SEV-SNP path.

Installing --kata also disables the host-side ratls-mesh, attestation-api, and nri-image-policy: under Kata their function moves inside the guest image, where the host cannot tamper with it.

Measured direct-kernel boot

Kata's confidential path does not use IGVM or a UKI. It uses measured direct-kernel boot with dm-verity:

ComponentWhat's supplied
kernela bare vmlinuz (a hardened kernel)
imagekata-rootfs.img — an ext4 rootfs plus a dm-verity hash tree
verity paramsroot_hash=…,salt=…,data_blocks=… on the kernel cmdline

The dm-verity root hash rides on the kernel cmdline, and the cmdline is folded into the SNP launch measurement at boot. So the launch digest commits to OVMF + vmlinuz + the exact rootfs. To keep the digest stable it is measured at 1 vCPU and predicted with sev-snp-measure, then published so an operator can pre-attest it and pin it in kata.guestImage.tag.

The sealed guest image

The kata-guest-base image is the dm-verity-sealed guest rootfs. Because it is part of the launch measurement, everything baked into it is transitively attested:

Baked-inRole
attestation-servicelocalhost-only attester at 127.0.0.1:8400
ratls-meshin-guest mesh proxy (ratls-mesh in-guest)
policy-monitorwatches container creation and SIGKILLs non-allowlisted images
/etc/c8s/bootstrap-allowlist.jsonthe baked image-digest allowlist (part of the measurement)
/etc/kata-opa/default-policy.regoOPA policy denying host exec/log RPCs and SetPolicyRequest

A peer that verifies an RA-TLS handshake therefore transitively verifies that the attestation service, mesh proxy, and policy monitor are the genuine, sealed versions — they cannot be swapped out by the host.

In debug builds (--kata --debug), the guest policy allows host log/exec streams so kubectl logs and kubectl exec work — but container I/O becomes readable by the untrusted host, and the launch measurement differs from the locked image. Development only.

Three layers of attestation

   LAYER 0                  LAYER 1                  LAYER 2
 ┌───────────────┐        ┌───────────────┐        ┌───────────────┐
 │  Guest image  │        │   Container   │        │   Workload    │
 │               │──────► │     image     │──────► │   identity    │
 │  SNP launch   │        │               │        │               │
 │    digest     │        │  OCI digest · │        │    RA-TLS     │
 │               │        │   allowlist   │        │  certificate  │
 └───────────────┘        └───────────────┘        └───────────────┘
  1. Guest image — the SNP launch digest over OVMF + vmlinuz + cmdline (embedding the dm-verity root hash). Operator pre-attests and pins it.
  2. Container image — the OCI image digest, checked against the allowlist. The pod webhook injects get-cert, which fetches a leaf cert from the CDS — the CDS verifies the container measurement and signs in one step.
  3. Workload identity — an RA-TLS cert carrying attestation evidence; peers verify it on the mTLS handshake.

The guest-pull model

With shared_fs="none" and experimental_force_guest_pull=true, the kata-agent's confidential-data-hub pulls the OCI image inside the guest over virtio-net — not from a host bind-mount. The host never sees the unpacked workload rootfs, only encrypted VM memory.

A consequence worth planning for: a private workload image needs two credentials — one host-side (for the kubelet's pull) and one in-guest at /run/image-security/auth.json (for the confidential-data-hub's pull).

Constraints

  • x86-64, SEV-SNP or Intel TDX. The confidential pod path needs an AMD SEV-SNP host (Genoa / Milan / Turin class) or an Intel TDX host, declared at install time via --hardware-platform. On a host with neither TEE you can still run kata-qemu (isolation, not confidentiality) but no confidential class.
  • Confidential GPU is passthrough-based and comes with constraints. GPU pods run as confidential VMs with the NVIDIA GPU passed through over VFIO (request an nvidia.com/* resource; the webhook injects the GPU RuntimeClass). The GPU host setup (vfio-pci binding, GPU CC mode, BAR resize) is provisioning's job, not the NVIDIA GPU Operator's — the operator assumes host-visible GPUs with a host driver and must not be installed on these clusters. GPU attestation (SPDM) is not yet surfaced to the relying party — see Limitations.
  • Per-pod, not per-node. Pod-as-CVM attests each pod; it does not by itself attest the node. If you need a single confidential node instead, see pod-as-CVM vs node-as-CVM.
  • Not available on Azure AKS. Per-pod CVMs need nesting that AKS does not provide; use the node-as-CVM shape there — see Provisioning on Azure.