Skip to content

Commit 0e6295d

Browse files
committed
docs(aws): align verifier guidance with release flow
Trim the superseded security evaluation to the live threat model and property table, document the actual UKI archive and PCR sidecar layout, and remove stale recipient/fallback claims. Also simplify the certificate output and build fragments referenced by the updated runbook.
1 parent aaa9f7e commit 0e6295d

13 files changed

Lines changed: 93 additions & 1444 deletions

File tree

docs/aws-attested-instance-security-evaluation.md

Lines changed: 30 additions & 1366 deletions
Large diffs are not rendered by default.

docs/aws-ec2-production-verifier-runbook.md

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ deployments under the account-admin-untrusted threat model.
55

66
The relying party must not trust an AMI ID, DNS name, load balancer, AWS KMS
77
key, or dstack KMS endpoint by itself. It should accept a workload only after
8-
checking the release evidence, NitroTPM evidence freshness, policy `BootInfo`,
8+
checking the release evidence, NitroTPM challenge binding, policy `BootInfo`,
99
and endpoint identity.
1010

1111
## Inputs
1212

1313
- The release image package for the exact release candidate, produced by the
1414
unified build entrypoint `os/build.sh` (the `<name>-<version>-uki.tar.gz`
15-
dist archive). It contains the UKI, `sha256sum.txt`, `digest.txt`,
16-
`metadata.json`, `measurement.aws.cbor`, and the `aws-pcrs.json` side-car.
15+
dist archive). It contains `disk.raw`, `sha256sum.txt`, `digest.txt`, and
16+
`measurement.{gcp,aws}.cbor`. The build output also contains an
17+
`aws-pcrs.json` side-car, but that file is not part of the archive.
1718
- The dstack monorepo sources pinned at the exact release revision, for the
1819
reproducible rebuild.
1920
- A dstack verifier built from this repository.
@@ -36,12 +37,21 @@ git status --porcelain # must be empty at the pinned release revision
3637
./os/build.sh # reproducible Yocto backend; emits images/<name>-<version>-uki.tar.gz
3738
```
3839

39-
Compare the rebuilt package against the published one:
40+
Compare the rebuilt package against the published one. `sha256sum.txt` lists
41+
the full build inputs, not only the files in the UKI archive, so do not run
42+
`sha256sum -c` inside the extracted UKI package:
4043

4144
```bash
42-
tar -xzf <name>-<version>-uki.tar.gz
43-
sha256sum -c sha256sum.txt # artifacts match their recorded hashes
44-
sha256sum sha256sum.txt # os_image_hash; must equal digest.txt and the rebuilt value
45+
mkdir published rebuilt
46+
tar -xzf published-<name>-<version>-uki.tar.gz -C published --strip-components=1
47+
tar -xzf rebuilt-<name>-<version>-uki.tar.gz -C rebuilt --strip-components=1
48+
49+
cmp published/sha256sum.txt rebuilt/sha256sum.txt
50+
cmp published/measurement.aws.cbor rebuilt/measurement.aws.cbor
51+
cmp published/disk.raw rebuilt/disk.raw
52+
53+
expected=$(sha256sum published/sha256sum.txt | awk '{print $1}')
54+
test "$expected" = "$(cat published/digest.txt)"
4555
```
4656

4757
BitBake enforces per-recipe input checksums during the rebuild; for full
@@ -60,9 +70,9 @@ os/yocto/tools/aws/audit-aws-ec2-image-hardening.sh \
6070

6171
## 2. Register the AMI and deploy with `dstack-cloud`
6272

63-
After the release artifact hashes and AWS PCR references match the release
64-
package, deploy with **`dstack-cloud`** (`platform: aws`). The CLI imports the
65-
local UKI `disk.raw` as an Attestable AMI (UEFI + NitroTPM v2.0) when
73+
After the release artifact hashes and AWS PCR references match the published
74+
release evidence, deploy with **`dstack-cloud`** (`platform: aws`). The CLI
75+
imports the local UKI `disk.raw` as an Attestable AMI (UEFI + NitroTPM v2.0) when
6676
`aws_config.ami_id` is empty, builds the shared config disk with
6777
`aws_measurement` from `measurement.aws.cbor`, and launches the instance.
6878

@@ -73,7 +83,7 @@ export PATH="$PATH:$(pwd)/dstack/scripts/bin"
7383
# project for AWS
7484
dstack-cloud new my-aws-app --platform aws --region us-west-2
7585
cd my-aws-app
76-
# edit dstack-app.json: aws_config.s3_bucket (vmimport), subnet/security groups as needed
86+
# edit app.json: aws_config.s3_bucket (vmimport), subnet/security groups as needed
7787
# pull or place the UKI package (must include measurement.aws.cbor) under image_search_paths
7888

7989
dstack-cloud pull dstack-0.6.0 # UKI package must include assemble-time measurement.aws.cbor
@@ -93,11 +103,15 @@ toolchain for reference measurements):
93103

94104
```bash
95105
cargo install --git https://github.com/aws/NitroTPM-Tools --locked nitro-tpm-pcr-compute
106+
# The UKI is EFI/BOOT/BOOTX64.EFI in the 256 MiB EFI partition at 1 MiB.
107+
dd if=published/disk.raw of=efi.img bs=1M skip=1 count=256 status=none
108+
mcopy -i efi.img ::EFI/BOOT/BOOTX64.EFI dstack-uki.efi
96109
nitro-tpm-pcr-compute --image dstack-uki.efi
97110
pesign -h -P -i dstack-uki.efi # UKI AuthentiCode SHA256
98111
```
99112

100-
The PCRs must match `aws-pcrs.json` in the release package, and their digest
113+
The PCRs must match the build-output `aws-pcrs.json` side-car when it is
114+
published with the release, and their digest
101115
`sha256(PCR4 || PCR7 || PCR12)` must equal the `boot_pcr_digest` committed in
102116
`measurement.aws.cbor`. For the generic hardened UKI this is the boot PCR set
103117
plus the UKI hash:
@@ -129,8 +143,7 @@ expected MrConfig V2 config id: <mr-config-id>
129143

130144
Challenge binding uses **report_data → NitroTPM user_data** (same role as
131145
TDX/GCP): the relying party embeds its own challenge in `report_data` and
132-
checks it against the returned `boot_info`. There is no GetAppKey recipient
133-
`public_key` / v2 encryption path.
146+
checks it against `details.report_data` in the verifier result.
134147

135148
Example verifier request shape:
136149

@@ -150,11 +163,16 @@ Require:
150163

151164
```bash
152165
# from the release package: os_image_hash = sha256(sha256sum.txt)
153-
expected_os_image_hash=$(sha256sum sha256sum.txt | awk '{print $1}')
166+
expected_os_image_hash=$(sha256sum published/sha256sum.txt | awk '{print $1}')
167+
# exact 64-byte challenge used when collecting this attestation, as hex
168+
expected_report_data="<128-hex-characters>"
154169

