Skip to content

Commit 17f6148

Browse files
authored
Merge pull request #786 from Dstack-TEE/fix/dependabot-357-hickory-proto
fix: update vulnerable Hickory DNS dependencies
2 parents 7b1b493 + d33a8a6 commit 17f6148

11 files changed

Lines changed: 268 additions & 156 deletions

File tree

dstack/Cargo.lock

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

dstack/Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,10 @@ hyper-rustls = { version = "0.27", default-features = false, features = [
183183
] }
184184
hyperlocal = "0.9.1"
185185
ipnet = { version = "2.11.0", features = ["serde"] }
186-
reqwest = { version = "0.12.14", default-features = false, features = [
186+
reqwest = { version = "0.13.4", default-features = false, features = [
187187
"json",
188-
"rustls-tls",
188+
"query",
189+
"rustls",
189190
"charset",
190191
"hickory-dns",
191192
] }
@@ -202,7 +203,7 @@ url = "2.5"
202203
# Cryptography/Security
203204
aes-gcm = "0.10.3"
204205
curve25519-dalek = "4.1.3"
205-
dcap-qvl = "0.3.10"
206+
dcap-qvl = "0.5.2"
206207
dcap-qvl-webpki = "0.103.4"
207208
elliptic-curve = { version = "0.13.8", features = ["pkcs8"] }
208209
getrandom = "0.3.1"
@@ -231,7 +232,7 @@ alloy = { version = "1.0.32", default-features = false }
231232
ez-hash = "1.1.0"
232233

233234
# Certificate/DNS
234-
hickory-resolver = "0.24.4"
235+
hickory-resolver = "0.26.1"
235236
instant-acme = "0.7.2"
236237
pem = "3.0"
237238
rcgen = { version = "0.13.2", features = ["pem"] }

dstack/certbot/src/acme_client.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
use anyhow::{bail, Context, Result};
66
use fs_err as fs;
7-
use hickory_resolver::error::ResolveErrorKind;
7+
use hickory_resolver::proto::rr::RData;
8+
use hickory_resolver::TokioResolver;
89
use instant_acme::{
910
Account, AccountCredentials, AuthorizationStatus, ChallengeType, Identifier, NewAccount,
1011
NewOrder, Order, OrderStatus, Problem,
@@ -372,8 +373,6 @@ impl AcmeClient {
372373
let start_time = std::time::Instant::now();
373374

374375
'outer: loop {
375-
use hickory_resolver::AsyncResolver;
376-
377376
sleep(delay).await;
378377

379378
let elapsed = start_time.elapsed();
@@ -385,25 +384,28 @@ impl AcmeClient {
385384
break;
386385
}
387386

388-
let dns_resolver =
389-
AsyncResolver::tokio_from_system_conf().context("failed to create dns resolver")?;
387+
let dns_resolver = TokioResolver::builder_tokio()
388+
.context("failed to create dns resolver")?
389+
.build()
390+
.context("failed to build dns resolver")?;
390391

391392
while let Some(challenge) = unsettled_challenges.pop() {
392393
let expected_txt = &challenge.dns_value;
393394
let settled = match dns_resolver.txt_lookup(&challenge.acme_domain).await {
394-
Ok(record) => record.iter().any(|txt| {
395+
Ok(record) => record.answers().iter().any(|answer| {
396+
let RData::TXT(txt) = &answer.data else {
397+
return false;
398+
};
395399
let actual_txt = txt.to_string();
396400
debug!("Expected challenge: {expected_txt}, actual: {actual_txt}");
397401
actual_txt == *expected_txt
398402
}),
403+
Err(err) if err.is_no_records_found() => false,
399404
Err(err) => {
400-
let ResolveErrorKind::NoRecordsFound { .. } = err.kind() else {
401-
bail!(
402-
"failed to lookup dns record {}: {err}",
403-
challenge.acme_domain
404-
);
405-
};
406-
false
405+
bail!(
406+
"failed to lookup dns record {}: {err}",
407+
challenge.acme_domain
408+
);
407409
}
408410
};
409411
if !settled {

dstack/ct_monitor/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ clap = { workspace = true, features = ["derive", "env"] }
1515
hex = { workspace = true, features = ["alloc", "std"] }
1616
hex_fmt.workspace = true
1717
regex.workspace = true
18-
reqwest = { workspace = true, default-features = false, features = ["json", "rustls-tls", "charset", "hickory-dns"] }
18+
reqwest = { workspace = true, default-features = false, features = ["json", "rustls", "charset", "hickory-dns"] }
1919
serde = { workspace = true, features = ["derive"] }
2020
serde-human-bytes.workspace = true
2121
serde_json.workspace = true

dstack/dstack-attest/src/attestation.rs

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use std::{borrow::Cow, time::SystemTime};
1414
use anyhow::{anyhow, bail, Context, Result};
1515
use cc_eventlog::{RuntimeEvent, TdxEvent};
1616
use dcap_qvl::{
17+
collateral::CollateralClient,
1718
quote::{EnclaveReport, Quote, Report, TDReport10, TDReport15},
1819
verify::VerifiedReport as TdxVerifiedReport,
1920
};
@@ -44,6 +45,13 @@ const DSTACK_AMD_SEV_SNP: &str = "dstack-amd-sev-snp";
4445
const DSTACK_GCP_TDX: &str = "dstack-gcp-tdx";
4546
const DSTACK_NITRO_ENCLAVE: &str = "dstack-nitro-enclave";
4647

48+
fn dcap_collateral_client(pccs_url: Option<&str>) -> Result<CollateralClient> {
49+
match pccs_url.map(str::trim).filter(|url| !url.is_empty()) {
50+
Some(pccs_url) => CollateralClient::with_default_http(pccs_url),
51+
None => CollateralClient::from_env(),
52+
}
53+
}
54+
4755
/// Path to sys-config.json in the host-shared dir.
4856
///
4957
/// Honors `DSTACK_HOST_SHARED_DIR` (exported by `dstack-util setup` because the
@@ -1419,17 +1427,10 @@ async fn verify_tdx_quote_with_events(
14191427
runtime_events: &[RuntimeEvent],
14201428
report_data: &[u8; 64],
14211429
) -> Result<TdxVerifiedReport> {
1422-
let mut pccs_url = Cow::Borrowed(pccs_url.unwrap_or_default());
1423-
if pccs_url.is_empty() {
1424-
pccs_url = match std::env::var("PCCS_URL") {
1425-
Ok(url) => Cow::Owned(url),
1426-
Err(_) => Cow::Borrowed(""),
1427-
};
1428-
}
1429-
let tdx_report =
1430-
dcap_qvl::collateral::get_collateral_and_verify(quote, Some(pccs_url.as_ref()))
1431-
.await
1432-
.context("Failed to get collateral")?;
1430+
let tdx_report = dcap_collateral_client(pccs_url)?
1431+
.fetch_and_verify(quote)
1432+
.await
1433+
.context("Failed to get collateral")?;
14331434
validate_tcb(&tdx_report)?;
14341435

14351436
let td_report = tdx_report.report.as_td10().context("no td report")?;
@@ -1997,18 +1998,10 @@ impl Attestation {
19971998
}
19981999

19992000
async fn verify_tdx(&self, pccs_url: Option<&str>, quote: &[u8]) -> Result<TdxVerifiedReport> {
2000-
let mut pccs_url = Cow::Borrowed(pccs_url.unwrap_or_default());
2001-
if pccs_url.is_empty() {
2002-
// try to read from PCCS_URL env var
2003-
pccs_url = match std::env::var("PCCS_URL") {
2004-
Ok(url) => Cow::Owned(url),
2005-
Err(_) => Cow::Borrowed(""),
2006-
};
2007-
}
2008-
let tdx_report =
2009-
dcap_qvl::collateral::get_collateral_and_verify(quote, Some(pccs_url.as_ref()))
2010-
.await
2011-
.context("Failed to get collateral")?;
2001+
let tdx_report = dcap_collateral_client(pccs_url)?
2002+
.fetch_and_verify(quote)
2003+
.await
2004+
.context("Failed to get collateral")?;
20122005
validate_tcb(&tdx_report)?;
20132006

20142007
let td_report = tdx_report.report.as_td10().context("no td report")?;

dstack/dstack-util/src/host_api.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use crate::utils::{deserialize_json_file, sha256, SysConfig};
66
use anyhow::{anyhow, bail, Context, Result};
7+
use dcap_qvl::collateral::{CollateralClient, PHALA_PCCS_URL};
78
use dstack_types::{
89
shared_filenames::{HOST_SHARED_DIR, SYS_CONFIG},
910
Platform,
@@ -94,12 +95,16 @@ impl HostApi {
9495
.map_err(|err| anyhow!("Failed to get sealing key: {err:?}"))?;
9596

9697
// verify the key provider quote
97-
let verified_report = dcap_qvl::collateral::get_collateral_and_verify(
98-
&provision.provider_quote,
99-
self.pccs_url.as_deref(),
100-
)
101-
.await
102-
.context("Failed to get quote collateral")?;
98+
let pccs_url = self
99+
.pccs_url
100+
.as_deref()
101+
.map(str::trim)
102+
.filter(|url| !url.is_empty())
103+
.unwrap_or(PHALA_PCCS_URL);
104+
let verified_report = CollateralClient::with_default_http(pccs_url)?
105+
.fetch_and_verify(&provision.provider_quote)
106+
.await
107+
.context("Failed to get quote collateral")?;
103108
validate_tcb(&verified_report)?;
104109
let sgx_report = verified_report
105110
.report

dstack/gateway/src/proxy/tls_passthough.rs

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use std::sync::atomic::Ordering;
77
use std::time::Duration;
88

99
use anyhow::{bail, Context, Result};
10-
use hickory_resolver::{lookup::TxtLookup, TokioAsyncResolver};
10+
use hickory_resolver::lookup::Lookup;
11+
use hickory_resolver::proto::rr::RData;
12+
use hickory_resolver::TokioResolver;
1113
use proxy_protocol::ProxyHeader;
1214
use tokio::{io::AsyncWriteExt, net::TcpStream, task::JoinSet, time::timeout};
1315
use tracing::{debug, info, warn};
@@ -53,7 +55,7 @@ impl AppAddress {
5355
pub(crate) struct AppAddressResolver {
5456
prefix: String,
5557
compat: bool,
56-
resolver: TokioAsyncResolver,
58+
resolver: TokioResolver,
5759
}
5860

5961
impl AppAddressResolver {
@@ -70,36 +72,39 @@ impl AppAddressResolver {
7072
}
7173
}
7274

73-
fn app_address_tokio_resolver_from_system_conf() -> Result<TokioAsyncResolver> {
74-
let (config, mut options) = hickory_resolver::system_conf::read_system_conf()
75-
.context("failed to read system dns config")?;
75+
fn app_address_tokio_resolver_from_system_conf() -> Result<TokioResolver> {
76+
let mut builder = TokioResolver::builder_tokio().context("failed to read system dns config")?;
7677

7778
// App-address records may appear shortly after a CVM/app is registered.
7879
// Reusing one resolver enables positive TXT caching, but we do not want a
7980
// transient NXDOMAIN/NODATA response to hide a newly-added app for too
8081
// long. Keep positive caching TTL-aware and cap negative caching.
81-
options.cache_size = APP_ADDRESS_DNS_CACHE_SIZE;
82+
let options = builder.options_mut();
83+
options.cache_size = APP_ADDRESS_DNS_CACHE_SIZE as u64;
8284
options.negative_min_ttl = Some(Duration::ZERO);
8385
options.negative_max_ttl = Some(APP_ADDRESS_NEGATIVE_CACHE_TTL);
8486

85-
Ok(TokioAsyncResolver::tokio(config, options))
87+
builder.build().context("failed to build dns resolver")
8688
}
8789

88-
fn parse_lookup(lookup: &TxtLookup, sni: &str, txt_domain: &str) -> Result<Option<AppAddress>> {
89-
let Some(txt_record) = lookup.iter().next() else {
90-
return Ok(None);
91-
};
92-
let Some(data) = txt_record.txt_data().first() else {
93-
return Ok(None);
94-
};
95-
AppAddress::parse(data)
96-
.with_context(|| format!("failed to parse app address for {sni} via {txt_domain}"))
97-
.map(Some)
90+
fn parse_lookup(lookup: &Lookup, sni: &str, txt_domain: &str) -> Result<Option<AppAddress>> {
91+
for answer in lookup.answers() {
92+
let RData::TXT(txt) = &answer.data else {
93+
continue;
94+
};
95+
let Some(data) = txt.txt_data.first() else {
96+
continue;
97+
};
98+
return AppAddress::parse(data)
99+
.with_context(|| format!("failed to parse app address for {sni} via {txt_domain}"))
100+
.map(Some);
101+
}
102+
Ok(None)
98103
}
99104

100105
/// Resolve app address by SNI. `resolver` is shared so its DNS cache is reused.
101106
async fn resolve_app_address(
102-
resolver: &TokioAsyncResolver,
107+
resolver: &TokioResolver,
103108
prefix: &str,
104109
sni: &str,
105110
compat: bool,

dstack/nsm-qvl/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ rustls-pki-types.workspace = true
3030
dcap-qvl-webpki = { workspace = true, features = ["alloc", "rustcrypto"] }
3131

3232
# CRL download
33-
reqwest = { workspace = true, features = ["rustls-tls"] }
33+
reqwest = { workspace = true, features = ["rustls"] }
3434

3535
[dev-dependencies]
3636
nsm-attest.workspace = true

dstack/ra-rpc/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ prpc.workspace = true
1515
rocket = { workspace = true, features = ["mtls"], optional = true }
1616
serde_json.workspace = true
1717
tracing.workspace = true
18-
reqwest = { workspace = true, default-features = false, features = ["rustls-tls", "charset"], optional = true }
18+
reqwest = { workspace = true, default-features = false, features = ["rustls", "charset"], optional = true }
1919

2020
ra-tls.workspace = true
2121
bon.workspace = true

dstack/ra-rpc/src/client.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ impl RaClientConfig {
4848
.tls_sni(true)
4949
.danger_accept_invalid_certs(self.tls_no_check)
5050
.danger_accept_invalid_hostnames(self.tls_no_check_hostname)
51-
.tls_built_in_root_certs(self.tls_built_in_root_certs)
5251
.connect_timeout(Duration::from_secs(5))
5352
.timeout(Duration::from_secs(60));
5453
if self.cert_validator.is_some() {
@@ -60,9 +59,20 @@ impl RaClientConfig {
6059
Identity::from_pem(identity_pem.as_bytes()).context("Failed to parse identity")?;
6160
builder = builder.identity(identity);
6261
}
63-
if let Some(ca) = self.tls_ca_cert {
64-
let ca = Certificate::from_pem(ca.as_bytes()).context("Failed to parse CA")?;
65-
builder = builder.add_root_certificate(ca);
62+
// reqwest 0.13 replaced tls_built_in_root_certs / add_root_certificate with
63+
// tls_certs_merge (keep platform roots) and tls_certs_only (custom roots only).
64+
// Hostname-check bypass also requires tls_certs_only on the rustls backend.
65+
let ca_cert = self
66+
.tls_ca_cert
67+
.as_deref()
68+
.map(|ca| Certificate::from_pem(ca.as_bytes()).context("Failed to parse CA"))
69+
.transpose()?;
70+
if self.tls_built_in_root_certs && !self.tls_no_check_hostname {
71+
if let Some(ca) = ca_cert {
72+
builder = builder.tls_certs_merge([ca]);
73+
}
74+
} else {
75+
builder = builder.tls_certs_only(ca_cert);
6676
}
6777
let client = builder.build().context("failed to create client")?;
6878
Ok(RaClient {

0 commit comments

Comments
 (0)