diff --git a/dstack/Cargo.lock b/dstack/Cargo.lock index b60ffca35..c18e7ba55 100644 --- a/dstack/Cargo.lock +++ b/dstack/Cargo.lock @@ -684,7 +684,7 @@ dependencies = [ "bytes", "enum_dispatch", "fs-err", - "hickory-resolver 0.24.4", + "hickory-resolver", "http", "http-body-util", "instant-acme", @@ -889,6 +889,16 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "console" version = "0.16.3" @@ -1247,9 +1257,9 @@ checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "dcap-qvl" -version = "0.3.12" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e7842b81018f3b991dc65ec0a95ff347332de58478c4ac43459095af00cc89" +checksum = "92a14fb8954c867d6855e44d98eab18e769816357738406691ebe60d8fdd005d" dependencies = [ "anyhow", "asn1_der", @@ -1313,7 +1323,7 @@ dependencies = [ "netlink-packet-route", "netlink-sys", "once_cell", - "system-configuration", + "system-configuration 0.5.1", "windows 0.48.0", ] @@ -1651,7 +1661,7 @@ dependencies = [ "futures", "git-version", "hex", - "hickory-resolver 0.24.4", + "hickory-resolver", "http-body-util", "http-client", "hyper", @@ -2177,18 +2187,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "enum_dispatch" version = "0.3.13" @@ -2814,23 +2812,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" [[package]] -name = "hickory-proto" -version = "0.24.4" +name = "hickory-net" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" +checksum = "e2295ed2f9c31e471e1428a8f88a3f0e1f4b27c15049592138d1eebe9c35b183" dependencies = [ "async-trait", "cfg-if", "data-encoding", - "enum-as-inner", "futures-channel", "futures-io", "futures-util", + "hickory-proto", "idna", "ipnet", - "once_cell", - "rand 0.8.6", - "thiserror 1.0.69", + "jni", + "rand 0.10.1", + "thiserror 2.0.18", "tinyvec", "tokio", "tracing", @@ -2839,66 +2837,45 @@ dependencies = [ [[package]] name = "hickory-proto" -version = "0.25.2" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" +checksum = "0bab31817bfb44672a252e97fe81cd0c18d1b2cf892108922f6818820df8c643" dependencies = [ - "async-trait", - "cfg-if", "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", "idna", "ipnet", + "jni", "once_cell", - "rand 0.9.4", + "prefix-trie", + "rand 0.10.1", "ring", "thiserror 2.0.18", "tinyvec", - "tokio", "tracing", "url", ] [[package]] name = "hickory-resolver" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" -dependencies = [ - "cfg-if", - "futures-util", - "hickory-proto 0.24.4", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot 0.12.5", - "rand 0.8.6", - "resolv-conf", - "smallvec", - "thiserror 1.0.69", - "tokio", - "tracing", -] - -[[package]] -name = "hickory-resolver" -version = "0.25.2" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" +checksum = "f0d58d28879ceecde6607729660c2667a081ccdc082e082675042793960f178c" dependencies = [ "cfg-if", "futures-util", - "hickory-proto 0.25.2", + "hickory-net", + "hickory-proto", "ipconfig", + "ipnet", + "jni", "moka", + "ndk-context", "once_cell", "parking_lot 0.12.5", - "rand 0.9.4", + "rand 0.10.1", "resolv-conf", "smallvec", + "system-configuration 0.7.0", "thiserror 2.0.18", "tokio", "tracing", @@ -3084,7 +3061,6 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", ] [[package]] @@ -3496,6 +3472,55 @@ dependencies = [ "libc", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -3622,12 +3647,6 @@ dependencies = [ "libc", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -3697,15 +3716,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "lru-slab" version = "0.1.2" @@ -3921,6 +3931,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "netlink-packet-core" version = "0.7.0" @@ -4658,6 +4674,17 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prefix-trie" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf6e3177f0684016a5c209b00882e15f8bdd3f3bb48f0491df10cd102d0c6e7" +dependencies = [ + "either", + "ipnet", + "num-traits", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -4946,6 +4973,7 @@ version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ + "aws-lc-rs", "bytes", "getrandom 0.3.4", "lru-slab", @@ -5288,9 +5316,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" -version = "0.12.28" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ "base64 0.22.1", "bytes", @@ -5298,7 +5326,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "hickory-resolver 0.25.2", + "hickory-resolver", "http", "http-body", "http-body-util", @@ -5314,6 +5342,7 @@ dependencies = [ "quinn", "rustls", "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", "serde_urlencoded", @@ -5327,7 +5356,6 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", ] [[package]] @@ -5664,6 +5692,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.13" @@ -6374,6 +6429,22 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "similar" version = "2.7.0" @@ -6648,7 +6719,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", - "system-configuration-sys", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.9.4", + "system-configuration-sys 0.6.0", ] [[package]] @@ -6661,6 +6743,16 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tagptr" version = "0.2.0" @@ -7565,10 +7657,10 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "1.0.7" +name = "webpki-root-certs" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" dependencies = [ "rustls-pki-types", ] diff --git a/dstack/Cargo.toml b/dstack/Cargo.toml index 39240f39d..a2542ed69 100644 --- a/dstack/Cargo.toml +++ b/dstack/Cargo.toml @@ -183,9 +183,10 @@ hyper-rustls = { version = "0.27", default-features = false, features = [ ] } hyperlocal = "0.9.1" ipnet = { version = "2.11.0", features = ["serde"] } -reqwest = { version = "0.12.14", default-features = false, features = [ +reqwest = { version = "0.13.4", default-features = false, features = [ "json", - "rustls-tls", + "query", + "rustls", "charset", "hickory-dns", ] } @@ -202,7 +203,7 @@ url = "2.5" # Cryptography/Security aes-gcm = "0.10.3" curve25519-dalek = "4.1.3" -dcap-qvl = "0.3.10" +dcap-qvl = "0.5.2" dcap-qvl-webpki = "0.103.4" elliptic-curve = { version = "0.13.8", features = ["pkcs8"] } getrandom = "0.3.1" @@ -231,7 +232,7 @@ alloy = { version = "1.0.32", default-features = false } ez-hash = "1.1.0" # Certificate/DNS -hickory-resolver = "0.24.4" +hickory-resolver = "0.26.1" instant-acme = "0.7.2" pem = "3.0" rcgen = { version = "0.13.2", features = ["pem"] } diff --git a/dstack/certbot/src/acme_client.rs b/dstack/certbot/src/acme_client.rs index fd32c2ad6..60b0dec12 100644 --- a/dstack/certbot/src/acme_client.rs +++ b/dstack/certbot/src/acme_client.rs @@ -4,7 +4,8 @@ use anyhow::{bail, Context, Result}; use fs_err as fs; -use hickory_resolver::error::ResolveErrorKind; +use hickory_resolver::proto::rr::RData; +use hickory_resolver::TokioResolver; use instant_acme::{ Account, AccountCredentials, AuthorizationStatus, ChallengeType, Identifier, NewAccount, NewOrder, Order, OrderStatus, Problem, @@ -372,8 +373,6 @@ impl AcmeClient { let start_time = std::time::Instant::now(); 'outer: loop { - use hickory_resolver::AsyncResolver; - sleep(delay).await; let elapsed = start_time.elapsed(); @@ -385,25 +384,28 @@ impl AcmeClient { break; } - let dns_resolver = - AsyncResolver::tokio_from_system_conf().context("failed to create dns resolver")?; + let dns_resolver = TokioResolver::builder_tokio() + .context("failed to create dns resolver")? + .build() + .context("failed to build dns resolver")?; while let Some(challenge) = unsettled_challenges.pop() { let expected_txt = &challenge.dns_value; let settled = match dns_resolver.txt_lookup(&challenge.acme_domain).await { - Ok(record) => record.iter().any(|txt| { + Ok(record) => record.answers().iter().any(|answer| { + let RData::TXT(txt) = &answer.data else { + return false; + }; let actual_txt = txt.to_string(); debug!("Expected challenge: {expected_txt}, actual: {actual_txt}"); actual_txt == *expected_txt }), + Err(err) if err.is_no_records_found() => false, Err(err) => { - let ResolveErrorKind::NoRecordsFound { .. } = err.kind() else { - bail!( - "failed to lookup dns record {}: {err}", - challenge.acme_domain - ); - }; - false + bail!( + "failed to lookup dns record {}: {err}", + challenge.acme_domain + ); } }; if !settled { diff --git a/dstack/ct_monitor/Cargo.toml b/dstack/ct_monitor/Cargo.toml index 82c1dccdd..a49394432 100644 --- a/dstack/ct_monitor/Cargo.toml +++ b/dstack/ct_monitor/Cargo.toml @@ -15,7 +15,7 @@ clap = { workspace = true, features = ["derive", "env"] } hex = { workspace = true, features = ["alloc", "std"] } hex_fmt.workspace = true regex.workspace = true -reqwest = { workspace = true, default-features = false, features = ["json", "rustls-tls", "charset", "hickory-dns"] } +reqwest = { workspace = true, default-features = false, features = ["json", "rustls", "charset", "hickory-dns"] } serde = { workspace = true, features = ["derive"] } serde-human-bytes.workspace = true serde_json.workspace = true diff --git a/dstack/dstack-attest/src/attestation.rs b/dstack/dstack-attest/src/attestation.rs index c7fc6f038..4506af8ec 100644 --- a/dstack/dstack-attest/src/attestation.rs +++ b/dstack/dstack-attest/src/attestation.rs @@ -14,6 +14,7 @@ use std::{borrow::Cow, time::SystemTime}; use anyhow::{anyhow, bail, Context, Result}; use cc_eventlog::{RuntimeEvent, TdxEvent}; use dcap_qvl::{ + collateral::CollateralClient, quote::{EnclaveReport, Quote, Report, TDReport10, TDReport15}, verify::VerifiedReport as TdxVerifiedReport, }; @@ -44,6 +45,13 @@ const DSTACK_AMD_SEV_SNP: &str = "dstack-amd-sev-snp"; const DSTACK_GCP_TDX: &str = "dstack-gcp-tdx"; const DSTACK_NITRO_ENCLAVE: &str = "dstack-nitro-enclave"; +fn dcap_collateral_client(pccs_url: Option<&str>) -> Result { + match pccs_url.map(str::trim).filter(|url| !url.is_empty()) { + Some(pccs_url) => CollateralClient::with_default_http(pccs_url), + None => CollateralClient::from_env(), + } +} + /// Path to sys-config.json in the host-shared dir. /// /// Honors `DSTACK_HOST_SHARED_DIR` (exported by `dstack-util setup` because the @@ -1419,17 +1427,10 @@ async fn verify_tdx_quote_with_events( runtime_events: &[RuntimeEvent], report_data: &[u8; 64], ) -> Result { - let mut pccs_url = Cow::Borrowed(pccs_url.unwrap_or_default()); - if pccs_url.is_empty() { - pccs_url = match std::env::var("PCCS_URL") { - Ok(url) => Cow::Owned(url), - Err(_) => Cow::Borrowed(""), - }; - } - let tdx_report = - dcap_qvl::collateral::get_collateral_and_verify(quote, Some(pccs_url.as_ref())) - .await - .context("Failed to get collateral")?; + let tdx_report = dcap_collateral_client(pccs_url)? + .fetch_and_verify(quote) + .await + .context("Failed to get collateral")?; validate_tcb(&tdx_report)?; let td_report = tdx_report.report.as_td10().context("no td report")?; @@ -1997,18 +1998,10 @@ impl Attestation { } async fn verify_tdx(&self, pccs_url: Option<&str>, quote: &[u8]) -> Result { - let mut pccs_url = Cow::Borrowed(pccs_url.unwrap_or_default()); - if pccs_url.is_empty() { - // try to read from PCCS_URL env var - pccs_url = match std::env::var("PCCS_URL") { - Ok(url) => Cow::Owned(url), - Err(_) => Cow::Borrowed(""), - }; - } - let tdx_report = - dcap_qvl::collateral::get_collateral_and_verify(quote, Some(pccs_url.as_ref())) - .await - .context("Failed to get collateral")?; + let tdx_report = dcap_collateral_client(pccs_url)? + .fetch_and_verify(quote) + .await + .context("Failed to get collateral")?; validate_tcb(&tdx_report)?; let td_report = tdx_report.report.as_td10().context("no td report")?; diff --git a/dstack/dstack-util/src/host_api.rs b/dstack/dstack-util/src/host_api.rs index 6bcc270a6..18a23d123 100644 --- a/dstack/dstack-util/src/host_api.rs +++ b/dstack/dstack-util/src/host_api.rs @@ -4,6 +4,7 @@ use crate::utils::{deserialize_json_file, sha256, SysConfig}; use anyhow::{anyhow, bail, Context, Result}; +use dcap_qvl::collateral::{CollateralClient, PHALA_PCCS_URL}; use dstack_types::{ shared_filenames::{HOST_SHARED_DIR, SYS_CONFIG}, Platform, @@ -94,12 +95,16 @@ impl HostApi { .map_err(|err| anyhow!("Failed to get sealing key: {err:?}"))?; // verify the key provider quote - let verified_report = dcap_qvl::collateral::get_collateral_and_verify( - &provision.provider_quote, - self.pccs_url.as_deref(), - ) - .await - .context("Failed to get quote collateral")?; + let pccs_url = self + .pccs_url + .as_deref() + .map(str::trim) + .filter(|url| !url.is_empty()) + .unwrap_or(PHALA_PCCS_URL); + let verified_report = CollateralClient::with_default_http(pccs_url)? + .fetch_and_verify(&provision.provider_quote) + .await + .context("Failed to get quote collateral")?; validate_tcb(&verified_report)?; let sgx_report = verified_report .report diff --git a/dstack/gateway/src/proxy/tls_passthough.rs b/dstack/gateway/src/proxy/tls_passthough.rs index d073fce59..3848883d1 100644 --- a/dstack/gateway/src/proxy/tls_passthough.rs +++ b/dstack/gateway/src/proxy/tls_passthough.rs @@ -7,7 +7,9 @@ use std::sync::atomic::Ordering; use std::time::Duration; use anyhow::{bail, Context, Result}; -use hickory_resolver::{lookup::TxtLookup, TokioAsyncResolver}; +use hickory_resolver::lookup::Lookup; +use hickory_resolver::proto::rr::RData; +use hickory_resolver::TokioResolver; use proxy_protocol::ProxyHeader; use tokio::{io::AsyncWriteExt, net::TcpStream, task::JoinSet, time::timeout}; use tracing::{debug, info, warn}; @@ -53,7 +55,7 @@ impl AppAddress { pub(crate) struct AppAddressResolver { prefix: String, compat: bool, - resolver: TokioAsyncResolver, + resolver: TokioResolver, } impl AppAddressResolver { @@ -70,36 +72,39 @@ impl AppAddressResolver { } } -fn app_address_tokio_resolver_from_system_conf() -> Result { - let (config, mut options) = hickory_resolver::system_conf::read_system_conf() - .context("failed to read system dns config")?; +fn app_address_tokio_resolver_from_system_conf() -> Result { + let mut builder = TokioResolver::builder_tokio().context("failed to read system dns config")?; // App-address records may appear shortly after a CVM/app is registered. // Reusing one resolver enables positive TXT caching, but we do not want a // transient NXDOMAIN/NODATA response to hide a newly-added app for too // long. Keep positive caching TTL-aware and cap negative caching. - options.cache_size = APP_ADDRESS_DNS_CACHE_SIZE; + let options = builder.options_mut(); + options.cache_size = APP_ADDRESS_DNS_CACHE_SIZE as u64; options.negative_min_ttl = Some(Duration::ZERO); options.negative_max_ttl = Some(APP_ADDRESS_NEGATIVE_CACHE_TTL); - Ok(TokioAsyncResolver::tokio(config, options)) + builder.build().context("failed to build dns resolver") } -fn parse_lookup(lookup: &TxtLookup, sni: &str, txt_domain: &str) -> Result> { - let Some(txt_record) = lookup.iter().next() else { - return Ok(None); - }; - let Some(data) = txt_record.txt_data().first() else { - return Ok(None); - }; - AppAddress::parse(data) - .with_context(|| format!("failed to parse app address for {sni} via {txt_domain}")) - .map(Some) +fn parse_lookup(lookup: &Lookup, sni: &str, txt_domain: &str) -> Result> { + for answer in lookup.answers() { + let RData::TXT(txt) = &answer.data else { + continue; + }; + let Some(data) = txt.txt_data.first() else { + continue; + }; + return AppAddress::parse(data) + .with_context(|| format!("failed to parse app address for {sni} via {txt_domain}")) + .map(Some); + } + Ok(None) } /// Resolve app address by SNI. `resolver` is shared so its DNS cache is reused. async fn resolve_app_address( - resolver: &TokioAsyncResolver, + resolver: &TokioResolver, prefix: &str, sni: &str, compat: bool, diff --git a/dstack/nsm-qvl/Cargo.toml b/dstack/nsm-qvl/Cargo.toml index 27dd8cec0..7ef014221 100644 --- a/dstack/nsm-qvl/Cargo.toml +++ b/dstack/nsm-qvl/Cargo.toml @@ -30,7 +30,7 @@ rustls-pki-types.workspace = true dcap-qvl-webpki = { workspace = true, features = ["alloc", "rustcrypto"] } # CRL download -reqwest = { workspace = true, features = ["rustls-tls"] } +reqwest = { workspace = true, features = ["rustls"] } [dev-dependencies] nsm-attest.workspace = true diff --git a/dstack/ra-rpc/Cargo.toml b/dstack/ra-rpc/Cargo.toml index 2ec2b636b..0fa6a67a6 100644 --- a/dstack/ra-rpc/Cargo.toml +++ b/dstack/ra-rpc/Cargo.toml @@ -15,7 +15,7 @@ prpc.workspace = true rocket = { workspace = true, features = ["mtls"], optional = true } serde_json.workspace = true tracing.workspace = true -reqwest = { workspace = true, default-features = false, features = ["rustls-tls", "charset"], optional = true } +reqwest = { workspace = true, default-features = false, features = ["rustls", "charset"], optional = true } ra-tls.workspace = true bon.workspace = true diff --git a/dstack/ra-rpc/src/client.rs b/dstack/ra-rpc/src/client.rs index 3d91abe7c..07d875363 100644 --- a/dstack/ra-rpc/src/client.rs +++ b/dstack/ra-rpc/src/client.rs @@ -48,7 +48,6 @@ impl RaClientConfig { .tls_sni(true) .danger_accept_invalid_certs(self.tls_no_check) .danger_accept_invalid_hostnames(self.tls_no_check_hostname) - .tls_built_in_root_certs(self.tls_built_in_root_certs) .connect_timeout(Duration::from_secs(5)) .timeout(Duration::from_secs(60)); if self.cert_validator.is_some() { @@ -60,9 +59,20 @@ impl RaClientConfig { Identity::from_pem(identity_pem.as_bytes()).context("Failed to parse identity")?; builder = builder.identity(identity); } - if let Some(ca) = self.tls_ca_cert { - let ca = Certificate::from_pem(ca.as_bytes()).context("Failed to parse CA")?; - builder = builder.add_root_certificate(ca); + // reqwest 0.13 replaced tls_built_in_root_certs / add_root_certificate with + // tls_certs_merge (keep platform roots) and tls_certs_only (custom roots only). + // Hostname-check bypass also requires tls_certs_only on the rustls backend. + let ca_cert = self + .tls_ca_cert + .as_deref() + .map(|ca| Certificate::from_pem(ca.as_bytes()).context("Failed to parse CA")) + .transpose()?; + if self.tls_built_in_root_certs && !self.tls_no_check_hostname { + if let Some(ca) = ca_cert { + builder = builder.tls_certs_merge([ca]); + } + } else { + builder = builder.tls_certs_only(ca_cert); } let client = builder.build().context("failed to create client")?; Ok(RaClient { diff --git a/dstack/tdx-attest/examples/test_tdx.rs b/dstack/tdx-attest/examples/test_tdx.rs index 173fc15da..ef523c691 100644 --- a/dstack/tdx-attest/examples/test_tdx.rs +++ b/dstack/tdx-attest/examples/test_tdx.rs @@ -4,7 +4,7 @@ //! Test TDX attestation functions on real TDX hardware. -use dcap_qvl::collateral::get_collateral_and_verify; +use dcap_qvl::collateral::CollateralClient; use dcap_qvl::quote::Quote; #[tokio::main] @@ -64,7 +64,11 @@ async fn main() { " PCCS URL: {}", pccs_url.as_deref().unwrap_or("(default)") ); - match get_collateral_and_verify(quote, pccs_url.as_deref()).await { + let verification = match CollateralClient::from_env() { + Ok(client) => client.fetch_and_verify(quote).await, + Err(err) => Err(err), + }; + match verification { Ok(verified) => { println!(" ✓ Quote verified!"); println!(" QE status: {:?}", verified.qe_status);