1- // SPDX-FileCopyrightText: © 2024-2025 Phala Network < dstack@phala.network>
1+ // SPDX-FileCopyrightText: 2024-2025 Phala Network dstack@phala.network
22//
33// SPDX-License-Identifier: Apache-2.0
44
55use anyhow:: { anyhow, Context , Result } ;
6+ use base64:: { engine:: general_purpose:: STANDARD , Engine as _} ;
67use clap:: Parser ;
78use config:: { Config , TlsConfig } ;
89use dstack_guest_agent_rpc:: { dstack_guest_client:: DstackGuestClient , GetTlsKeyArgs } ;
10+ use dstack_kms_rpc:: SignCertRequest ;
911use http_client:: prpc:: PrpcClient ;
1012use ra_rpc:: { client:: RaClient , prpc_routes as prpc, rocket_helper:: QuoteVerifier } ;
13+ use ra_tls:: cert:: { CertConfigV2 , CertSigningRequestV2 , Csr } ;
14+ use ra_tls:: rcgen:: KeyPair ;
1115use rocket:: {
1216 fairing:: AdHoc ,
1317 figment:: { providers:: Serialized , Figment } ,
1418} ;
19+ use serde:: { Deserialize , Serialize } ;
1520use tracing:: info;
1621
1722use admin_service:: AdminRpcHandler ;
@@ -30,6 +35,18 @@ mod models;
3035mod proxy;
3136mod web_routes;
3237
38+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
39+ struct DebugKeyData {
40+ /// Private key in PEM format
41+ key_pem : String ,
42+ /// TDX quote in base64 format
43+ quote_base64 : String ,
44+ /// Event log in JSON string format
45+ event_log : String ,
46+ /// VM config in JSON string format
47+ vm_config : String ,
48+ }
49+
3350#[ global_allocator]
3451static ALLOCATOR : jemallocator:: Jemalloc = jemallocator:: Jemalloc ;
3552
@@ -126,29 +143,78 @@ async fn gen_debug_certs(
126143 info ! ( "KMS URL is empty, skipping cert generation" ) ;
127144 return Ok ( ( ) ) ;
128145 }
146+
147+ // Check if debug key file is configured
148+ if config. debug . key_file . is_empty ( ) {
149+ info ! ( "Debug key file not configured, skipping cert generation" ) ;
150+ return Ok ( ( ) ) ;
151+ }
152+
153+ // Load pre-generated key pair and quote data from JSON file
154+ info ! ( "Loading debug key data from: {}" , config. debug. key_file) ;
155+ let ctx = "Failed to read debug key, run `cargo run --bin gen_debug_key -- <simulator_url>` to generate it" ;
156+ let json_content = fs_err:: read_to_string ( & config. debug . key_file ) . context ( ctx) ?;
157+ let debug_data: DebugKeyData =
158+ serde_json:: from_str ( & json_content) . context ( "Failed to parse debug key JSON" ) ?;
159+
160+ let key_pem = debug_data. key_pem ;
161+ let quote_bin = STANDARD
162+ . decode ( & debug_data. quote_base64 )
163+ . context ( "Failed to decode quote from base64" ) ?;
164+ let event_log_json = debug_data. event_log ;
165+ let vm_config_json = debug_data. vm_config ;
166+
167+ // Parse key pair
168+ let key = KeyPair :: from_pem ( & key_pem) . context ( "Failed to parse debug key" ) ?;
169+ let pubkey = key. public_key_der ( ) ;
170+
171+ // Build CSR with attestation from debug quote
172+ let attestation =
173+ ra_tls:: attestation:: Attestation :: from_tdx_quote ( quote_bin, event_log_json. as_bytes ( ) )
174+ . context ( "Failed to create attestation from debug quote" ) ?
175+ . into_versioned ( ) ;
176+
177+ let csr = CertSigningRequestV2 {
178+ confirm : "please sign cert:" . to_string ( ) ,
179+ pubkey,
180+ config : CertConfigV2 {
181+ org_name : None ,
182+ subject : "dstack-gateway" . to_string ( ) ,
183+ subject_alt_names : alt_names,
184+ usage_server_auth : true ,
185+ usage_client_auth : true ,
186+ ext_quote : true ,
187+ not_before : None ,
188+ not_after : None ,
189+ } ,
190+ attestation,
191+ } ;
192+ let signature = csr. signed_by ( & key) . context ( "Failed to sign CSR" ) ?;
193+
194+ // Send CSR to KMS for signing
129195 let kms_url = format ! ( "{kms_url}/prpc" ) ;
130- info ! ( "Getting temp CA cert from {kms_url}" ) ;
131- let client = RaClient :: new ( kms_url, true ) . context ( "Failed to create kms client" ) ?;
132- let client = dstack_kms_rpc:: kms_client:: KmsClient :: new ( client ) ;
133- let temp_ca_response = client . get_temp_ca_cert ( ) . await ? ;
134- let ca_cert = temp_ca_response . temp_ca_cert . clone ( ) ;
135- let temp_ca =
136- ra_tls :: cert :: CaCert :: new ( temp_ca_response . temp_ca_cert , temp_ca_response . temp_ca_key )
137- . context ( "Failed to create temp CA" ) ? ;
138- let key = ra_tls :: rcgen :: KeyPair :: generate ( ) . context ( "Failed to generate key" ) ? ;
139- let cert_req = ra_tls :: cert :: CertRequest :: builder ( )
140- . key ( & key )
141- . subject ( "dstack-gateway" )
142- . alt_names ( & alt_names )
143- . usage_server_auth ( true )
144- . usage_client_auth ( true )
145- . build ( ) ;
146- let cert = temp_ca
147- . sign ( cert_req )
148- . context ( "Failed to sign rpc cert with temp CA" ) ? ;
196+ info ! ( "Sending CSR to KMS for signing: {kms_url}" ) ;
197+ let kms_client = RaClient :: new ( kms_url, true ) . context ( "Failed to create kms client" ) ?;
198+ let kms_client = dstack_kms_rpc:: kms_client:: KmsClient :: new ( kms_client ) ;
199+ let sign_response = kms_client
200+ . sign_cert ( SignCertRequest {
201+ api_version : 2 ,
202+ csr : csr . to_vec ( ) ,
203+ signature ,
204+ vm_config : vm_config_json . to_string ( ) ,
205+ } )
206+ . await
207+ . context ( "Failed to sign certificate via KMS" ) ? ;
208+
209+ let ca_cert = sign_response
210+ . certificate_chain
211+ . last ( )
212+ . context ( "Empty certificate chain" ) ?
213+ . to_string ( ) ;
214+ let certs = sign_response . certificate_chain . join ( " \n " ) ;
149215
150216 write_cert ( & tls_config. mutual . ca_certs , & ca_cert) ?;
151- write_cert ( & tls_config. certs , & cert . pem ( ) ) ?;
217+ write_cert ( & tls_config. certs , & certs ) ?;
152218 write_cert ( & tls_config. key , & key. serialize_pem ( ) ) ?;
153219 Ok ( ( ) )
154220}
0 commit comments