Confidential containers on Azure and GCP
The deployment recipes for azureskr and gcpcs.
Read that section first for what they protect against and what they do not.
Both recipes are built from Microsoft's and Google's documentation and the Edge's tests against
stand-ins for their services. Neither has run on a real Azure or GCP account yet. Run your own
end-to-end test — seal, deploy, see seed unsealed, then try the refusals below — before a
live wallet depends on either.
The reference files are in the repository under chainos-edge/deploy/azureskr/ and
chainos-edge/deploy/gcpcs/.
Azure: confidential container group (azureskr)
| Piece | Where |
|---|---|
Edge, EDGE_KEYSTORE=azureskr | A container in a confidential ACI container group (sku: Confidential) |
SKR sidecar, mcr.microsoft.com/aci/skr | A second container in the same group, on 127.0.0.1:8080 |
| Key | An exportable RSA-HSM key, 3072 bits or more, in Key Vault Premium or Managed HSM, with a release policy |
| Identity | A user-assigned managed identity on the group, with Key Vault's release permission on the key |
- The CCE policy. Fill in
aci-template.json— the Edge image by digest, the subnet, the identity, the key, the attestation provider — and generate the policy into it:az confcom acipolicygen -a aci-template.json. Never--debug-mode: that policy allowsexecinto the group. Note the policy's SHA-256; Azure reports it asx-ms-sevsnpvm-hostdata. - The key. Create it exportable, with
release-policy.template.jsonfilled in: the attestation provider asauthority, and the policy hash from step 1. The template also requiresazure-compliant-uvm, a non-debuggable VM and VMPL 0. Key Vault refuses to release to anything else. - Seal on a workstation, as the custodian (Key Vault read and wrap, never unwrap or
release):
chainos-edge seal-mnemonic --provider=azureskr --key=<versioned key id> …. It refuses a key that is not exportable with a release policy — an envelope under such a key could never be opened. - Deploy the template with the envelope and the API key as secure parameters. The data volume is an Azure Files share, so the outbox survives a restart.
- Verify. The Edge logs
seed unsealedand reportshostdata:<hash>as its measurement; it must equal the hash in the release policy. Then check the refusals: the same template generated with--debug-modemust not get the key, andaz container execmust fail.
Upgrading changes the image digest, so the CCE policy and its hash change. Regenerate the policy, update the key's release policy to the new hash, then redeploy.
GCP: Confidential Space (gcpcs)
| Piece | Where |
|---|---|
Edge, EDGE_KEYSTORE=gcpcs | The workload container of a Confidential Space VM, production image |
| Key | A Cloud KMS symmetric key at protection level HSM |
| Pool | A Workload Identity Pool whose OIDC provider trusts Google's attestation verifier, with a condition naming the image digest |
deploy/gcpcs/setup.sh is the reference, step by step:
- The key,
--protection-level=hsm. Its decrypter role goes only to the pool's principal set for the image digest — no service account, no owner. - The pool and provider, issuer
https://confidentialcomputing.googleapis.com, audiencehttps://sts.googleapis.com, with the condition:swnameisCONFIDENTIAL_SPACE,STABLEsupport,dbgstatisdisabled-since-boot, and the image digest is the release's. - Seal as the custodian (encrypter and viewer on the key, never decrypter), with
gcloud auth application-default login:chainos-edge seal-mnemonic --provider=gcpcs --key=projects/…/cryptoKeys/seed …. It refuses a key that is not in an HSM. - Launch the VM from the
confidential-spaceimage family (notconfidential-space-debug), with the workload's configuration astee-env-*metadata. Confidential Space passes no files, so the envelope goes inline asPASS_PHRASE_SEALED. The Edge image's launch policy allows only its own configuration variables — neverPASS_PHRASEorPASS_PHRASE_FILE, so nobody can hand it a plaintext phrase there. - Verify. Cloud Logging shows
seed unsealed, and the measurementimage:<digest>. Then check the refusals: the same workload on the debug image must not get a token the pool accepts, and the Edge refuses a debug image's token itself.
Instance metadata is readable by anyone with compute.instances.get: harmless for the
envelope, which is ciphertext, but API_KEY is there too. Restrict who can read the instance.
Upgrading changes the image digest. Add the new digest to the pool's condition and the key binding, start the new release, then remove the old digest.
Recovery copies
seal-mnemonic --generate needs a recovery envelope that opens outside any TEE: use
--recovery-provider=awskms or azurekv. A recovery copy under azureskr, gcpcs or nitro
would fail exactly when the environment's measurement does, and seal-mnemonic refuses it.