Skip to content

Commit 0f638ea

Browse files
committed
Merge branch 'master' into gateway-wavekv
2 parents 73b3cec + 6d146c3 commit 0f638ea

20 files changed

Lines changed: 307 additions & 117 deletions

File tree

Cargo.lock

Lines changed: 123 additions & 52 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# SPDX-License-Identifier: Apache-2.0
66

77
[workspace.package]
8-
version = "0.5.5"
8+
version = "0.5.6"
99
authors = ["Kevin Wang <[email protected]>", "Leechael <[email protected]>"]
1010
edition = "2021"
1111
license = "MIT"
@@ -174,8 +174,7 @@ default-net = "0.22.0"
174174
# Cryptography/Security
175175
aes-gcm = "0.10.3"
176176
curve25519-dalek = "4.1.3"
177-
dcap-qvl = "0.3.8"
178-
dcap-qvl-webpki = "0.103"
177+
dcap-qvl = "0.3.10"
179178
elliptic-curve = { version = "0.13.8", features = ["pkcs8"] }
180179
getrandom = "0.3.1"
181180
hkdf = "0.12.4"

basefiles/dstack-prepare.sh

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,22 @@ if ! [[ -e /dev/tdx_guest ]]; then
103103
modprobe tdx-guest
104104
fi
105105

106-
# Mount configfs for TSM (required for TDX quote generation)
107-
if [[ ! -d /sys/kernel/config ]]; then
108-
mkdir -p /sys/kernel/config
109-
fi
110-
if ! mountpoint -q /sys/kernel/config; then
111-
log "Mounting configfs for TSM..."
112-
mount -t configfs none /sys/kernel/config
113-
fi
114-
115-
# Create TSM report directory for TDX attestation
116-
if [[ -e /dev/tdx_guest ]] && [[ ! -d /sys/kernel/config/tsm/report/com.intel.dcap ]]; then
117-
log "Creating TSM report directory..."
118-
mkdir -p /sys/kernel/config/tsm/report/com.intel.dcap
119-
fi
106+
# Setup configfs and TSM for TDX attestation
107+
setup_tsm() {
108+
if ! grep -q configfs /proc/filesystems; then
109+
log "Warning: configfs not available in kernel, TSM may not work"
110+
return 1
111+
fi
112+
if ! mountpoint -q /sys/kernel/config 2>/dev/null; then
113+
log "Mounting configfs for TSM..."
114+
mount -t configfs none /sys/kernel/config
115+
fi
116+
if [[ -e /dev/tdx_guest ]] && [[ ! -d /sys/kernel/config/tsm/report/com.intel.dcap ]]; then
117+
log "Creating TSM report directory..."
118+
mkdir -p /sys/kernel/config/tsm/report/com.intel.dcap
119+
fi
120+
}
121+
setup_tsm || true
120122

121123
# Setup dstack system
122124
log "Preparing dstack system..."

cert-client/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use dstack_types::{AppKeys, KeyProvider};
88
use ra_rpc::client::{RaClient, RaClientConfig};
99
use ra_tls::{
1010
attestation::{QuoteContentType, VersionedAttestation},
11-
cert::{generate_ra_cert, CaCert, CertConfig, CertSigningRequestV2, Csr},
11+
cert::{generate_ra_cert, CaCert, CertConfigV2, CertSigningRequestV2, Csr},
1212
rcgen::KeyPair,
1313
};
1414

