This episode covers supply chain security for Kata workloads: secure boot with guest kernel signature verification, image signing and SBOM, and admission policy with Kyverno/OPA. You'll understand how to make sure only trusted images run in your microVMs.

The last two episodes secured the runtime (microVM) and the network. Episode 15 secures something more fundamental: where does the running code come from? Even the most secure microVM is worthless if its guest kernel is a tampered image, or if the container image being pulled comes from an untrusted source.
This episode covers two sides of the supply chain: secure boot — ensuring the booted guest kernel is the one it should be, and image trust — ensuring the container images being run are signed, documented (SBOM), and pass admission policy.
Secure boot means: before the guest kernel is executed, its signature is verified. Only kernels signed with a trusted key may boot. This prevents attacks where an attacker replaces the guest kernel file with a malicious version — for example through access to the node's filesystem.
In Kata, the guest kernel and guest image can be signed. Verification happens before boot, so the microVM never executes an unauthenticated binary.
Kata provides secure boot-related options in the configuration file:
[hypervisor.qemu]
# Enable secure boot for the guest
enable_secure_boot = trueenable_secure_boot = true enables secure boot on the guest. With this feature active, boot will fail if the guest image doesn't have a valid signature — a failure that's better than running a suspected image.
Note: secure boot isn't a replacement for all supply chain controls. It protects Kata's guest image. The application images inside the guest still need to be protected with the image trust mechanisms below.
A container image should be signed before it's run. Signing ensures the image comes from the right source and wasn't altered in transit. The common tools: cosign (Sigstore) and Notary. With signing, the party running the image can verify who published it.
A brief signing flow with cosign:
cosign sign ghcr.io/perusahaan/image:1.0.0
cosign verify --key cosign.pub ghcr.io/perusahaan/image:1.0.0cosign sign adds a signature to the image, and cosign verify verifies that signature with a public key. In production environments, this verification is done automatically before an image runs — through the admission controller below.
Attestation goes further than signing: not just "who published", but "what's in the image". Attestation carries verified claims — for example the image SBOM, vulnerability scan results, or build provenance (which CI the image was built from). Sigstore calls this provenance; another common mechanism is in-toto attestation.
From episode 11, you already know attestation for confidential computing. The principle is the same here: before trust, prove first.
SBOM (Software Bill of Materials) is a complete inventory of the software components in an image — every package, library, and their versions. Why does it matter for Kata workloads? Because the microVM adds system components (guest kernel, agent, hypervisor) on top of the application image. For a full audit, the SBOM must cover both.
Create and store the SBOM as an auditable artifact:
syft ghcr.io/perusahaan/image:1.0.0 -o spdx-json > sbom.jsonsyft ... -o spdx-json > sbom.json produces an SBOM in SPDX format. Store this SBOM alongside the image in the registry — so when an incident happens, you can answer "which packages were involved" in minutes, not days.
Signing and SBOMs are useless if they're not enforced. An admission controller sits in the pod creation path: every pod creation request is first verified against policy. If the image isn't signed, the pod is rejected — no shortcuts.
Two commonly used tools: Kyverno (YAML-based, Kubernetes-native policies) and OPA/Gatekeeper (Rego, general purpose). Both work with Kata pods just like regular pods.
A simple policy: only signed images may run:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: check-cosign
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/perusahaan/*"
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----validationFailureAction: Enforce makes the policy reject violating pods. verifyImages checks that the image has a signature from a trusted attestor before the pod is allowed. Kata pods are no exception — this policy applies to all pods, including those using the kata RuntimeClass.
You can also create policies that require a specific RuntimeClass for untrusted workloads. For example: pods from the sandbox namespace must use runtimeClassName: kata — so no untrusted workload silently runs on runc:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-kata-for-untrusted
spec:
validationFailureAction: Enforce
rules:
- name: sandbox-must-use-kata
match:
any:
- resources:
kinds:
- Pod
preconditions:
all:
- key: "{{ request.object.metadata.namespace }}"
operator: Equals
value: sandbox
validate:
message: "Workload di namespace sandbox harus memakai runtimeClassName: kata"
pattern:
spec:
runtimeClassName: kataThis policy guarantees every untrusted workload in the sandbox namespace runs in a microVM. With admission policy like this, security decisions no longer depend on an engineer's memory — they're enforced by the system.
Tip
Admission policy is the best place to enforce runtime policies: require a RuntimeClass for certain namespaces, reject images without signatures, and block pods with disallowed privileges. Automation is more reliable than manual discipline.
Combine all the layers into a trust chain:
One broken link, and the episode 13 trust boundary weakens. Building all of it from the start is far cheaper than fixing it after an incident.
What you should take away:
kata RuntimeClass for untrusted workloads.In the next episode, episode 16, we'll cover multi-tenancy & isolation best practices — one pod per microVM, resource isolation via the VM, sandbox patterns, and the practice of separating trusted/untrusted workloads on different nodes with per-VM monitoring. This is the episode that unifies the isolation patterns into a real cluster design.