Your app proves who it is to Seal with its cloud identity: an AWS role, an Azure managed identity, a Google service account. That proof comes from credentials, and credentials can be copied. Someone who reads them off your machine can prove to be your app from anywhere, until they expire.
A TPM closes that gap. It's a chip, real or virtual, that holds a key no one can copy off the machine, root included. With it, each proof says both "this is my role" and "this request comes from this machine". A copied credential then works nowhere else.
The Seal SDK finds the TPM on its own. You don't change any code. If it can't use the TPM, it says why and links to the section below that fixes it.
Seal uses the TPM on AWS first. Azure and Google Cloud are next, and their sections are here so you can turn it on now.
AWS
AWS calls it NitroTPM. It's off unless the AMI turns it on:
- Linux AMIs: off. AWS doesn't publish Linux AMIs with NitroTPM turned on, Amazon Linux included, so you register your own.
- Windows AMIs from AWS: on.
Check an AMI:
aws ec2 describe-images --image-ids ami-0123456789abcdef0 \
--query 'Images[].[BootMode,TpmSupport]' --output text
You need uefi and v2.0. None means it's off.
Turn it on for an AMI
You can't change an existing AMI. Register a new one from its snapshot, with UEFI boot and TPM support. If the AMI isn't yours (Amazon Linux, Ubuntu), copy it into your account first, so the snapshot is yours:
aws ec2 copy-image --source-image-id ami-0123456789abcdef0 \
--source-region us-east-1 --name my-app-base
aws ec2 describe-images --image-ids ami-<copy> \
--query 'Images[].BlockDeviceMappings[0].Ebs.SnapshotId' --output text
aws ec2 register-image --name my-app-tpm \
--architecture x86_64 --virtualization-type hvm --ena-support \
--root-device-name /dev/xvda \
--boot-mode uefi --tpm-support v2.0 \
--block-device-mappings 'DeviceName=/dev/xvda,Ebs={SnapshotId=snap-<id>,VolumeType=gp3}'
If you build images with Packer, set it on the amazon-ebs builder:
boot_mode = "uefi"
tpm_support = "v2.0"
With Terraform, set it on aws_ami:
boot_mode = "uefi"
tpm_support = "v2.0"
Where it runs
- Instance types: most current Nitro types have NitroTPM. See AWS's list. But Seal needs the TPM's signed attestation, and not every type that has NitroTPM can produce one. In our tests a C7i could and a T3 couldn't. If you change an instance's type later, check the new type too.
- Not supported: Outposts, Local Zones and Wavelength Zones.
Azure
Azure's TPM comes with Trusted Launch.
- New Gen2 VMs and scale sets: on. Since July 2026 Trusted Launch is the default when you create them in the Portal, the CLI or PowerShell.
- ARM, Bicep, Terraform and the Azure SDKs: on once your subscription is registered for the default. See Microsoft's notes. Or set it yourself, as below.
- Existing VMs: unchanged. Gen1 VMs don't have it, but you can upgrade existing VMs to Trusted Launch.
To set it yourself with the CLI:
az vm create ... --security-type TrustedLaunch \
--enable-vtpm true --enable-secure-boot true
With Terraform, on azurerm_linux_virtual_machine:
vtpm_enabled = true
secure_boot_enabled = true
AKS
AKS node pools have it off unless you ask for it:
az aks nodepool add ... --enable-vtpm --enable-secure-boot
Pods on AKS should still prove who they are with a Kubernetes service account token.
Google Cloud
Google's TPM comes with Shielded VM. It's on by default for Google's public images. Check a VM:
gcloud compute instances describe my-vm \
--format='value(shieldedInstanceConfig)'
You need enableVtpm=True. To create a VM with it:
gcloud compute instances create my-vm ... \
--shielded-vtpm --shielded-secure-boot --shielded-integrity-monitoring
Let your app open the TPM
On a Linux VM the TPM is a device file, and your app's user needs read and write access to it. On AWS the SDK opens /dev/tpm0 itself, not the shared /dev/tpmrm0, because AWS's attestation command only goes through the device itself. So:
-
Give your app's user access. Out of the box only root can open it: on Amazon Linux 2023
/dev/tpm0isroot:root, mode0600. Rather than run your app as root, give the device to a group and put your app's user in it:sudo groupadd --system tss 2>/dev/null echo 'KERNEL=="tpm0", GROUP="tss", MODE="0660"' | sudo tee /etc/udev/rules.d/60-tpm.rules sudo udevadm control --reload && sudo udevadm trigger --name-match=tpm0Then, with systemd:
[Service] SupplementaryGroups=tss -
Keep the device free. Only one process can hold
/dev/tpm0at a time, and the SDK opens it only for the moment it takes to sign a proof. A resource manager daemon such astpm2-abrmdholds it permanently, so stop it on AWS.
Containers and Kubernetes
- Docker: pass the device in with
docker run --device /dev/tpm0 .... - Kubernetes: a pod doesn't get the node's TPM, and handing it one takes privileged access you shouldn't give an app. Pods prove who they are with a projected service account token instead. The console shows how when you add the app.
Check that it works
The console's Apps page shows, for every app, whether its last connection used the TPM. If it didn't, the page says why and links back here.
Once every machine running an app connects with its TPM, turn on Require cloud TPM for that app. From then on, a proof without one is refused.