155-
jq -e --arg expected_os_image_hash "$expected_os_image_hash" '
170+
jq -e \
171+
--arg expected_os_image_hash "$expected_os_image_hash" \
172+
--arg expected_report_data "$expected_report_data" '
156173
.is_valid == true and
157174
.details.quote_verified == true and
175+
.details.report_data == $expected_report_data and
158176
.details.boot_info.attestationMode == "dstack-aws-nitro-tpm" and
159177
.details.boot_info.tcbStatus == "UpToDate" and
160178
.details.boot_info.osImageHash == $expected_os_image_hash
@@ -177,7 +195,7 @@ For AWS NitroTPM, the policy must require:
177195
is required and must bind the attested boot PCRs);
178196
- accepted app `composeHash` and `appId`;
179197
- accepted KMS identity via **early `mrAggregated`** (boot-mr-done), same as
180-
bare TDX — not `kms.composeHashes`;
198+
bare TDX;
181199
- verified PCR14 event-log replay (single event lane; no PCR23 runtime split) —
182200
this is the authoritative app-identity binding (`composeHash`, `appId`,
183201
`instance-id`, `key-provider`);
@@ -227,7 +245,6 @@ Require the generated `endpoint-cert.pem.ratls-verification.json` to contain:
227245
```bash
228246
jq -e '
229247
.is_valid == true and
230-
.details.endpoint_identity_verified == true and
231248
.details.attestation_mode == "dstack-aws-nitro-tpm" and
232249
.details.app_info.os_image_hash_verified == true
233250
' endpoint-cert.pem.ratls-verification.json
@@ -248,12 +265,13 @@ An AWS EC2 NitroTPM deployment can be promoted only when:
248265
1. a reproducible rebuild from the clean, pinned release sources yields a
249266
byte-identical `sha256sum.txt` (and therefore the same `os_image_hash`);
250267
2. Yocto input content is mirrored or otherwise available by content hash;
251-
3. independently recomputed AWS PCRs and the UKI AuthentiCode hash match
252-
`aws-pcrs.json` and the `boot_pcr_digest` in `measurement.aws.cbor`;
268+
3. independently recomputed AWS PCRs and the UKI AuthentiCode hash match the
269+
published build-output `aws-pcrs.json` side-car (when provided) and the
270+
`boot_pcr_digest` in `measurement.aws.cbor`;
253271
4. the hardening audit has zero failures and zero warnings, or every warning has
254272
a documented release exception;
255273
5. the registered AMI has a live EC2 smoke record for the exact AMI ID and
256274
root snapshot in the release review package;
257275
6. `/verify` returns a valid AWS `BootInfo`;
258-
7. auth policy accepts only the intended OS, app, KMS, and recipient-key state;
276+
7. auth policy accepts only the intended OS, app, and KMS state;
259277
8. endpoint identity is RA-TLS, signed-response, or attested-gateway bound.

docs/quickstart.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ These are encrypted before leaving your machine and only decrypted inside the TE
115115
Deploy to your cloud provider:
116116

117117
```bash
118+
# AWS without a preconfigured aws_config.ami_id needs the local UKI package first:
119+
# dstack-cloud pull <release-image-name>
118120
dstack-cloud deploy
119121
```
120122

docs/security/security-model.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Use this checklist to verify a workload running in a dstack CVM.
153153
- [ ] compose-hash matches your docker-compose
154154
- [ ] All images use SHA256 digests (no mutable tags)
155155
- [ ] Launch event log replays correctly (RTMR3 on TDX-family platforms, PCR14 on AWS NitroTPM)
156-
- [ ] Config commitment matches the expected app/config target (TDX `mr_config_id` / SEV `HOST_DATA`; on AWS NitroTPM the app/config identity is bound by PCR14 event-log replay, and the guest-computed `MrConfig` V2 PCR8 commitment is only an optional shortcut for verifiers that skip that replay)
156+
- [ ] Config commitment matches the expected app/config target (on AWS: PCR14 replay; PCR8 is an optional shortcut — see the [AWS verifier runbook](../aws-ec2-production-verifier-runbook.md))
157157
- [ ] reportData contains your challenge (replay protection)
158158

159159
**Key management verification:**

docs/tutorials/attestation-verification.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ The workflow below uses TDX names because the hello-world tutorial deploys a TDX
9090
| AMD SEV-SNP | SNP report fields and measured config ID | `MrConfigV3` app/config target |
9191
| AWS EC2 NitroTPM | NitroTPM Attestation Document, AWS NitroTPM PKI, PCR4/PCR7/PCR12, OS image hash | SHA384 PCR14 launch event log through `system-ready` |
9292

93-
For AWS EC2 NitroTPM, PCR23 is not the launch authorization register. It is resettable from the guest and is reserved for optional post-launch runtime telemetry. AWS verifiers must check PCR14 replay (the authoritative app-identity binding) and the recipient public key when key material is returned to the instance. The PCR8 `MrConfig` V2 config commitment is an optional shortcut for lightweight verifiers that would rather recompute the expected PCR8 from the known compose hash and key-provider than replay the PCR14 event log; dstack's own verifier does not check it.
93+
For the authoritative PCR14 replay and optional PCR8 shortcut, see the [AWS production verifier runbook](../aws-ec2-production-verifier-runbook.md).
9494

9595
## Understanding RA-TLS
9696

@@ -784,7 +784,7 @@ OVMF VM Config Kernel Initrd App
784784

785785
Each register builds on the previous one. A compromised kernel (RTMR1) could fake application measurements (RTMR3), so always verify from the firmware up.
786786

787-
On AWS EC2 NitroTPM, the equivalent production chain is: verify the NitroTPM Attestation Document signature and AWS NitroTPM PKI, check the expected boot PCRs and OS image hash, replay the PCR14 launch event log, and bind any released key material to the attested recipient public key. (Optionally, a verifier that skips PCR14 event-log replay can instead recompute and check the PCR8 `MrConfig` V2 config commitment for the compose hash and key-provider.)
787+
On AWS EC2 NitroTPM, verify the AWS NitroTPM PKI, expected boot PCRs and OS image hash, then replay PCR14. See the [AWS production verifier runbook](../aws-ec2-production-verifier-runbook.md).
788788

789789
### 4. Keep expected measurements updated
790790

docs/verification.md

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,12 @@ by platform.
3232
| --- | --- | --- | --- |
3333
| TDX-family dstack CVM | MRTD and RTMR0-2 | RTMR3 event log | `report_data` or RA-TLS certificate binding |
3434
| AMD SEV-SNP | SNP report fields plus `HOST_DATA`/config ID | `MrConfigV3` app/config target | report data or RA-TLS certificate binding |
35-
| AWS EC2 NitroTPM | NitroTPM Attestation Document, AWS NitroTPM PKI, PCR4/PCR7/PCR12, OS image hash | SHA384 PCR14 launch event log through `system-ready` (authoritative; the PCR8 `MrConfig` V2 config commitment is an optional shortcut for verifiers that skip event-log replay) | NitroTPM `nonce`, `user_data`, `public_key`, or RA-TLS certificate binding |
36-
37-
For AWS EC2 NitroTPM, do not treat PCR23 as the launch authorization register.
38-
PCR23 is resettable from the guest and is reserved for optional post-launch
39-
runtime telemetry. Production AWS policy must require the NitroTPM attestation
40-
mode, valid AWS NitroTPM signature chain, expected boot PCRs, expected OS image
41-
hash, verified PCR14 replay, and the recipient public key when key material is
42-
returned to the instance. The PCR8 `MrConfig` V2 config commitment is an
43-
optional shortcut: a verifier that does not want to replay the PCR14 event log
44-
may instead recompute the expected PCR8 from the known compose hash and
45-
key-provider and compare it. It is not required, and dstack's own verifier does
46-
not check it.
35+
| AWS EC2 NitroTPM | NitroTPM Attestation Document, AWS NitroTPM PKI, PCR4/PCR7/PCR12, OS image hash | SHA384 PCR14 launch event log through `system-ready` | NitroTPM `user_data` challenge or RA-TLS certificate binding |
36+
37+
On AWS, PCR14 replay is the authoritative application-identity check; PCR8 is
38+
an optional shortcut for third-party verifiers. See the
39+
[AWS production verifier runbook](./aws-ec2-production-verifier-runbook.md)
40+
for the complete platform policy.
4741

4842
## Learn More
4943

dstack/kms/auth-simple/README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,8 @@ that early MR is precomputable.
8888

8989
The verifier that calls auth-simple must already have verified the NitroTPM
9090
Attestation Document, AWS NitroTPM PKI chain, boot PCRs, PCR14 launch-event
91-
replay (the authoritative app-identity binding), and any recipient public key
92-
used for encrypted key release. The PCR8 `MrConfig` V2 config commitment is an
93-
optional shortcut for verifiers that skip PCR14 replay, not a required check.
91+
replay, and OS image binding. PCR8 is an optional third-party shortcut, not a
92+
required dstack check; see `docs/aws-ec2-production-verifier-runbook.md`.
9493
auth-simple authorizes the resulting canonical `BootInfo`; it does not verify
9594
raw NitroTPM evidence by itself.
9695

@@ -208,9 +207,8 @@ KMS boot authorization.
208207
1. `tcbStatus` must be listed in `allowedTcbStatuses` (default: only `"UpToDate"`). AWS NitroTPM is normalized to `"UpToDate"` by the KMS before it reaches auth-simple.
209208
2. Every `advisoryIds` entry must be listed in `allowedAdvisoryIds` (default: none allowed)
210209
3. `osImageHash` must be in `osImages` array
211-
4. At least one KMS identity allowlist must be configured:
212-
- `kms.mrAggregated` early/boot-mr-done aggregate MR (required)
213-
6. `deviceId` must be in `kms.devices` unless `allowAnyDevice` is true
210+
4. `mrAggregated` must be in the `kms.mrAggregated` early/boot-mr-done allowlist
211+
5. `deviceId` must be in `kms.devices` unless `allowAnyDevice` is true
214212

215213
### App Boot Validation
216214

dstack/nsm-qvl/tests/nitro_attestation.README.md

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,6 @@ with `TEST_FETCH_CRL` set (that path reaches AWS CRL endpoints over the network)
3939
- The exact capture command, host operator, and the enclave workload were **not
4040
recorded** in-repo — treat those as unknown.
4141

42-
## Verified values (informational)
43-
44-
```
45-
module_id: i-0827e799ec9232d44-enc019b55a68bd2ed4b
46-
instance: i-0827e799ec9232d44 (region us-east-1)
47-
digest: SHA384
48-
signature: ECDSA P-384 / SHA-384 (COSE alg -35 / ES384, 96 bytes)
49-
cert window: 2025-12-25T13:15:34Z .. 2025-12-25T16:15:37Z (short-lived NSM cert)
50-
root CA: CN=aws.nitro-enclaves (G1), valid 2019-10-28 .. 2049-10-28
51-
```
52-
5342
## Refreshing
5443

5544
NSM leaf certificates expire a few hours after issuance, so this fixture's

dstack/verifier/src/main.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,11 @@ async fn run_cert_oneshot(file_path: &str, config: &Config) -> anyhow::Result<()
182182
let output = serde_json::json!({
183183
"is_valid": true,
184184
"details": {
185-
"endpoint_identity_verified": true,
186185
"attestation_mode": verified.attestation.quote.mode(),
187-
"tee_platform": verified.attestation.quote.mode().as_str(),
188186
"report_data": hex::encode(verified.attestation.report_data),
189187
"public_key_der": hex::encode(&verified.public_key_der),
190188
"app_id_extension": verified.app_id.as_ref().map(hex::encode),
191189
"special_usage": verified.special_usage,
192-
"app_info_extension_present": verified.app_info.is_some(),
193190
"app_info": app_info.map(|info| serde_json::json!({
194191
"app_id": hex::encode(info.app_id),
195192
"compose_hash": hex::encode(info.compose_hash),

os/image/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ Both are listed in `sha256sum.txt`, so
2020
Deploy tooling (`dstack-cloud prepare`) only **embeds** these files into
2121
`VmConfig`; it must not recompute PCRs (that would change the image identity).
2222

23-
AWS PCR precompute prefers a host `nitro-tpm-pcr-compute` binary (Rust,
23+
AWS PCR precompute requires a pinned host `nitro-tpm-pcr-compute` binary (Rust,
2424
[aws/NitroTPM-Tools](https://github.com/aws/NitroTPM-Tools)). Set
25-
`NITRO_TPM_PCR_COMPUTE_BIN` or install it on `PATH`. If missing, assemble falls
26-
back to Docker `amazonlinux:2023` + `aws-nitro-tpm-tools`; if neither is
27-
available, UKI assemble **fails** (no silent skip).
25+
`NITRO_TPM_PCR_COMPUTE_BIN` or install it on `PATH`, for example with
26+
`cargo install --git https://github.com/aws/NitroTPM-Tools --locked nitro-tpm-pcr-compute`.
27+
If it is missing, UKI assembly fails.
2828

2929
`mk-image-mr.sh <release.tar.gz>` creates the flattened, rootfs-free
3030
`mr_<digest>.tar.gz` bundle consumed by verifier/KMS image-download endpoints.

0 commit comments

Comments
 (0)