diff --git a/docs/tutorials/attestation-verification.md b/docs/tutorials/attestation-verification.md index ea1cb1180..65910c5c8 100644 --- a/docs/tutorials/attestation-verification.md +++ b/docs/tutorials/attestation-verification.md @@ -526,7 +526,6 @@ These are the standard events you'll see in the log: | `gpu-attestation` | Verified GPU state and digest of the boot-time `nvattest` JSON | Required for an attested GPU launch; verify as described below | | `instance-id` | Unique instance identifier | Should match `instance_id` from response | | `boot-mr-done` | Boot measurements complete | Marker event | -| `mr-kms` | KMS identity measurement | KMS public key hash | | `os-image-hash` | Guest OS image hash | Should match `tcb_info.os_image_hash` | | `key-provider` | Key provider type | e.g., `kms` | | `storage-fs` | Storage filesystem type | Storage configuration | diff --git a/dstack/dstack-util/src/system_setup.rs b/dstack/dstack-util/src/system_setup.rs index ff747400b..8283725aa 100644 --- a/dstack/dstack-util/src/system_setup.rs +++ b/dstack/dstack-util/src/system_setup.rs @@ -95,12 +95,6 @@ async fn sign_cert_request( mod config_id_verifier; -fn is_unsupported_app_info_quote(err: &anyhow::Error) -> bool { - let message = format!("{err:#}"); - message.contains("Unsupported attestation quote") - || message.contains("unsupported attestation quote for app info decoding") -} - #[derive(clap::Parser)] /// Prepare full disk encryption pub struct SetupArgs { @@ -1993,6 +1987,25 @@ pub async fn cmd_gateway_refresh(args: GatewayRefreshArgs) -> Result<()> { .await } +/// Accept only a certificate the KMS issued for its own RPC endpoint. +/// +/// The attestation behind this certificate is already verified by the RA-TLS +/// layer, and the KMS identity that matters to the guest is its CA public key, +/// pinned separately by `verify_key_provider_id`. All that is left here is +/// refusing a certificate minted for some other purpose. +fn validate_kms_rpc_cert(cert: Option) -> Result<()> { + let Some(cert) = cert else { + bail!("missing server cert"); + }; + let Some(usage) = cert.special_usage else { + bail!("missing server cert usage"); + }; + if usage != "kms:rpc" { + bail!("Invalid server cert usage: {usage}"); + } + Ok(()) +} + struct AppIdValidator { allowed_app_id: String, } @@ -2093,8 +2106,6 @@ impl<'a> Stage0<'a> { }; let cert_pair = generate_ra_cert(tmp_ca.temp_ca_cert.clone(), tmp_ca.temp_ca_key.clone())?; let attestation_verifier = attestation_verifier(&self.shared.sys_config)?; - let verified_kms_measurement = Arc::new(std::sync::Mutex::new(None::<[u8; 32]>)); - let captured_kms_measurement = verified_kms_measurement.clone(); let ra_client = RaClientConfig::builder() .tls_no_check(false) .tls_built_in_root_certs(false) @@ -2103,32 +2114,7 @@ impl<'a> Stage0<'a> { .tls_client_key(cert_pair.key_pem) .tls_ca_cert(tmp_ca.ca_cert.clone()) .attestation_verifier(attestation_verifier) - .cert_validator(Box::new(move |cert| { - let Some(cert) = cert else { - bail!("Missing server cert"); - }; - let Some(usage) = cert.special_usage else { - bail!("Missing server cert usage"); - }; - if usage != "kms:rpc" { - bail!("Invalid server cert usage: {usage}"); - } - if let Some(att) = &cert.attestation { - match att.decode_app_info(false) { - Ok(kms_info) => { - *captured_kms_measurement - .lock() - .map_err(|_| anyhow!("KMS measurement capture lock poisoned"))? = - Some(kms_info.mr_aggregated); - } - Err(err) if is_unsupported_app_info_quote(&err) => { - warn!("Skipping mr-kms runtime event for unsupported attestation quote: {err:#}"); - } - Err(err) => return Err(err).context("Failed to decode app_info"), - } - } - Ok(()) - })) + .cert_validator(Box::new(validate_kms_rpc_cert)) .build() .into_client() .context("Failed to create client")?; @@ -2141,14 +2127,6 @@ impl<'a> Stage0<'a> { .await .context("Failed to get app key")?; - let kms_measurement = verified_kms_measurement - .lock() - .map_err(|_| anyhow!("KMS measurement capture lock poisoned"))? - .take(); - if let Some(kms_measurement) = kms_measurement { - emit_runtime_event("mr-kms", &kms_measurement) - .context("failed to extend mr-kms to the launch measurement")?; - } emit_runtime_event("os-image-hash", &response.os_image_hash) .context("failed to extend os-image-hash to the launch measurement")?; diff --git a/dstack/kms/kms.toml b/dstack/kms/kms.toml index 08e954662..935fdbd7b 100644 --- a/dstack/kms/kms.toml +++ b/dstack/kms/kms.toml @@ -34,9 +34,8 @@ enforce_self_authorization = true # Whether the KMS embeds an attestation in its own RPC certificate. Set false # only for local dev/testing where the KMS runs outside a TEE. This narrows what # the KMS asserts about itself and relaxes no verification: quotes presented to -# the KMS are still fully checked, a guest that accepts an unattested -# certificate simply does not extend mr-kms, and another KMS refuses to onboard -# from one. +# the KMS are still fully checked, and another KMS refuses to onboard from an +# unattested one. attest_rpc_cert = true # AMD SEV-SNP key/cert release remains disabled unless this local KMS gate is # explicitly enabled. External auth policy must still allow the verified diff --git a/dstack/kms/src/config.rs b/dstack/kms/src/config.rs index 4d95b0e17..ba3f9f93e 100644 --- a/dstack/kms/src/config.rs +++ b/dstack/kms/src/config.rs @@ -61,10 +61,8 @@ pub(crate) struct KmsConfig { /// /// This narrows what the KMS asserts about itself; it relaxes no /// verification anywhere. Quotes presented *to* the KMS are still fully - /// checked, so key release stays gated, and relying parties keep their own - /// policy: a guest accepts an unattested KMS certificate but then does not - /// extend `mr-kms`, so a remote verifier can still tell, and another KMS - /// refuses to onboard from one. + /// checked, so key release stays gated, and another KMS refuses to onboard + /// from an unattested one. #[serde(default = "default_true")] pub attest_rpc_cert: bool, /// Whether trusted RPCs require the KMS to first attest itself to its diff --git a/dstack/kms/src/main.rs b/dstack/kms/src/main.rs index fde993d95..dd52622cd 100644 --- a/dstack/kms/src/main.rs +++ b/dstack/kms/src/main.rs @@ -128,8 +128,8 @@ async fn main() -> Result<()> { if !config.attest_rpc_cert { warn!( "attest_rpc_cert = false; the KMS RPC certificate carries no attestation, so \ - guests cannot verify which KMS they are talking to and will not extend \ - mr-kms. Intended for local development only" + guests cannot verify which KMS they are talking to. Intended for local \ + development only" ); }