Threat Model
The adversary c8s defends against, where the trust boundary sits, which gates are enforced today, and why measurement pinning is mandatory for production.
c8s is designed around a single, demanding assumption: the infrastructure operator is adversarial. That includes the cloud provider, the bare-metal operator, and anything that can read raw host memory or inspect a QEMU command line. The trust boundary is the TEE — the node (an AMD SEV-SNP or Intel TDX confidential VM) under node-as-CVM, or each pod's own confidential VM under pod-as-CVM (Kata).
The Kubernetes control plane is on the far side of that boundary too. etcd sees only
ciphertext and public material for the keys c8s holds in the TEE — the mesh CA, the EAR
(Entity Attestation Result) issuer, RA-TLS leaf keys — because none of them is ever written
to a Kubernetes Secret. Ordinary Kubernetes Secrets are not covered: image-pull
credentials, the webhook caBundle, and any tenant Secret are plaintext to whoever reads
etcd.
What the adversary can and cannot do
The left column is the baseline you are coming from: an ordinary Kubernetes cluster on non-confidential hardware. Node-as-CVM gives the same protections as the right column, but at node granularity rather than per pod.
| Ordinary Kubernetes | Pod-as-CVM (Kata) | |
|---|---|---|
| Read workload memory | Yes (host-trusted) | No — the TEE encrypts pod memory |
| Tamper with the runtime undetected | n/a | No — tampering changes the launch measurement; attestation fails |
| See the unpacked workload rootfs | Yes | No — images are pulled and unpacked inside the guest |
| See which image a pod runs | Yes | Yes — the host brokers the pull, so it observes the reference and layers (metadata, not content) |
| Observe inter-node traffic | Mitigated by RA-TLS mesh | Mitigated by RA-TLS mesh |
| Read a running container's stdout | Yes | No — the locked guest policy denies ReadStreamRequest and ExecProcessRequest, so kubectl logs is empty by design |
Under pod-as-CVM, if the host tampers with the Kata runtime, QEMU, or the shim, it changes the pod's launch measurement, and attestation fails. The host can break a pod, but it cannot break it silently — detection is by attestation, not by trust.
A co-tenant pod is not the confidentiality adversary under node-as-CVM
Node-as-CVM is a single-tenant shape. The boundary is the node, the adversary (the cloud or bare-metal operator) sits outside it, and pods inside the node are separated only by the kernel — as they would be on any Kubernetes cluster.
What c8s does enforce inside the node is least privilege between workloads: a compromised
pod must not be able to obtain another pod's identity. When CDS issues a certificate it asks
the node's
admission inventory which pod is
behind the requesting key, and the inventory answers on a privileged port that only the
node's own network namespace can bind. The c8s-deny-host-namespaces
ValidatingAdmissionPolicy (hostNamespacePolicy.enabled, on by default) denies tenant pods
hostNetwork, hostPID, hostIPC, and hostPath volumes, which is what keeps a tenant pod
out of that namespace and stops it from answering as the inventory.
Privileged node DaemonSets — a CNI, a CSI driver, the NVIDIA GPU operator — can bind that
port. They are already root inside the node CVM and can read another pod's memory directly, so
c8s assumes them to be part of the node's trusted computing base rather than checking
them. The policy exempts the release namespace, kube-system, and anything you add to
hostNamespacePolicy.exemptNamespaces, so admitting one of those DaemonSets is a visible
decision in your values file.
Under pod-as-CVM each pod is its own CVM and the separation is hardware-backed instead.
Where the boundary sits
OUTSIDE the TEE, and still deciding what the TEE will accept:
CI · ghcr.io ────────────► the image digests baked into the measurement
your operator key ───────► who may rewrite the served allowlist
your measurement pins ───► which builds CDS and the mesh talk to
╔═ TEE BOUNDARY · HOST CANNOT READ ══════════════════════════════════════════╗
║ ║
║ attestation-api CDS workload ║
║ verifies hardware mesh CA + EAR issuer, prompts · weights ║
║ evidence one process KV cache ║
║ │ │ │ ║
║ └───── verdict ──────────►│──── leaf cert ───────────►│ ║
║ │ ║
║ │ CA bundle ║
║ ▼ ║
║ ratls-mesh ◄── attested mTLS ──► peers ║
╚════════════════════════════════════════════════════════════════════════════╝
ALSO outside, and assumed hostile:
hypervisor · host OS · kubelet · containerd · Kubernetes API · etcd · CNITwo different kinds of "outside". The supply chain never touches your data but decides what the platform will accept, so it is load-bearing; the host decides nothing and is assumed hostile.
What is enforced today
| Gate | Enforced by | Source of truth |
|---|---|---|
| TEE evidence is valid | attestation-api, co-located with CDS in the same TCB | hardware evidence verification |
| A CSR can be signed | CDS | verified evidence, plus cds.measurements when set |
| An image — and the argv it runs with — is allowed | nri-image-policy on the host, or in-guest policy-monitor under Kata | CDS-served allowlist, plus the seed baked into the measured image |
| Mesh peer chains to the mesh CA | ratls-mesh | mesh CA bundle (chain only — the peer's measurement is not pinned) |
| Pod is an injection candidate | mutating admission webhook, failurePolicy: Fail | pod annotation confidential.ai/cw |
A confidential.ai/cw pod cannot shed its injected c8s-cert sidecar | c8s-cw-label-integrity ValidatingAdmissionPolicy | API server |
A tenant pod cannot take the node's network, PID, or IPC namespace, or a hostPath | c8s-deny-host-namespaces ValidatingAdmissionPolicy | API server |
| The front door's attestation is bound to this cluster's identity | c8s cds-attest sidecar | a TEE report whose report_data transcript commits the session keys, the client nonce, the exact mesh leaf, and its issuing CA |
Two of these carry a caveat worth stating. The attestation-api's verdict is unsigned, so the gate holds because the verifier runs inside the same TCB as CDS — not because the response is authenticated. And the image gate covers the digest and the effective argv (see Allowlist); it does not cover env, mounts, capabilities, or the rest of the pod spec.
Measurement pinning is mandatory for production
The default chart pins no launch measurements. Both cds.measurements and
ratlsMesh.measurements default to empty, which means "accept any attested peer". That is
fine for a demo or a dev loop, but it is not the production trust model:
With empty measurements, an attacker on the pod network can stand up their own genuine TEE
enclave (same open-source image, valid measurement, real silicon) and MITM the bootstrap —
every signature checks out, but you are talking to an attacker-operated enclave. Pin both
cds.measurements and ratlsMesh.measurements to your known-good launch digests in
production — see Obtaining launch measurements
for how to get those values.
Under node-as-CVM, c8s install --measurements <M> pins both values in one step: M is the
node image's launch measurement, published in its manifest.json, and it is known before the
cluster runs. The flag is rejected with --cvm-mode=pod, where each Kata guest is measured
separately and the pins are per-guest.
Empty is not a bug — it is "pin nothing yet", and only you know your measurements. CDS and
ratls-mesh both log loudly when their allowlist is empty, and ratls-mesh publishes
ratls_mesh_measurement_pinning=0 for alerting. Neither signal makes accept-any safe.
Proving it is your cluster
A launch measurement proves "genuine audited code on real silicon". It does not prove "my cluster" — the open-source images are deliberately fungible across deployments, so anyone can boot the same build and produce a valid measurement. An outside verifier therefore has to pin a cluster-unique anchor as well: today, the mesh CA certificate.
Those two pins now authenticate a cluster. The front door's attestation binds the exact mesh
leaf and its issuing CA into the report_data transcript, and signs that transcript with the
leaf's private key, so copying a target cluster's public certificate chain is not enough to
impersonate it. The client verifies the hardware signature, the measurement against its
allowlist, the transcript, the chain to the pinned mesh CA, and the possession proof before
any application traffic flows. The proof is ECDSA, so cluster authentication is classical;
the session keys are X25519 + ML-KEM-768 hybrid, which covers confidentiality but is not a
post-quantum authentication claim.
See consumer verification for the client side of this.
The production direction
The chart-managed default keeps the CA key in CDS process memory: any restart generates a
new CA and invalidates old leaves, and there is a single CDS. The production direction holds
the CA key only inside attested CVM memory and lets replicas join via attested key
handoff (cds.handoff.enabled=true), so Kubernetes only ever sees ciphertext and public
material. A joining replica must present a launch digest in cds.measurements and a
REPORTDATA-bound hash of the operator-key set that exactly matches the serving CDS, so a
hostile control plane cannot hand the mesh CA to a replica running a substituted
allowlist-write policy. The chart refuses to render if either value is empty while handoff
is enabled. See CDS bootstrapping.
Out of scope (today)
These are known gaps, tracked but not yet enforced — see Limitations for the full list:
- The image gate covers the digest and argv, but not env, mounts, capabilities, or the rest of the pod spec.
- Mesh peer verification chains to the mesh CA but does not pin the peer's measurement.
- The sandbox ID stamped into a leaf is vouched for by the mesh CA signature and the node's admission inventory, not by hardware evidence.
- No per-workload peer allowlists in the mesh; no multi-tenant isolation between workloads on the same node CVM beyond what admission policy provides.
And these are permanent non-goals: side-channel attacks (micro-architectural, timing, power), denial of service and availability, application-layer vulnerabilities in your own workload, model distillation or dataset reconstruction from legitimate query access, physical attacks on the host, and a compromised hardware manufacturer.