@@ -96,7 +96,7 @@ impl CertRequestClient {
9696
pub async fn request_cert(
9797
&self,
9898
key: &KeyPair,
99-
config: CertConfig,
99+
config: CertConfigV2,
100100
attestation_override: Option<VersionedAttestation>,
101101
) -> Result<Vec<String>> {
102102
let pubkey = key.public_key_der();

dstack-attest/src/attestation.rs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use dcap_qvl::{
1212
quote::{EnclaveReport, Quote, Report, TDReport10, TDReport15},
1313
verify::VerifiedReport as TdxVerifiedReport,
1414
};
15+
#[cfg(feature = "quote")]
16+
use dstack_types::SysConfig;
1517
use dstack_types::{Platform, VmConfig};
1618
use ez_hash::{sha256, Hasher, Sha384};
1719
use or_panic::ResultOrPanic;
@@ -23,6 +25,25 @@ use sha2::Digest as _;
2325
const DSTACK_TDX: &str = "dstack-tdx";
2426
const DSTACK_GCP_TDX: &str = "dstack-gcp-tdx";
2527
const DSTACK_NITRO_ENCLAVE: &str = "dstack-nitro-enclave";
28+
#[cfg(feature = "quote")]
29+
const SYS_CONFIG_PATH: &str = "/dstack/.host-shared/.sys-config.json";
30+
31+
/// Global lock for quote generation. The underlying TDX driver does not support concurrent access.
32+
#[cfg(feature = "quote")]
33+
static QUOTE_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
34+
35+
/// Read vm_config from sys-config.json
36+
#[cfg(feature = "quote")]
37+
fn read_vm_config() -> Result<String> {
38+
let content = match fs_err::read_to_string(SYS_CONFIG_PATH) {
39+
Ok(content) => content,
40+
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(String::new()),
41+
Err(err) => return Err(err).context("Failed to read sys-config"),
42+
};
43+
let sys_config: SysConfig =
44+
serde_json::from_str(&content).context("Failed to parse sys-config")?;
45+
Ok(sys_config.vm_config)
46+
}
2647

2748
/// Attestation mode
2849
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)]
@@ -318,10 +339,13 @@ impl<T> Attestation<T> {
318339
.map(|q| serde_json::to_vec(&q.event_log).unwrap_or_default())
319340
}
320341

321-
/// Get TDX event log string
342+
/// Get TDX event log string with RTMR[0-2] payloads stripped to reduce size.
343+
/// Only digests are kept for boot-time events; runtime events (RTMR3) retain full payload.
322344
pub fn get_tdx_event_log_string(&self) -> Option<String> {
323-
self.tdx_quote()
324-
.map(|q| serde_json::to_string(&q.event_log).unwrap_or_default())
345+
self.tdx_quote().map(|q| {
346+
let stripped: Vec<_> = q.event_log.iter().map(|e| e.stripped()).collect();
347+
serde_json::to_string(&stripped).unwrap_or_default()
348+
})
325349
}
326350

