Repository navigation
chore: sync upstream guest measurement and verifier updates - #3
Merged
Merged
Conversation
Debian's 90-systemd.preset sorts before 99-dstack-default.preset, so its enable rules shadowed the `disable *` catch-all and enabled units such as systemd-sysext and systemd-confext. Remove non-dstack presets in postinst (mkosi applies presets afterwards) and enable systemd-networkd-wait-online explicitly, since network-online.target relied on the distro preset.
os-image-hash was extended inside the per-URL request, before the CA cert was parsed. If that parse failed, the next KMS URL extended it again and RTMR3 could no longer be replayed. Measure only after a request fully succeeds, and report every KMS's error instead of just the first.
…olicy fix(os): drop foreign systemd presets so dstack's policy is terminal
…easure-once fix(dstack-util): measure os-image-hash once across a KMS failover
The ExecStartPre loop spun forever (TimeoutStartSec=0) once the agent hit its start limit and was parked in failed, wedging the boot with nothing reported to the VMM. Exit on is-failed, bound the wait, and report boot.error to the host via OnFailure.
SystemKeepFree=1G asked for more free space than a 2 GB CVM's /var/log has, and journald folds keep-free into the limit, so the declared 128M budget collapsed to the 16M floor. The System* settings were inert anyway: nothing in the image creates /var/log/journal, so journald has always used /run/log/journal, where RuntimeMaxUse=0 left it with no budget at all.
…quires-prepare fix(os): require dstack-prepare and the guest agent before the app
…siduals docs: explain that gateway_app_id may list several gateway app ids
`storage-fs` is a launch event; `dstack.storage_encrypted` was not, on the grounds (issue Dstack-TEE#618) that the kernel command line is measured into RTMR2. That holds on TDX legacy and TDX lite, which both recompute RTMR2 from the image's base command line, on SEV-SNP, where the command line is hashed into the launch measurement, and on AWS NitroTPM, where PCR12 is inside the bound boot_pcr_digest. It does not hold on GCP TDX: verify_os_image_hash_for_gcp_tdx compares PCR0 and the UKI Authenticode digest out of the PCR2 event log and nothing else -- no RTMR1/RTMR2, and no PCR11/PCR12, where systemd-stub measures a command line handed to it rather than the one inside the UKI. Emit `storage-encrypted` next to `storage-fs` so the answer rides the one channel every platform replays. The event ships with a new os_image_hash, so no deployment sees RTMR3 move without the image moving too.
A relying party cannot tell an omitted instance_id from no_instance_id, so the guest must not skip the check when the host leaves the field out.
…ge-encrypted feat(dstack-util): measure whether the data disk was encrypted
…eanups fix(os): give the guest journal a budget it can satisfy
fix(os): bound docker's wait for the guest agent and report failure
kernel.unprivileged_userns_clone is a Debian/Ubuntu kernel patch. The image builds mainline from kernel.org -- the source whose sha256 versions.env pins has zero occurrences of the name, while every other key in this file is there -- and none of the ten dstack patches adds it. systemd-sysctl logged "Couldn't write '1' to 'kernel/unprivileged_userns_clone', ignoring" at LOG_WARNING on every boot and exited 0, so the unit succeeded and the setting did nothing. Nothing is lost: mainline has no gate, so with CONFIG_USER_NS=y an unprivileged CLONE_NEWUSER is already allowed, which is all sysbox asks for.
…-clone-sysctl fix(os): drop a sysctl the dstack kernel does not have
tboot.service was replaced by dstack-prepare.service in c901eed. 0ff57b2 updated app-compose.service but missed this unit, leaving an After= on a unit that no longer exists. The ordering itself still held via Before=dstack-guest-agent.service in dstack-prepare.service; this makes it visible from the guest agent side too.
`setup_tsm || true` disables `set -e` inside the function, so a failed `mount -t configfs` fell through to `mkdir` under an unmounted directory without any warning. Log the failure and return, matching the existing "configfs not in kernel" case. Boot still continues, since quotes can be obtained through the ioctl fallbacks.
…s-failure fix(os): warn and skip TSM setup when configfs mount fails
…ime-log-level fix(os): stop shipping the NVIDIA container runtime at debug level
…ordering fix(os): order guest agent after dstack-prepare, not tboot
…-checks fix(dstack-util): check an omitted MrConfig instance_id as empty
…on-claim-flags Expose GPU claim status in measured attestation event
`image.verify = false` turns off all OS-image binding at `info!` level, after which `BootInfo.os_image_hash` is whatever the caller put in `GetAppKeyRequest.vm_config` - so an `allowedOsImages` allowlist downstream is gating an attacker-supplied string. `enforce_self_authorization = false` already warns at startup for a comparable relaxation. Warn once at startup on the same terms, drop the per-call line to `debug!`, and report the posture as `GetMeta.os_image_verification` so a relying party can tell which kind of allowlist decision it is reading. The proto field is optional and additive: an older KMS leaves it unset, which must not be read as `true`. No SDK consumes `GetMeta`.
…fo does not decode `is_valid` was the literal `true` for any certificate whose RA-TLS attestation verified, and `decode_app_info(false).ok()` threw the decode error away. A certificate carrying an attestation that verifies but whose app identity does not decode printed a valid result with `app_info: null` -- no app_id, no compose_hash, no os_image_hash -- and exited 0. Report it the way `/verify` does: `is_valid` plus a `reason`, and a non-zero exit. The result file is still written first, so a caller inspecting it sees which check failed.
The field documented itself as verifying RTMR 0-2 digests "through replay comparison with the quote". Nothing in dstack-verifier or dstack-attest replays a boot-time event log: the flag is set once the runtime event log replays to its register and the app identity decodes out of its payloads. A TDX quote's RTMR 0-2 entries reach only the `--debug` diff and the three named ACPI digests the lite path cross-checks. Correct the comment rather than the code. Replaying RTMR 0-2 would add nothing: those registers are already checked against measurements recomputed from the OS image, which does not trust the host's event log at all, and is the stronger of the two checks.
…fy-visibility fix(kms): make a disabled os image verification visible
The debug request flag only fed a development-time RTMR diff (rtmr_debug) and an ACPI table dump (acpi_tables) on the legacy TDX path. The diff was never wired to the event log, so it always reported the whole expected sequence as missing. Nobody relies on it, so drop the flag, both response fields and the code behind them instead of fixing it. VerificationRequest does not deny unknown fields, so clients still sending debug keep working.
With compare_tdx_mrs gone, the private Mrs struct only mirrored dstack_mr::TdxMeasurements field by field. Compare TdxMeasurements directly and share the TD report extraction between the legacy and lite paths. TdxMeasurementDetails.acpi_tables was only read by the removed debug dump, so drop it and let Tdvf::rtmr0_log return just the log.
…-rtmr-diff verifier: remove the debug verification path
…y-cert-validity fix(verifier): stop reporting --verify-cert results valid when app info does not decode
vm_config_cache_key hashed the whole VmConfig, including fields the full-image measurement never reads: an arbitrary `image` string, and the `tdx_measurement`/`gcp_measurement`/`aws_measurement` documents, each carrying a caller-sized checksum_file and CBOR blob. A cache miss costs a full firmware and kernel hash plus ACPI generation and leaves a file under <cache>/measurements/ that nothing evicts, so one captured quote replayed with a different filler byte per request misses every time and grows the cache without bound. Clear those four before hashing. Clearing rather than listing the fields that matter keeps a VmConfig field added later in the key by default, which is the safe direction: an extra miss, never a stale measurement.
compute_measurements hashes the firmware and the kernel and generates the ACPI tables -- pure CPU over a multi-hundred-megabyte image -- and was called synchronously from async fns, so it parked a Rocket worker for the whole computation. tpm-qvl already fetches collateral through spawn_blocking for the same reason. Pass ImagePaths instead of its fields one by one, and run the measurement through spawn_blocking. The cache lookup and store stay on the async side: they are small file operations.
SEV-SNP has no runtime event log (identity comes from mr_config via HOST_DATA), Nitro Enclave and GCP TDX were missing, and the replay runs during quote verification, so a mismatch fails quote_verified. The --debug diff does not read event log entries, and the lite ACPI digest check is fatal rather than diagnostic.
…rement-cache fix(verifier): key the measurement cache on the measured VM shape only
…re-off-runtime refactor(verifier): measure the OS image on a blocking thread
…t-log-verified docs(verifier): say what event_log_verified actually covers
79 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sync upstream
Dstack-TEE/dstack@59fee205b5b672448d794824bd1b64624c16b747intoalpha@95fe9395903d748902ef0e38711b024a81202863. Imports 43 new upstream commits without conflicts, preserving the fork’s encrypted-environment manifest support, legacy ciphertext handling, Yocto compatibility and owner-scoped release publishing.The delta measures encrypted-storage and GPU-claim status, fixes KMS-failover measurement and omitted instance identity handling, orders guest preparation before application startup, bounds Docker startup waits, and corrects verifier certificate validity, measurement caching and event-log documentation. The removed verifier debug response fields are diagnostic; consumers must continue checking quote, app identity and OS-image verification according to the platform-specific contract.
Validation: Rust workspace formatting and merge-diff whitespace checks passed. All seven release build-orchestration and namespace contract tests passed with GNU coreutils (6.557 seconds). Linux-hosted Rust, image and platform checks are required before merge; macOS cannot establish the Linux-only SEV/guest-image behavior. No enclave, KMS policy, encrypted volume or production service is changed by this PR. Hardware acceptance and recovery remain separate requirements.
Full-image follow-up: run35831327742 at0162f776 passed the actual guest-image reproducibility comparison and produced the image archives, but the subsequent module-builder container failed apt dependency resolution. Its floating Debian root had newer libc/SSL/Perl libraries than the pinned July snapshot’s development packages.
Commit54c78ba628cd464da723639f996a7f8477df22d8 pins the module-builder base to the repository’s verified July13 Debian image digest, retains the July21 package snapshot, and checks the suite against the requested release. Build.sh consumes the immutable pin from versions.env. Tracked by elizaOS/eliza#32279 because this fork has issues disabled.
Rust formatting, shell syntax, whitespace and all seven release contracts pass after the fix. Local static acceptance reaches a pre-existing GNU-sed expression unsupported by this macOS environment; the Linux static job remains required. No local Docker daemon is available, so the actual Docker build is delegated to the non-publishing workflow. New full reproducibility run35839398931 at54c78ba6 is pending; do not infer its result from the previous guest-only success. No guest image, KMS policy or production service is deployed.
Final qualification at54c78ba628cd464da723639f996a7f8477df22d8: full image run35839398931 completed SUCCESS. Static contract, guest images and kernel module builder all passed. Guest logs explicitly report
reproducibility check passedat2026-09-23T10:20:13Z. Publish release was intentionally skipped; no release tag/publication is claimed. All ordinary PR checks also passed. Native driver build emitted objtool warnings; successful image construction/reproducibility is not proof of supported GPU hardware deployment or protected links. Real TDX/Nitro key release, multi-CVM isolation and recovery acceptance remain separate.