@@ -8,13 +8,16 @@ use parity_scale_codec::{Decode, Encode};
88use serde:: { Deserialize , Serialize } ;
99use std:: {
1010 fmt:: { self , Display , Formatter } ,
11- time:: { SystemTime , UNIX_EPOCH } ,
11+ time:: { Duration , SystemTime , UNIX_EPOCH } ,
1212} ;
1313
1414use thiserror:: Error ;
1515
1616use crate :: attestation:: { dcap:: DcapVerificationError , measurements:: MeasurementPolicy } ;
1717
18+ const GCP_METADATA_API : & str =
19+ "http://metadata.google.internal/computeMetadata/v1/project/project-id" ;
20+
1821/// This is the type sent over the channel to provide an attestation
1922#[ derive( Clone , Debug , Serialize , Deserialize , Encode , Decode ) ]
2023pub struct AttestationExchangeMessage {
@@ -68,20 +71,23 @@ impl AttestationType {
6871 }
6972
7073 /// Detect what platform we are on by attempting an attestation
71- pub async fn detect ( ) -> Self {
74+ pub async fn detect ( ) -> Result < Self , AttestationError > {
7275 // First attempt azure, if the feature is present
7376 #[ cfg( feature = "azure" ) ]
7477 {
7578 if azure:: create_azure_attestation ( [ 0 ; 64 ] ) . await . is_ok ( ) {
76- return AttestationType :: AzureTdx ;
79+ return Ok ( AttestationType :: AzureTdx ) ;
7780 }
7881 }
7982 // Otherwise try DCAP quote - this internally checks that the quote provider is `tdx_guest`
8083 if configfs_tsm:: create_tdx_quote ( [ 0 ; 64 ] ) . is_ok ( ) {
81- // TODO Possibly also check if it looks like we are on GCP (eg: hit metadata API)
82- return AttestationType :: DcapTdx ;
84+ if running_on_gcp ( ) . await ? {
85+ return Ok ( AttestationType :: GcpTdx ) ;
86+ } else {
87+ return Ok ( AttestationType :: DcapTdx ) ;
88+ }
8389 }
84- AttestationType :: None
90+ Ok ( AttestationType :: None )
8591 }
8692}
8793
@@ -124,7 +130,7 @@ impl AttestationGenerator {
124130 let attestation_type_string = attestation_type_string. unwrap_or_else ( || "auto" . to_string ( ) ) ;
125131 let attestaton_type = if attestation_type_string == "auto" {
126132 tracing:: info!( "Doing attestation type detection..." ) ;
127- AttestationType :: detect ( ) . await
133+ AttestationType :: detect ( ) . await ?
128134 } else {
129135 serde_json:: from_value ( serde_json:: Value :: String ( attestation_type_string) ) ?
130136 } ;
@@ -362,6 +368,32 @@ async fn log_attestation(attestation: &AttestationExchangeMessage) {
362368 }
363369}
364370
371+ /// Test whether it looks like we are running on GCP by hitting the metadata API
372+ async fn running_on_gcp ( ) -> Result < bool , AttestationError > {
373+ let mut headers = reqwest:: header:: HeaderMap :: new ( ) ;
374+ headers. insert (
375+ "Metadata-Flavor" ,
376+ "Google" . parse ( ) . expect ( "Cannot parse header" ) ,
377+ ) ;
378+
379+ let client = reqwest:: Client :: builder ( )
380+ . timeout ( Duration :: from_millis ( 200 ) )
381+ . default_headers ( headers)
382+ . build ( ) ?;
383+
384+ let resp = client. get ( GCP_METADATA_API ) . send ( ) . await ;
385+
386+ if let Ok ( r) = resp {
387+ return Ok ( r. status ( ) . is_success ( )
388+ && r. headers ( )
389+ . get ( "Metadata-Flavor" )
390+ . map ( |v| v == "Google" )
391+ . unwrap_or ( false ) ) ;
392+ }
393+
394+ Ok ( false )
395+ }
396+
365397/// An error when generating or verifying an attestation
366398#[ derive( Error , Debug ) ]
367399pub enum AttestationError {
@@ -392,6 +424,8 @@ pub enum AttestationError {
392424 DummyServer ( String ) ,
393425 #[ error( "JSON: {0}" ) ]
394426 SerdeJson ( #[ from] serde_json:: Error ) ,
427+ #[ error( "HTTP client: {0}" ) ]
428+ Reqwest ( #[ from] reqwest:: Error ) ,
395429}
396430
397431#[ cfg( test) ]
@@ -403,4 +437,9 @@ mod tests {
403437 // We dont enforce what platform the test is run on, only that the function does not panic
404438 let _ = AttestationGenerator :: new_with_detection ( None , None ) . await ;
405439 }
440+
441+ #[ tokio:: test]
442+ async fn running_on_gcp_check_does_not_panic ( ) {
443+ let _ = running_on_gcp ( ) . await ;
444+ }
406445}
0 commit comments