327351
pub fn get_td10_report(&self) -> Option<TDReport10> {
@@ -557,6 +581,11 @@ impl Attestation {
557581
}
558582

559583
pub fn quote_with_app_id(report_data: &[u8; 64], app_id: Option<[u8; 20]>) -> Result<Self> {
584+
// Lock to prevent concurrent quote generation (TDX driver doesn't support it)
585+
let _guard = QUOTE_LOCK
586+
.lock()
587+
.map_err(|_| anyhow!("Quote lock poisoned"))?;
588+
560589
let mode = AttestationMode::detect()?;
561590
let runtime_events = if mode.is_composable() {
562591
RuntimeEvent::read_all().context("Failed to read runtime events")?
@@ -579,8 +608,7 @@ impl Attestation {
579608
};
580609
let config = match &quote {
581610
AttestationQuote::DstackTdx(_) => {
582-
// TODO: Find a better way handling this hardcode path
583-
fs_err::read_to_string("/dstack/.host-shared/.sys-config.json").unwrap_or_default()
611+
read_vm_config().context("Failed to read VM config")?
584612
}
585613
AttestationQuote::DstackGcpTdx | AttestationQuote::DstackNitroEnclave => {
586614
bail!("Unsupported attestation mode: {mode:?}");

dstack-util/src/system_setup.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use luks2::{
2828
LuksSegmentSize,
2929
};
3030
use ra_rpc::client::{CertInfo, RaClient, RaClientConfig};
31-
use ra_tls::cert::generate_ra_cert;
31+
use ra_tls::cert::{generate_ra_cert, CertConfigV2};
3232
use rand::Rng as _;
3333
use scopeguard::defer;
3434
use serde::{Deserialize, Serialize};
@@ -48,10 +48,7 @@ use cmd_lib::run_fun as cmd;
4848
use dstack_gateway_rpc::{
4949
gateway_client::GatewayClient, RegisterCvmRequest, RegisterCvmResponse, WireGuardPeer,
5050
};
51-
use ra_tls::{
52-
cert::CertConfig,
53-
rcgen::{KeyPair, PKCS_ECDSA_P256_SHA256},
54-
};
51+
use ra_tls::rcgen::{KeyPair, PKCS_ECDSA_P256_SHA256};
5552
use serde_human_bytes as hex_bytes;
5653
use serde_json::Value;
5754

@@ -388,13 +385,15 @@ impl<'a> GatewayContext<'a> {
388385
let sk = cmd!(wg genkey)?;
389386
let pk = cmd!(echo $sk | wg pubkey).or(Err(anyhow!("Failed to generate public key")))?;
390387

391-
let config = CertConfig {
388+
let config = CertConfigV2 {
392389
org_name: None,
393390
subject: "dstack-guest-agent".to_string(),
394391
subject_alt_names: vec![],
395392
usage_server_auth: false,
396393
usage_client_auth: true,
397394
ext_quote: true,
395+
not_before: None,
396+
not_after: None,
398397
};
399398
let cert_client = CertRequestClient::create(
400399
self.keys,

gateway/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ async fn maybe_gen_certs(config: &Config, tls_config: &TlsConfig) -> Result<()>
8383
usage_ra_tls: true,
8484
usage_server_auth: true,
8585
usage_client_auth: false,
86+
not_before: None,
87+
not_after: None,
8688
})
8789
.await?;
8890

guest-agent/rpc/proto/agent_rpc.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ message GetTlsKeyArgs {
7272
bool usage_server_auth = 4;
7373
// Key usage client auth
7474
bool usage_client_auth = 5;
75+
// Certificate validity start time as seconds since UNIX epoch
76+
optional uint64 not_before = 6;
77+
// Certificate validity end time as seconds since UNIX epoch
78+
optional uint64 not_after = 7;
7579
}
7680

7781
// The request to derive a key

guest-agent/src/rpc_service.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use or_panic::ResultOrPanic;
2929
use ra_rpc::{Attestation, CallContext, RpcCall};
3030
use ra_tls::{
3131
attestation::{QuoteContentType, VersionedAttestation, DEFAULT_HASH_ALGORITHM},
32-
cert::CertConfig,
32+
cert::CertConfigV2,
3333
kdf::{derive_ecdsa_key, derive_ecdsa_key_pair_from_bytes},
3434
};
3535
use rcgen::KeyPair;
@@ -78,13 +78,15 @@ impl AppStateInner {
7878
.cert_client
7979
.request_cert(
8080
&key,
81-
CertConfig {
81+
CertConfigV2 {
8282
org_name: None,
8383
subject: "demo-cert".to_string(),
8484
subject_alt_names: vec![],
8585
usage_server_auth: false,
8686
usage_client_auth: true,
8787
ext_quote: true,
88+
not_after: None,
89+
not_before: None,
8890
},
8991
attestation_override,
9092
)
@@ -233,13 +235,15 @@ impl DstackGuestRpc for InternalRpcHandler {
233235
.context("Failed to generate secure seed")?;
234236
let derived_key =
235237
derive_ecdsa_key_pair_from_bytes(&seed, &[]).context("Failed to derive key")?;
236-
let config = CertConfig {
238+
let config = CertConfigV2 {
237239
org_name: None,
238240
subject: request.subject,
239241
subject_alt_names: request.alt_names,
240242
usage_server_auth: request.usage_server_auth,
241243
usage_client_auth: request.usage_client_auth,
242244
ext_quote: request.usage_ra_tls,
245+
not_after: request.not_after,
246+
not_before: request.not_before,
243247
};
244248
let attestation_override = self
245249
.state
@@ -493,13 +497,15 @@ impl TappdRpc for InternalRpcHandlerV0 {
493497
};
494498
let derived_key = derive_ecdsa_key_pair_from_bytes(seed, &[request.path.as_bytes()])
495499
.context("Failed to derive key")?;
496-
let config = CertConfig {
500+
let config = CertConfigV2 {
497501
org_name: None,
498502
subject: request.subject,
499503
subject_alt_names: request.alt_names,
500504
usage_server_auth: request.usage_server_auth,
501505
usage_client_auth: request.usage_client_auth,
502506
ext_quote: request.usage_ra_tls,
507+
not_before: None,
508+
not_after: None,
503509
};
504510
let attestation_override = self
505511
.state
@@ -561,8 +567,10 @@ impl TappdRpc for InternalRpcHandlerV0 {
561567
});
562568
}
563569
let event_log = read_event_log().context("Failed to decode event log")?;
570+
// Strip RTMR[0-2] payloads, keep only digests
571+
let stripped: Vec<_> = event_log.iter().map(|e| e.stripped()).collect();
564572
let event_log =
565-
serde_json::to_string(&event_log).context("Failed to serialize event log")?;
573+
serde_json::to_string(&stripped).context("Failed to serialize event log")?;
566574
let quote = tdx_attest::get_quote(&report_data).context("Failed to get quote")?;
567575
Ok(TdxQuoteResponse {
568576
quote,
@@ -657,12 +665,13 @@ impl WorkerRpc for ExternalRpcHandler {
657665
} else {
658666
let ed25519_quote = tdx_attest::get_quote(&ed25519_report_data)
659667
.context("Failed to get ed25519 quote")?;
660-
let event_log = serde_json::to_string(
661-
&read_event_log().context("Failed to read event log")?,
662-
)?;
668+
let raw_event_log = read_event_log().context("Failed to read event log")?;
669+
// Strip RTMR[0-2] payloads, keep only digests
670+
let stripped: Vec<_> = raw_event_log.iter().map(|e| e.stripped()).collect();
671+
let event_log = serde_json::to_string(&stripped)?;
663672
Ok(GetQuoteResponse {
664673
quote: ed25519_quote,
665-
event_log: event_log.clone(),
674+
event_log,
666675
report_data: ed25519_report_data.to_vec(),
667676
vm_config: self.state.inner.vm_config.clone(),
668677
})
@@ -688,9 +697,10 @@ impl WorkerRpc for ExternalRpcHandler {
688697
} else {
689698
let secp256k1_quote = tdx_attest::get_quote(&secp256k1_report_data)
690699
.context("Failed to get secp256k1 quote")?;
691-
let event_log = serde_json::to_string(
692-
&read_event_log().context("Failed to read event log")?,
693-
)?;
700+
let raw_event_log = read_event_log().context("Failed to read event log")?;
701+
// Strip RTMR[0-2] payloads, keep only digests
702+
let stripped: Vec<_> = raw_event_log.iter().map(|e| e.stripped()).collect();
703+
let event_log = serde_json::to_string(&stripped)?;
694704

695705
Ok(GetQuoteResponse {
696706
quote: secp256k1_quote,

kms/src/main_service.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl KmsState {
6969
config.image.cache_dir.display().to_string(),
7070
config.image.download_url.clone(),
7171
config.image.download_timeout,
72+
config.pccs_url.clone(),
7273
);
7374
Ok(Self {
7475
inner: Arc::new(KmsStateInner {

0 commit comments

Comments
 (0)