feat(odoh): bootstrap-IP overrides + zero hedge for ODoH (post-deploy fixes)
Two issues surfaced from running mode = "odoh" against the live Hetzner relay as system DNS: 1. **Bootstrap deadlock.** The reqwest HTTPS client resolves the relay and target hostnames via system DNS. When numa is itself the system resolver, the ODoH client loops trying to resolve through itself. Adds optional `relay_ip` and `target_ip` to `[upstream]`, plumbed into reqwest's `resolve()` so the HTTPS client bypasses system DNS for those two hostnames. TLS still validates against the URL hostname, so a stale IP fails loudly rather than silently MITM'ing. 2. **2x relay load.** Default `hedge_ms = 10` triggers a duplicate in-flight query for every request. Useful for UDP/DoH/DoT (rescues tail latency cheaply); wasteful for ODoH (doubles HPKE seal/unseal, doubles sealed-byte footprint a passive observer can correlate, no latency win — relay hop dominates either way). Force-zero in oblivious mode regardless of configured hedge_ms. Validated end-to-end against odoh-relay.numa.rs → Cloudflare: 3 digs produced 3 forwarded_ok on the relay (was 6 before the hedge fix), upstream_transport.odoh ticks correctly.
This commit is contained in:
@@ -176,6 +176,25 @@ pub fn build_https_client() -> reqwest::Client {
|
||||
/// and benefit from a larger pool so warm connections survive concurrent
|
||||
/// fan-out.
|
||||
pub fn build_https_client_with_pool(pool_max_idle_per_host: usize) -> reqwest::Client {
|
||||
https_client_builder(pool_max_idle_per_host)
|
||||
.build()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// HTTPS client for the ODoH upstream, with bootstrap-IP overrides applied
|
||||
/// so relay/target hostname resolution can bypass system DNS.
|
||||
pub fn build_odoh_client(odoh: &crate::config::OdohUpstream) -> reqwest::Client {
|
||||
let mut builder = https_client_builder(1);
|
||||
if let Some(addr) = odoh.relay_bootstrap {
|
||||
builder = builder.resolve(&odoh.relay_host, addr);
|
||||
}
|
||||
if let Some(addr) = odoh.target_bootstrap {
|
||||
builder = builder.resolve(&odoh.target_host, addr);
|
||||
}
|
||||
builder.build().unwrap_or_default()
|
||||
}
|
||||
|
||||
fn https_client_builder(pool_max_idle_per_host: usize) -> reqwest::ClientBuilder {
|
||||
reqwest::Client::builder()
|
||||
.use_rustls_tls()
|
||||
.http2_initial_stream_window_size(65_535)
|
||||
@@ -185,8 +204,6 @@ pub fn build_https_client_with_pool(pool_max_idle_per_host: usize) -> reqwest::C
|
||||
.http2_keep_alive_timeout(Duration::from_secs(10))
|
||||
.pool_idle_timeout(Duration::from_secs(300))
|
||||
.pool_max_idle_per_host(pool_max_idle_per_host)
|
||||
.build()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn build_dot_connector() -> Result<tokio_rustls::TlsConnector> {
|
||||
|
||||
Reference in New Issue
Block a user