@@ -2,6 +2,7 @@ use std::{
22 collections:: { BTreeMap , BTreeSet } ,
33 net:: Ipv4Addr ,
44 ops:: Deref ,
5+ path:: Path ,
56 sync:: { Arc , Mutex , MutexGuard , RwLock } ,
67 time:: { Duration , Instant , SystemTime , UNIX_EPOCH } ,
78} ;
@@ -12,11 +13,14 @@ use certbot::{CertBot, WorkDir};
1213use cmd_lib:: run_cmd as cmd;
1314use dstack_gateway_rpc:: {
1415 gateway_server:: { GatewayRpc , GatewayServer } ,
15- AcmeInfoResponse , GatewayState , GuestAgentConfig , RegisterCvmRequest , RegisterCvmResponse ,
16- WireGuardConfig , WireGuardPeer ,
16+ AcmeInfoResponse , GatewayState , GuestAgentConfig , QuotedPublicKey , RegisterCvmRequest ,
17+ RegisterCvmResponse , WireGuardConfig , WireGuardPeer ,
1718} ;
19+ use dstack_guest_agent_rpc:: { dstack_guest_client:: DstackGuestClient , RawQuoteArgs } ;
1820use fs_err as fs;
21+ use http_client:: prpc:: PrpcClient ;
1922use ra_rpc:: { CallContext , RpcCall , VerifiedAttestation } ;
23+ use ra_tls:: attestation:: QuoteContentType ;
2024use rand:: seq:: IteratorRandom ;
2125use rinja:: Template as _;
2226use safe_write:: safe_write;
@@ -170,6 +174,51 @@ impl Proxy {
170174 }
171175 Ok ( renewed)
172176 }
177+
178+ pub ( crate ) async fn acme_info ( & self ) -> Result < AcmeInfoResponse > {
179+ let config = self . lock ( ) . config . clone ( ) ;
180+ let workdir = WorkDir :: new ( & config. certbot . workdir ) ;
181+ let account_uri = workdir. acme_account_uri ( ) . unwrap_or_default ( ) ;
182+ let keys = workdir. list_cert_public_keys ( ) . unwrap_or_default ( ) ;
183+ let agent = crate :: dstack_agent ( ) . context ( "Failed to get dstack agent" ) ?;
184+ let account_quote = get_or_generate_quote (
185+ & agent,
186+ QuoteContentType :: Custom ( "acme-account" ) ,
187+ account_uri. as_bytes ( ) ,
188+ workdir. acme_account_quote_path ( ) ,
189+ )
190+ . await
191+ . unwrap_or_default ( ) ;
192+
193+ let mut quoted_hist_keys = vec ! [ ] ;
194+ for cert_path in workdir. list_certs ( ) . unwrap_or_default ( ) {
195+ let cert_pem = fs:: read_to_string ( & cert_path) . context ( "Failed to read key" ) ?;
196+ let pubkey = certbot:: read_pubkey ( & cert_pem) . context ( "Failed to read pubkey" ) ?;
197+ let quote = get_or_generate_quote (
198+ & agent,
199+ QuoteContentType :: Custom ( "zt-cert" ) ,
200+ & pubkey,
201+ cert_path. display ( ) . to_string ( ) + ".quote" ,
202+ )
203+ . await
204+ . unwrap_or_default ( ) ;
205+ quoted_hist_keys. push ( QuotedPublicKey {
206+ public_key : pubkey,
207+ quote,
208+ } ) ;
209+ }
210+ let active_cert =
211+ fs:: read_to_string ( workdir. cert_path ( ) ) . context ( "Failed to read active cert" ) ?;
212+
213+ Ok ( AcmeInfoResponse {
214+ account_uri,
215+ hist_keys : keys. into_iter ( ) . collect ( ) ,
216+ account_quote,
217+ quoted_hist_keys,
218+ active_cert,
219+ base_domain : config. proxy . base_domain . clone ( ) ,
220+ } )
221+ }
173222}
174223
175224fn load_state ( state_path : & str ) -> Result < ProxyStateMut > {
@@ -681,14 +730,7 @@ impl GatewayRpc for RpcHandler {
681730 }
682731
683732 async fn acme_info ( self ) -> Result < AcmeInfoResponse > {
684- let state = self . state . lock ( ) ;
685- let workdir = WorkDir :: new ( & state. config . certbot . workdir ) ;
686- let account_uri = workdir. acme_account_uri ( ) . unwrap_or_default ( ) ;
687- let keys = workdir. list_cert_public_keys ( ) . unwrap_or_default ( ) ;
688- Ok ( AcmeInfoResponse {
689- account_uri,
690- hist_keys : keys. into_iter ( ) . collect ( ) ,
691- } )
733+ self . state . acme_info ( ) . await
692734 }
693735
694736 async fn update_state ( self , request : GatewayState ) -> Result < ( ) > {
@@ -725,6 +767,26 @@ impl GatewayRpc for RpcHandler {
725767 }
726768}
727769
770+ async fn get_or_generate_quote (
771+ agent : & DstackGuestClient < PrpcClient > ,
772+ content_type : QuoteContentType < ' _ > ,
773+ payload : & [ u8 ] ,
774+ quote_path : impl AsRef < Path > ,
775+ ) -> Result < String > {
776+ let quote_path = quote_path. as_ref ( ) ;
777+ if fs:: metadata ( quote_path) . is_ok ( ) {
778+ return fs:: read_to_string ( quote_path) . context ( "Failed to read quote" ) ;
779+ }
780+ let report_data = content_type. to_report_data ( payload) . to_vec ( ) ;
781+ let response = agent
782+ . get_quote ( RawQuoteArgs { report_data } )
783+ . await
784+ . context ( "Failed to get quote" ) ?;
785+ let quote = serde_json:: to_string ( & response) . context ( "Failed to serialize quote" ) ?;
786+ safe_write ( quote_path, & quote) . context ( "Failed to write quote" ) ?;
787+ Ok ( quote)
788+ }
789+
728790impl RpcCall < Proxy > for RpcHandler {
729791 type PrpcService = GatewayServer < Self > ;
730792
0 commit comments