Skip to main content

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.

Not yet validated on real accounts

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)​

PieceWhere
Edge, EDGE_KEYSTORE=azureskrA container in a confidential ACI container group (sku: Confidential)
SKR sidecar, mcr.microsoft.com/aci/skrA second container in the same group, on 127.0.0.1:8080
KeyAn exportable RSA-HSM key, 3072 bits or more, in Key Vault Premium or Managed HSM, with a release policy
IdentityA user-assigned managed identity on the group, with Key Vault's release permission on the key
  1. 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 allows exec into the group. Note the policy's SHA-256; Azure reports it as x-ms-sevsnpvm-hostdata.
  2. The key. Create it exportable, with release-policy.template.json filled in: the attestation provider as authority, and the policy hash from step 1. The template also requires azure-compliant-uvm, a non-debuggable VM and VMPL 0. Key Vault refuses to release to anything else.
  3. 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.
  4. 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.
  5. Verify. The Edge logs seed unsealed and reports hostdata:<hash> as its measurement; it must equal the hash in the release policy. Then check the refusals: the same template generated with --debug-mode must not get the key, and az container exec must 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)​

PieceWhere
Edge, EDGE_KEYSTORE=gcpcsThe workload container of a Confidential Space VM, production image
KeyA Cloud KMS symmetric key at protection level HSM
PoolA 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:

  1. 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.
  2. The pool and provider, issuer https://confidentialcomputing.googleapis.com, audience https://sts.googleapis.com, with the condition: swname is CONFIDENTIAL_SPACE, STABLE support, dbgstat is disabled-since-boot, and the image digest is the release's.
  3. 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.
  4. Launch the VM from the confidential-space image family (not confidential-space-debug), with the workload's configuration as tee-env-* metadata. Confidential Space passes no files, so the envelope goes inline as PASS_PHRASE_SEALED. The Edge image's launch policy allows only its own configuration variables — never PASS_PHRASE or PASS_PHRASE_FILE, so nobody can hand it a plaintext phrase there.
  5. Verify. Cloud Logging shows seed unsealed, and the measurement image:<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.