|
| 1 | +# AWS EC2 Instance Attestation Security Evaluation |
| 2 | + |
| 3 | +This document evaluates dstack attestation on AWS EC2 with NitroTPM |
| 4 | +(Attestable AMIs) under the account-admin-untrusted threat model. It states |
| 5 | +the security properties dstack requires from a platform and how the AWS |
| 6 | +NitroTPM path satisfies them in the current implementation. For the |
| 7 | +operational relying-party workflow (verifier deployment, allowlists, key |
| 8 | +release), see `docs/aws-ec2-production-verifier-runbook.md`. |
| 9 | + |
| 10 | +The threat model is: |
| 11 | + |
| 12 | +- Trusted root: AWS Nitro system and NitroTPM attestation signing |
| 13 | + infrastructure. |
| 14 | +- Verified, not blindly trusted: dstack OS images, dstack KMS instances, |
| 15 | + dstack verifier code, and governance policy. The relying party accepts these |
| 16 | + only after checking reproducible build outputs, attested measurements, event |
| 17 | + logs, and policy state. A published AMI ID or a running KMS endpoint is not |
| 18 | + sufficient evidence by itself. |
| 19 | +- Untrusted: the workload AWS account administrator/operator. They may launch, |
| 20 | + stop, replace, snapshot, reconfigure, and network-interpose EC2 resources in |
| 21 | + their account. AWS KMS keys controlled by that account are also untrusted for |
| 22 | + secret authority because the account administrator may be able to change key |
| 23 | + policy, create grants, or route secret-bearing calls through policies they |
| 24 | + control. |
| 25 | +- Out of scope: availability. The operator can always deny service. |
| 26 | + |
| 27 | +## Platform Security Property Spec |
| 28 | + |
| 29 | +The following properties are the readiness gates for any platform claiming the |
| 30 | +same security level as dstack under this threat model, and how the AWS |
| 31 | +NitroTPM path meets them today. |
| 32 | + |
| 33 | +| ID | Required property | dstack mechanism | AWS NitroTPM status | |
| 34 | +| --- | --- | --- | --- | |
| 35 | +| P1 | Verifiable platform root of trust | TDX/SNP/Nitro quote verification against vendor root; debug rejected; TCB surfaced | NitroTPM Attestation Documents are verified against the AWS Nitro Attestation PKI, including document timestamp sanity. AWS exposes no TDX/SNP-style TCB advisory field, so a verified attestation is normalized to `tcbStatus = "UpToDate"` and passes the standard authorization gate unchanged. | |
| 36 | +| P2 | Reproducible or independently computable base image measurement | meta-dstack rebuild plus `dstack-mr` computes `MRTD`/`RTMR0-2` | The unified `os/build.sh` flow emits the AWS image archive with `sha256sum.txt`, `digest.txt`, and `measurement.aws.cbor`; its output directory also contains the reference-PCR side-car `aws-pcrs.json`. `os_image_hash = sha256(sha256sum.txt)` is the same identity used on all platforms; the verifier recomputes it from the downloaded image directory (`dstack/verifier/src/verification.rs`). The hardening audit script `os/yocto/tools/aws/audit-aws-ec2-image-hardening.sh` checks the image for operator mutation channels. | |
| 37 | +| P3 | Boot command line and root filesystem integrity are measured | `RTMR1/2`, rootfs hash, dm-verity, measured initrd/cmdline | The UKI commits kernel, initrd, and embedded cmdline into `PCR4`; the rootfs is dm-verity-protected. `VmConfig.aws_measurement` is required and must bind `boot_pcr_digest = sha256(PCR4||PCR7||PCR12)` to the attested PCRs, so `PCR12` (external cmdline) is always part of the bound digest — a missing-PCR12 bypass is not expressible. Enforced in guest quote generation (`dstack/dstack-attest/src/attestation.rs`), `verify_os_image_hash_for_aws_nitro_tpm` (`dstack/verifier/src/verification.rs`), and the KMS pipeline via the same verifier check. | |
| 38 | +| P4 | Runtime application identity is cryptographically bound | RTMR3 `compose-hash`, `app-id`, `instance-id`, `key-provider`; event log replay | SHA384 `PCR14` event-log replay is the authoritative binding (RTMR3 analogue; non-resettable). Launch events: `system-preparing`, `app-id`, `compose-hash`, `instance-id`, `boot-mr-done`, `key-provider`, `storage-fs`, `system-ready` (`dstack/dstack-util/src/system_setup.rs`). `dstack-attest`, `dstack-verifier`, and KMS reject missing/mismatched PCR14 and bad replay. Optionally, the guest extends the raw `MrConfig` V2 `config_id` into `PCR8` once (`PCR8 = sha384(0^48 || config_id)`) so a lightweight third-party verifier can check compose hash + key provider without event-log replay; dstack's own verifier and KMS do not check PCR8. | |
| 39 | +| P5 | Challenge/liveness and caller key binding | `report_data` challenge or RA-TLS public key hash in quote | RA-TLS binds `report_data` to the TLS certificate public key. KMS key release is bound to the live RA-TLS handshake; external `/verify` callers supply and check their own `report_data` challenge. The low-level NitroTPM document verifier also rejects stale or far-future document timestamps. | |
| 40 | +| P6 | Secret release only to attested code | dstack KMS verifies attestation, checks auth policy, derives per-app keys | dstack KMS verifies the NitroTPM attestation, runs the same `verify_os_image_hash_for_aws_nitro_tpm` binding check as the verifier, builds `BootInfo` from verified boot PCRs plus PCR14 launch events, and checks auth policy before deriving app keys (`dstack/kms/src/main_service.rs`). AWS NitroTPM key release is gated behind the opt-in `aws_nitro_tpm_key_release` flag (default false in `kms.toml`). | |
| 41 | +| P7 | Key-release policy is not controlled by the untrusted account admin | KMS runs inside TEE; policy from auth API/contracts; KMS identity measured | Satisfied with dstack KMS or another verifiable secret authority outside the untrusted AWS account admin's control. A NitroTPM-backed dstack KMS keeps root material out of account-admin snapshots and clones. The policy backend (auth-simple in a trusted control plane, or on-chain `DstackKms`/`DstackApp`) must be outside the workload account admin's control. Same-account AWS KMS fails this property if the admin can change key policy, create grants, or call secret-bearing operations through a policy they control. | |
| 42 | +| P8 | Operator cannot inject boot-time or runtime inputs that affect secrets without detection | Host-shared files are measured or independently authenticated; KMS URLs not trusted, KMS key identity measured | App TPM access is enforced by measurement, not by a container sandbox: `init_script`, `pre_launch_script`, and the docker-compose are measured into the app-compose hash, hence into the governed app identity, and replayed into non-resettable `PCR14`, so an operator cannot alter app inputs without changing the measured identity and being denied key release. Production dstack-os AWS images exclude SSH, cloud-init, SSM, EC2 Instance Connect, and serial login (checked by `audit-aws-ec2-image-hardening.sh`). IMDS/operator-provided config must not be a secret-affecting input unless measured or authenticated. | |
| 43 | +| P9 | Persistent state confidentiality from account admin | dstack disk key derived after attestation; encrypted env vars decrypted only inside CVM | The local TPM key provider seals the disk seed under a SHA384 PCR policy over `[4, 7, 8, 12, 14]` (`AWS_NITRO_PCRS` in `dstack/tpm-attest/src/lib.rs`), so the sealed seed survives same-instance stop/start but does not transfer with cloned EBS volumes to another instance. EBS snapshots and volumes remain account-admin visible at the AWS control plane, so persisted confidential state must stay encrypted inside the instance and AMIs must contain no secrets. | |
| 44 | +| P10 | Network and TLS endpoint identity is attestation-bound | RA-TLS / Zero Trust HTTPS / gateway attestation; TLS keys generated in TEE | The Rust RA-TLS verifier exposes `verify_der`/`verify_pem`, which extract the certificate's embedded attestation, verify it with the platform verifier (including AWS NitroTPM), and require `report_data = QuoteContentType::RaTlsCert(SubjectPublicKeyInfo)`. `dstack-verifier --verify-cert` provides an operator/relying-party certificate evidence path that also binds `os_image_hash`. AWS attestation alone does not protect DNS, load balancers, or admin-controlled proxies; clients must require this RA-TLS check, signed responses, or an attested gateway. | |
| 45 | +| P11 | Upgrade governance is explicit and non-bypassable | DstackApp/DstackKms whitelists compose hashes, OS images, KMS aggregate MRs | auth-simple and the unchanged on-chain `DstackKms`/`DstackApp` contracts pin OS image hash, app compose hash, device ID, and KMS identity. KMS auth pins the early `mrAggregated` (the `boot-mr-done` launch-event snapshot). AWS reuses the standard gate via `tcbStatus = "UpToDate"` normalization with no AWS-specific on-chain field, plus the opt-in `aws_nitro_tpm_key_release` KMS flag. Secure Boot PCR7 certificate policies need explicit rotation/revocation handling so old AMIs do not remain authorized. | |
| 46 | +| P12 | Verifier is independent and complete | `dstack-verifier`, DCAP/QVL, RTMR replay, KMS/app/governance checks | `dstack-verifier` verifies the NitroTPM document against the AWS Nitro PKI, enforces the required `aws_measurement`/`boot_pcr_digest` binding against the unified `os_image_hash`, replays the PCR14 launch-event chain, and emits the canonical auth-policy object as `details.boot_info` for the same policy logic used by `/bootAuth/app` and `/bootAuth/kms`. The relying-party deployment workflow is `docs/aws-ec2-production-verifier-runbook.md`; each deployment must instantiate it with real OS-image, PCR, and compose-hash allowlists. | |
| 47 | + |
| 48 | +## Sources |
| 49 | + |
| 50 | +- AWS EC2 instance attestation: |
| 51 | + https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nitrotpm-attestation.html |
| 52 | +- AWS Attestable AMIs: |
| 53 | + https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/attestable-ami.html |
| 54 | +- AWS custom AMI PCR computation: |
| 55 | + https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-pcr-compute.html |
| 56 | +- AWS NitroTPM Attestation Document: |
| 57 | + https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/attestation-get-doc.html |
| 58 | +- AWS NitroTPM Attestation Document validation: |
| 59 | + https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nitrotpm-attestation-document-validate.html |
| 60 | +- AWS NitroTPM Attestation Document contents: |
| 61 | + https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nitrotpm-attestation-document-content.html |
| 62 | +- AWS KMS attested calls: |
| 63 | + https://docs.aws.amazon.com/kms/latest/developerguide/attested-calls.html |
| 64 | +- AWS NitroTPM samples: |
| 65 | + https://github.com/aws/nitrotpm-attestation-samples |
| 66 | +- AWS advisory on PCR12: |
| 67 | + https://github.com/aws/nitrotpm-attestation-samples/security/advisories/GHSA-xrv8-2pf5-f3q7 |
| 68 | +- dstack security model: `docs/security/security-model.md` |
| 69 | +- dstack TDX attestation guide: `docs/attestation-tdx.md` |
| 70 | +- dstack KMS protocol: `dstack/kms/README.md` |
| 71 | +- dstack Nitro Enclave flow: `docs/attestation-nitro-enclave.md` |
| 72 | +- dstack GCP TDX + TPM flow: `docs/attestation-gcp.md` |
| 73 | +- AWS EC2 production verifier runbook: |
| 74 | + `docs/aws-ec2-production-verifier-runbook.md` |
0 commit comments