@@ -9,10 +9,11 @@ use std::{
99
1010use anyhow:: { bail, Context , Result } ;
1111use dstack_kms_rpc:: {
12+ admin_client:: AdminClient ,
1213 kms_client:: KmsClient ,
1314 onboard_server:: { OnboardRpc , OnboardServer } ,
14- AttestationInfoResponse , BootstrapRequest , BootstrapResponse , GetKmsKeyRequest , OnboardRequest ,
15- OnboardResponse ,
15+ AttestationInfoResponse , BootstrapRequest , BootstrapResponse , GetKmsKeyRequest , KmsKeyResponse ,
16+ OnboardRequest , OnboardResponse ,
1617} ;
1718use fs_err as fs;
1819use k256:: ecdsa:: SigningKey ;
@@ -606,30 +607,68 @@ impl Keys {
606607 ) -> Result < Self > {
607608 let attestation_slot = Arc :: new ( Mutex :: new ( None :: < VerifiedAttestation > ) ) ;
608609 let attestation_slot_out = attestation_slot. clone ( ) ;
610+ let capture_source_attestation = Box :: new ( move |info : Option < CertInfo > | {
611+ let Some ( info) = info else {
612+ bail ! ( "Source KMS did not present a TLS certificate" ) ;
613+ } ;
614+ let Some ( attestation) = info. attestation else {
615+ bail ! ( "Source KMS certificate does not contain attestation" ) ;
616+ } ;
617+ let mut slot = attestation_slot_out
618+ . lock ( )
619+ . map_err ( |_| anyhow:: anyhow!( "source attestation mutex poisoned" ) ) ?;
620+ * slot = Some ( attestation) ;
621+ Ok ( ( ) )
622+ } ) ;
623+ let source_attestation = || {
624+ attestation_slot
625+ . lock ( )
626+ . map_err ( |_| anyhow:: anyhow!( "source attestation mutex poisoned" ) ) ?
627+ . clone ( )
628+ . context ( "Missing source KMS attestation" )
629+ } ;
630+
631+ if !source_token. is_empty ( ) {
632+ // `other_kms_url` is the source's admin listener. It accepts a self-issued
633+ // RA-TLS client certificate, so no temp CA is fetched first.
634+ let ( ra_cert, ra_key) = gen_ra_cert ( None ) . await ?;
635+ let client = RaClientConfig :: builder ( )
636+ . tls_no_check ( true )
637+ . tls_built_in_root_certs ( false )
638+ . remote_uri ( other_kms_url. to_string ( ) )
639+ . tls_client_cert ( ra_cert)
640+ . tls_client_key ( ra_key)
641+ . cert_validator ( capture_source_attestation)
642+ . attestation_verifier ( attestation_verifier. clone ( ) )
643+ . bearer_token ( source_token. to_string ( ) )
644+ . build ( )
645+ . into_client ( ) ?;
646+ let info = dstack_client ( ) . info ( ) . await . context ( "Failed to get info" ) ?;
647+ let keys_res = AdminClient :: new ( client)
648+ . get_kms_key ( GetKmsKeyRequest {
649+ vm_config : info. vm_config ,
650+ } )
651+ . await ?;
652+ // The attestation is only captured by that first request; nothing
653+ // received is used before the source passes this check.
654+ ensure_kms_allowed ( cfg, & source_attestation ( ) ?, & attestation_verifier)
655+ . await
656+ . context ( "Source KMS is not allowed for onboarding" ) ?;
657+ return Self :: from_key_response ( cfg, keys_res, domain) . await ;
658+ }
659+
609660 let client = RaClientConfig :: builder ( )
610661 . tls_no_check ( true )
611662 . remote_uri ( other_kms_url. to_string ( ) )
612- . cert_validator ( Box :: new ( move |info : Option < CertInfo > | {
613- let Some ( info) = info else {
614- bail ! ( "Source KMS did not present a TLS certificate" ) ;
615- } ;
616- let Some ( attestation) = info. attestation else {
617- bail ! ( "Source KMS certificate does not contain attestation" ) ;
618- } ;
619- let mut slot = attestation_slot_out
620- . lock ( )
621- . map_err ( |_| anyhow:: anyhow!( "source attestation mutex poisoned" ) ) ?;
622- * slot = Some ( attestation) ;
623- Ok ( ( ) )
624- } ) )
663+ . cert_validator ( capture_source_attestation)
625664 . attestation_verifier ( attestation_verifier. clone ( ) )
626- . maybe_bearer_token ( ( !source_token. is_empty ( ) ) . then ( || source_token. to_string ( ) ) )
627665 . build ( )
628666 . into_client ( ) ?;
629667 let mut kms_client = KmsClient :: new ( client) ;
630668
631669 let tmp_ca = kms_client. get_temp_ca_cert ( ) . await ?;
632- let ( ra_cert, ra_key) = gen_ra_cert ( tmp_ca. temp_ca_cert , tmp_ca. temp_ca_key ) . await ?;
670+ let ( ra_cert, ra_key) =
671+ gen_ra_cert ( Some ( ( tmp_ca. temp_ca_cert , tmp_ca. temp_ca_key ) ) ) . await ?;
633672 let ra_client = RaClient :: new_mtls (
634673 other_kms_url. into ( ) ,
635674 ra_cert,
@@ -638,12 +677,7 @@ impl Keys {
638677 )
639678 . context ( "Failed to create client" ) ?;
640679 kms_client = KmsClient :: new ( ra_client) ;
641- let source_attestation = attestation_slot
642- . lock ( )
643- . map_err ( |_| anyhow:: anyhow!( "source attestation mutex poisoned" ) ) ?
644- . clone ( )
645- . context ( "Missing source KMS attestation" ) ?;
646- ensure_kms_allowed ( cfg, & source_attestation, & attestation_verifier)
680+ ensure_kms_allowed ( cfg, & source_attestation ( ) ?, & attestation_verifier)
647681 . await
648682 . context ( "Source KMS is not allowed for onboarding" ) ?;
649683
@@ -653,6 +687,14 @@ impl Keys {
653687 vm_config : info. vm_config ,
654688 } )
655689 . await ?;
690+ Self :: from_key_response ( cfg, keys_res, domain) . await
691+ }
692+
693+ async fn from_key_response (
694+ cfg : & KmsConfig ,
695+ keys_res : KmsKeyResponse ,
696+ domain : & str ,
697+ ) -> Result < Self > {
656698 if keys_res. keys . len ( ) != 1 {
657699 return Err ( anyhow:: anyhow!( "Invalid keys" ) ) ;
658700 }
@@ -818,11 +860,11 @@ fn keccak256(msg: &[u8]) -> [u8; 32] {
818860 hasher. finalize ( ) . into ( )
819861}
820862
821- async fn gen_ra_cert ( ca_cert_pem : String , ca_key_pem : String ) -> Result < ( String , String ) > {
863+ /// Mint an RA-TLS client certificate, signed by `ca` (cert and key PEM) or self-issued.
864+ async fn gen_ra_cert ( ca : Option < ( String , String ) > ) -> Result < ( String , String ) > {
822865 use ra_tls:: cert:: CertRequest ;
823866 use ra_tls:: rcgen:: { KeyPair , PKCS_ECDSA_P256_SHA256 } ;
824867
825- let ca = CaCert :: new ( ca_cert_pem, ca_key_pem) ?;
826868 let key = KeyPair :: generate_for ( & PKCS_ECDSA_P256_SHA256 ) ?;
827869 let pubkey = key. public_key_der ( ) ;
828870 let report_data = QuoteContentType :: RaTlsCert . to_report_data ( & pubkey) ;
@@ -836,6 +878,10 @@ async fn gen_ra_cert(ca_cert_pem: String, ca_key_pem: String) -> Result<(String,
836878 . attestation ( & attestation)
837879 . key ( & key)
838880 . build ( ) ;
839- let cert = ca. sign ( req) . context ( "Failed to sign certificate" ) ?;
881+ let cert = match ca {
882+ Some ( ( cert_pem, key_pem) ) => CaCert :: new ( cert_pem, key_pem) ?. sign ( req) ,
883+ None => req. self_signed ( ) ,
884+ }
885+ . context ( "Failed to sign certificate" ) ?;
840886 Ok ( ( cert. pem ( ) , key. serialize_pem ( ) ) )
841887}
0 commit comments