fix(kms): ship CA certificates in the KMS image - #1128
Merged
Conversation
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.
Problem
dstack-kmsdoes not start in its own published image. Runningdstacktee/dstack-kms(herecr.kvin.wang/dstack/dstack-kms:0.6.0-rc0, built fromcb4c3736c5) with a stock config dies during onboarding, before it serves anything:The failure is not about attestation trust anchors — those are already compiled in and behave correctly.
AttestationVerifier::loadfalls back to the built-in Intel root and AMD ARKs when no override is configured (dstack/dstack-attest/src/attestation.rs:96-99,:110). What is missing is the web PKI root store used for the TLS transport to the collateral endpoints.The chain:
AttestationVerifier::loadeagerly constructs both collateral clients —CollateralClient::with_default_http(pccs)andAmdKdsClient::with_base_url(amd_kds)(dstack/dstack-attest/src/attestation.rs:147,152).AmdKdsClient::with_base_urlbuilds areqwest::Client(dstack/sev-snp-qvl/src/lib.rs:317-321).reqwestwithdefault-features = falseand therustlsfeature (dstack/Cargo.toml:214-220), which pulls inrustls-platform-verifier.Cargo.lockhasrustls-native-certsand nowebpki-roots, so the only root source is the system trust store.rustls-platform-verifierreturnsErrwhen that store comes up empty (rustls-platform-verifier-0.7.0/src/verification/others.rs:104-108).debian:bookwormplus the binary, and the Debian base image ships noca-certificates.Two details make this worse than it looks. reqwest fails when the client is built, not when a request is made, so the process dies even though nothing has tried to reach the network yet. And the client that kills it is the AMD KDS one — an Intel TDX deployment that will never talk to AMD KDS still cannot boot.
The sibling image already gets this right:
dstack-gatewayinstallsca-certificatesin its runtime stage (dstack/gateway/dstack-app/builder/Dockerfile:40) and verifies a runtime package list in CI. The KMS had neither, andgit log -S ca-certificates -- dstack/kms/dstack-app/builder/Dockerfileis empty, so it has been this way since the image was introduced rather than being a recent regression.Fix
Install
ca-certificatesin the KMS runtime stage, following the gateway's pattern exactly rather than inventing a second one:pin-packages.shagainst a runtimepinned-packages.txtand installca-certificatesfrom the same frozen Debian snapshot the gateway uses.WORKDIRis restored to/afterwards so the image's working directory is unchanged.build-image.sh— extract the runtime package list too, mirroringgateway/dstack-app/builder/build-image.sh:31,33. Without this the new list is never regenerated andcheck_clean_treecannot notice drift.docker-build-check.yml— verify the runtime list for the KMS image. CI previously checked only the KMS builder list while checking both for the gateway; that gap is part of why this went unnoticed.shared/pinned-packages.txt— generated bybuild-image.shfrom the built image, not hand-written. It resolvesca-certificates=20230311+deb12u1, the same version the gateway already pins, so both images stay on one snapshot.This keeps the image reproducible: the package set is pinned to exact versions from the frozen snapshot, and CI now fails if it drifts.
Fixing it in the image rather than the code is the minimal change that makes the published artifact work and brings the two images into line. Bundling
webpki-rootsinto the binary, or constructing the collateral clients lazily so an Intel-only deployment never builds the AMD one, are both defensible and orthogonal — worth considering separately, but neither is needed to make the image boot.Verification
A/B on one host, same config, neither container given a host CA mount — the only variable is the image:
dstack-kms:ca-fix)Up,rocket::rkt: endpoint=https://0.0.0.0:8000 (TCP)0.6.0-rc0(control)Exited (1),No CA certificates were loaded from the systemThe control run is what pins the cause to this change rather than to some other difference in the environment.
Beyond starting, the fixed image completes onboarding and serves RPC:
and writes the expected key material —
root-ca.crt,root-ca.key,rpc.crt,rpc.key,rpc-domain.Image contents, before and after:
216591 bytes is byte-identical in size to the bundle the gateway image already ships, as expected from the shared snapshot and pin.
Both
build-image.shruns completed withcheck_clean_treeclean on the second pass, confirming the generatedpinned-packages.txtis stable.