feat(kms): make RPC certificate attestation configurable - #830
Open
kvinwang wants to merge 2 commits into
Open
Conversation
kvinwang
force-pushed
the
fix/kms-unattested-rpc-cert
branch
from
July 28, 2026 03:14
46eb149 to
07e1f3a
Compare
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
The KMS cannot run outside a TEE, and two tools plus two security documents assume it can.
Keys::from_keysembeds a quote in the KMS RPC certificate unconditionally, so without a reachable dstack guest agent the KMS fails to bootstrap. The switch that used to control this,[core.onboard] quote_enabled, no longer exists — there are zero.rsreferences to it — but it was never cleaned up:tools/dev-stack.shstill writesquote_enabled = falseinto the KMS config it generates, andrun_in_dstack = falseinto the gateway config. serde ignores unknown keys, so both lines are silently dropped and the tool cannot start either component. It describes itself as a "Legacy all-in-one host development helper", so this is exactly its stated use case.docs/security/security-model.mdlistsquote_enabled = falseas a current development switch.docs/security/security-best-practices.mdtells operators not to deploy production KMS with it.The gateway already solved this problem:
[core.debug] insecure_skip_attestationswitches certificate generation off the guest agent, andgen_debug_keyexists to set it up. The KMS simply never got the equivalent, so what looks like a deliberate stance ("KMS must be in a TEE") is an omission.The failure is also badly misleading. Without the guest agent the KMS reports
Failed to get quote, and a CVM booting against it reports:which reads as "the KMS rejected this CVM's quote". It is the opposite — the CVM is rejecting the KMS's quote (the direction is now named explicitly by #829).
Fix
Add
[core] attest_rpc_cert, defaulttrue.It sits next to
enforce_self_authorization, which is the KMS's own existing knob for the same situation —[core], no prefix, positive default, and documented as "Setfalseonly for local dev/testing where the KMS runs outside a TEE and cannot reach a guest agent socket."Deliberately not named
insecure_*. Every existinginsecure_*in the repo makes some party accept something it would otherwise reject:insecure_no_authserves the admin API unauthenticated,insecure_allow_external_trust_anchorswidens the trust roots, the gateway'sinsecure_skip_attestationdrops peer verification inwavekv_sync.rs,insecure_enable_debug_rpcexposes an RPC that lets a caller claim any app id. This switch does none of that — it makes the KMS assert less about itself, and relaxes no verification on either side. Copying the gateway's name would also have been actively misleading here: in a KMS, "skip attestation" reads as "stop verifying the quotes CVMs present", which is exactly what must never happen.The two directions are cleanly separable in the KMS, and only the first is touched:
app_attestatonboard_service.rs— this PR gates only the call that embeds it in the RPC certificatemain_service::ensure_app_attestation_allowed→attestation_verifier— untouched, still gates all key releaseThe blast radius is bounded by behaviour that already exists:
if let Some(att)insystem_setup.rs) but then do not extendmr-kms, so a remote verifier can still tell;bail!("Source KMS certificate does not contain attestation").Also in this change:
warn!when it is off, stating the consequence rather than shouting "insecure";update_certsnow logs that it reissued the certificate. It rewrites the RPC certificate on every start, which silently replaces a hand-placed one — I lost time to exactly that while investigating, because the success path said nothing;#609in the public report log annotated to record thatquote_enabledwas retired;attest_rpc_cert = falseandinsecure_skip_attestation = truein rendered manifests;tools/dev-stack.shfixed on both halves: the new switch for KMS, and[core.debug] insecure_skip_attestation = truefor the gateway in place of the deadrun_in_dstack.The
quote_enabledreferences undertest-suites/full-stack-compose/are left alone: that suite deploys KMS 0.5.8 alongside current KMS for upgrade coverage, and its own comment already says# Required by KMS 0.5.8. Current KMS ignores this retired field.Verification
Fail-closed by default, outside a TEE, with no
[core.debug]section at all:With the switch on, same host, no TEE:
The certificate it produces carries one dstack extension (
special_usage) instead of two — the attestation extension is absent:Separately, on a TDX host, a real CVM booted successfully against a KMS running outside the TEE with an unattested certificate and registered with the gateway over WireGuard, which is what established that
mr-kmsis genuinely optional on the guest side.cargo test -p dstack-kms38 passed (2 new: the default must be attested, and a config written before this key existed must still parse onto the attested side).cargo fmtandcargo clippy -p dstack-kmsclean.