Install c8s
Prepare an external measured CDS, generate its component allowlist, and install c8s onto an existing confidential Kubernetes cluster.
Install c8s onto an existing confidential Kubernetes cluster after preparing its external Certificate Distribution Service (CDS). The CDS runs in its own confidential VM (CVM); Helm installs the cluster components that trust it.
If your node image already owns the installation and labels c8s-system with
confidential.ai/baked=true, c8s install refuses a second installation. Use its signed launch
configuration and fetch its kubeconfig.
Prerequisites
- A Kubernetes 1.30 or newer cluster on AMD SEV-SNP or Intel TDX confidential nodes,
with
kubectlconfigured for it. Choose the matching deployment shape below. - A Kubernetes identity authorized to install and update c8s's cluster-scoped chart resources, including its RBAC resources, and to create or update component namespaces and apply their privileged pod-security labels. Permission to list nodes alone does not establish these powers.
- Every node must publish at least one pod range in
spec.podCIDRorspec.podCIDRs. c8s,helm,kubectl,crane,openssl, andgenisoimageon your workstation'sPATH, plus access to the component registries. The installer usescraneto resolve component images and read their configurations.- CDS build outputs supplied by your image producer:
disk.raw,manifest.json, and the matching boot artifacts from one build. The source entrypoint isimages/cds-guest/buildin the c8s checkout; its builder writes these together underoutput/cds. For SEV-SNP, retain theguest-smp<N>.igvmmatching the chosen vCPU count. Obtain this complete bundle before starting;<CDS_MANIFEST_JSON>below names itsmanifest.json. - A VM provisioning mechanism for that CDS bundle that accepts an
opkeydataconfiguration ISO and the platform-specific launch settings described below. The VM must be reachable from your workstation and cluster on its CDS client port, normally TCP8443. CDS must reach each admitted node's inventory on TCP1019, using addresses in your node CIDRs. - Vendor verification collateral available in the evidence, local cache, or through network
access when retrieval is needed. A collateral retrieval failure returns verification exit
code
3; resolve it before continuing. - An independently approved JSON image policy for the nodes CDS may admit, and the cluster's
node, pod, and service CIDRs. Use complete launch identities; obtain them from approved image
and launch artifacts. Use a measured-node bundle's
peers.jsonas<NODE_POLICY_JSON>only when it describes exactly the nodes you intend CDS to admit. See the bundle's policy files. - An approved policy for the nodes hosting the router, used as
<ROUTER_POLICY_JSON>. Reuse<NODE_POLICY_JSON>when its entire node set is acceptable for the router; otherwise prepare a policy restricted to the intended router nodes before starting.
Before generating the seed or preparing CDS, check every node:
kubectl get nodes \
-o custom-columns='NAME:.metadata.name,POD_CIDR:.spec.podCIDR,POD_CIDRS:.spec.podCIDRs'Each row must contain a pod CIDR in at least one of the two pod-range columns. Stop if any
node has neither; resolve the cluster's pod-range publication before proceeding. Supplying
--pod-cidr to CDS configuration does not satisfy this node-object requirement. Seed generation
can succeed before the final install checks it.
To build the CLI from an existing c8s source checkout, use Go 1.27.3 or a compatible newer toolchain and run:
make installThis uses go install. Add go env GOBIN, or $(go env GOPATH)/bin when GOBIN is empty,
to your PATH.
Choosing the install shape
Pass both --cvm-mode and --hardware-platform in each installation command:
bare-metal: self-managed nodes with the baked attestation API and NRI image-policy plugin. Confirm your prepared image against the node CVM launch contract. Its published artifacts supply this stack. Current image-owned installations follow the kubeconfig path above; an arbitrary stock confidential VM does not supply the required stack.gke: the GKE confidential-node shape.aks: Azure confidential nodes using the vTPM report path.
--hardware-platform selects sev-snp or tdx for the nodes. The external CDS selects its
own platform separately. Each node CVM is one trust domain; see
trust boundaries.
Choose release, namespace, image tag, optional components, and any -f values files before
rendering the seed. Use the same choices for the final install. If another ingress owns host
port 443, set router.hostPort.enabled: false in a values file and arrange reachable ingress
to the router Service. Pass that file to both commands below. Computed installer values take
precedence over your values files; see the CLI reference.
Prepare CDS and install
This procedure creates a singleton CDS. For a replicated CDS, use the CDS configuration and HA commands, including signed Genesis initialization, before installing cluster components.
Create the operator credential
umask 077
openssl ecparam -name prime256v1 -genkey -noout -out operator.key
openssl ec -in operator.key -pubout -out operator.pubKeep operator.key private. The public key goes into the CDS configuration to authorize policy
writes. The installer's --operator-keys supplies router route-verification keys; it does not
change the external CDS's authority. CDS refuses a configuration with no operator keys.
Generate the component seed
c8s install --cvm-mode '<CVM_MODE>' --hardware-platform '<NODE_TEE>' \
--image-tag '<IMAGE_TAG>' --operator-keys operator.pub \
--allowlist-seed-out component-seed.jsonReplace <CVM_MODE> with the shape above, <NODE_TEE> with sev-snp or tdx, and
<IMAGE_TAG> with the component release tag you intend to install. This reads the cluster and
registries, writes component-seed.json, and stops before installation. The seed describes the
component images and launch settings that the image allowlist
must admit.
Generate the measured CDS configuration
c8s cds config new --out cds-bootstrap --cluster-id '<CLUSTER_ID>' \
--operator-keys operator.pub --allowlist-seed component-seed.json \
--node-pins '<NODE_POLICY_JSON>' \
--node-cidr '<NODE_CIDR>' --pod-cidr '<POD_CIDR>' --service-cidr '<SERVICE_CIDR>' \
--image-manifest '<CDS_MANIFEST_JSON>' --platform sev-snp \
--vcpus '<CDS_VCPU_COUNT>'Choose <CLUSTER_ID> yourself, for example inference-prod, using an RFC 1123 label; it is
the identity you assign this CDS configuration, not a value discovered from Kubernetes.
Replace <NODE_POLICY_JSON> with the approved node-policy file and the three CIDRs with your
cluster's ranges. Repeat each CIDR flag for additional ranges. Node inventory subnets must
exclude pod and service ranges. <CDS_MANIFEST_JSON> is
the exact CDS image's manifest; <CDS_VCPU_COUNT> is a launch shape that manifest supports.
For a TDX CDS, use --platform tdx and omit --vcpus.
The new cds-bootstrap directory contains cds-config.yaml and cds-pin.json. The pin binds
the CDS image and exact configuration bytes. Keep the host-readable configuration limited to
public inputs; private keys and secrets do not belong on this disk.
Launch and verify the external CDS
Prepare the configuration disk's contents:
mkdir cds-opkeydata
cp cds-bootstrap/cds-config.yaml cds-opkeydata/pubkey
sha256sum cds-opkeydata/pubkey
genisoimage -quiet -rock -joliet -volid opkeydata \
-output cds-opkeydata.iso cds-opkeydata/Hand cds-opkeydata.iso and the CDS build bundle to your VM provisioning mechanism. Attach
the ISO as the configuration disk and boot the CDS image selected above. For SEV-SNP, set the
VM's HOST_DATA to the printed SHA-256 and use the selected vCPU count. The TDX boot path binds the configuration
in RTMR[3]. The guest stages it at /run/confai/operator-pubkey; CDS checks its exact bytes
against a self-report before serving.
After the VM is reachable, verify its identity, admitted node set, and operator keys:
c8s cds verify 'https://<CDS_HOST>:8443' \
--image-policy-file cds-bootstrap/cds-pin.json \
--served-policy-file '<NODE_POLICY_JSON>' --operator-keys operator.pub && \
c8s cds ca --url 'https://<CDS_HOST>:8443' \
--image-policy-file cds-bootstrap/cds-pin.json --out mesh-ca.pemReplace <CDS_HOST> with its reachable DNS name or IPv4 address, and adjust the port if needed.
Use the same independently intended node policy as above. For HA, Genesis can override the
configuration's initial node set, and later signed commands can change it: verify against the
intended current set. A CDS identity pin alone does not check that set.
Require verification exit code 0 before fetching the CA or installing components. The &&
gates the CA read on that result. Any other verification status, including partial verification
or unavailable collateral, stops this procedure. Continue only after the CA read also succeeds.
Retain mesh-ca.pem as the deployment's mesh CA anchor. Compare subsequent CA reads with your
retained anchor before replacing it; a singleton restart replaces its volatile CA and secrets.
Install the cluster components
c8s install --cvm-mode '<CVM_MODE>' --hardware-platform '<NODE_TEE>' \
--image-tag '<IMAGE_TAG>' --operator-keys operator.pub \
--cds-address '<CDS_HOST>:8443' --cds-pin cds-bootstrap/cds-pin.jsonReuse the seed command's release, namespace, image tag, values files, and component options.
The installer checks the named CDS configuration against the required component seed and the
cluster's pod ranges. It runs helm upgrade --install with --wait by default. If Helm fails,
inspect the retained resources, correct the cause, and rerun the command.
Verify the install
Check the default component namespaces:
kubectl get pods -n c8s-system
kubectl get pods -n c8s-router
kubectl get mutatingwebhookconfiguration c8s-pod-injector \
-o jsonpath='{.webhooks[*].clientConfig.caBundle}'Expect component pods to become ready and the webhook CA bundle to be nonempty. Substitute
your release namespace and <RELEASE>-pod-injector name if you changed those defaults. The
external CDS does not appear in either pod listing. Readiness confirms deployment health;
it does not prove confidentiality.
From outside the cluster, verify the reachable router with its own approved node policy and the mesh CA fetched from the pinned CDS:
c8s verify 'https://<ROUTER_HOST>' --kind lb \
--image-policy-file '<ROUTER_POLICY_JSON>' --mesh-ca mesh-ca.pem<ROUTER_HOST> is your configured reachable router ingress. <ROUTER_POLICY_JSON> is the
independently approved complete policy for the node images hosting the router, including
required launch bindings and TDX registers. It is distinct from cds-pin.json.
Expect exit code 0; treat partial verification (exit 4) as an incomplete check. Follow
Verify a deployment to interpret the verdict and add
workload, policy, and route checks; this command alone does not prove the complete live policy.
The router needs a signed route manifest before it can serve application requests. Continue
with Run a confidential workload and
Manage the allowlist. Applications serve their ordinary
TCP/HTTP port; the injected mesh endpoint carries authenticated TLS and keeps mesh credentials
private. A covered pod's injected init containers begin with c8s-mesh, c8s-cert, then
c8s-cert-wait.
For failures, use Troubleshoot. To remove the installation, see Uninstall c8s.