From f856b2bf0f61a039c592ac0f84eadd1dde38383d Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 22 May 2024 11:58:39 +0200 Subject: [PATCH] expose proxy url config on the endpoint --- iroh-net/src/endpoint.rs | 68 +++++++++++++++++++++++++++ iroh-net/src/magicsock.rs | 15 ++++++ iroh-net/src/magicsock/relay_actor.rs | 6 ++- iroh-net/src/relay/http/client.rs | 58 ----------------------- 4 files changed, 88 insertions(+), 59 deletions(-) diff --git a/iroh-net/src/endpoint.rs b/iroh-net/src/endpoint.rs index 130db7e70d..e5cf7936ec 100644 --- a/iroh-net/src/endpoint.rs +++ b/iroh-net/src/endpoint.rs @@ -16,6 +16,7 @@ use derive_more::Debug; use futures_lite::{Stream, StreamExt}; use tokio_util::sync::{CancellationToken, WaitForCancellationFuture}; use tracing::{debug, info_span, trace, warn}; +use url::Url; use crate::{ config, @@ -58,6 +59,7 @@ pub struct Builder { concurrent_connections: Option, keylog: bool, discovery: Option>, + proxy_url: Option, /// Path for known peers. See [`Builder::peers_data_path`]. peers_path: Option, dns_resolver: Option, @@ -75,6 +77,7 @@ impl Default for Builder { concurrent_connections: Default::default(), keylog: Default::default(), discovery: Default::default(), + proxy_url: None, peers_path: None, dns_resolver: None, #[cfg(any(test, feature = "test-utils"))] @@ -100,6 +103,23 @@ impl Builder { self } + /// Set an explicit proxy url to proxy all HTTP(S) traffic through. + pub fn proxy_url(mut self, url: Url) -> Self { + self.proxy_url.replace(url); + self + } + + /// Set the proxy url from the environment, in this order: + /// + /// - `HTTP_PROXY` + /// - `http_proxy` + /// - `HTTPS_PROXY` + /// - `https_proxy` + pub fn proxy_from_env(mut self) -> Self { + self.proxy_url = proxy_url_from_env(); + self + } + /// If *keylog* is `true` and the KEYLOGFILE environment variable is present it will be /// considered a filename to which the TLS pre-master keys are logged. This can be useful /// to be able to decrypt captured traffic for debugging purposes. @@ -225,6 +245,7 @@ impl Builder { relay_map, nodes_path: self.peers_path, discovery: self.discovery, + proxy_url: self.proxy_url, dns_resolver, #[cfg(any(test, feature = "test-utils"))] insecure_skip_relay_cert_verify: self.insecure_skip_relay_cert_verify, @@ -831,6 +852,53 @@ fn try_send_rtt_msg(conn: &quinn::Connection, magic_ep: &Endpoint) { } } +/// Read a proxy url from the environemnt, in this order +/// +/// - `HTTP_PROXY` +/// - `http_proxy` +/// - `HTTPS_PROXY` +/// - `https_proxy` +fn proxy_url_from_env() -> Option { + if let Some(url) = std::env::var("HTTP_PROXY") + .ok() + .and_then(|s| s.parse::().ok()) + { + if is_cgi() { + warn!("HTTP_PROXY environment variable ignored in CGI"); + } else { + return Some(url); + } + } + if let Some(url) = std::env::var("http_proxy") + .ok() + .and_then(|s| s.parse::().ok()) + { + return Some(url); + } + if let Some(url) = std::env::var("HTTPS_PROXY") + .ok() + .and_then(|s| s.parse::().ok()) + { + return Some(url); + } + if let Some(url) = std::env::var("https_proxy") + .ok() + .and_then(|s| s.parse::().ok()) + { + return Some(url); + } + + None +} + +/// Check if we are being executed in a CGI context. +/// +/// If so, a malicious client can send the `Proxy:` header, and it will +/// be in the `HTTP_PROXY` env var. So we don't use it :) +fn is_cgi() -> bool { + std::env::var_os("REQUEST_METHOD").is_some() +} + // TODO: These tests could still be flaky, lets fix that: // https://github.com/n0-computer/iroh/issues/1183 #[cfg(test)] diff --git a/iroh-net/src/magicsock.rs b/iroh-net/src/magicsock.rs index 8243f4191d..f5c745dbc7 100644 --- a/iroh-net/src/magicsock.rs +++ b/iroh-net/src/magicsock.rs @@ -46,6 +46,7 @@ use tokio_util::sync::CancellationToken; use tracing::{ debug, error, error_span, info, info_span, instrument, trace, trace_span, warn, Instrument, }; +use url::Url; use watchable::Watchable; use crate::{ @@ -117,6 +118,9 @@ pub(super) struct Options { /// configuration. pub dns_resolver: DnsResolver, + /// Proxy configuration. + pub proxy_url: Option, + /// Skip verification of SSL certificates from relay servers /// /// May only be used in tests. @@ -132,6 +136,7 @@ impl Default for Options { relay_map: RelayMap::empty(), nodes_path: None, discovery: None, + proxy_url: None, dns_resolver: crate::dns::default_resolver().clone(), #[cfg(any(test, feature = "test-utils"))] insecure_skip_relay_cert_verify: false, @@ -170,6 +175,9 @@ pub(super) struct MagicSock { relay_actor_sender: mpsc::Sender, /// String representation of the node_id of this node. me: String, + /// Proxy + proxy_url: Option, + /// Used for receiving relay messages. relay_recv_receiver: flume::Receiver, /// Stores wakers, to be called when relay_recv_ch receives new data. @@ -249,6 +257,11 @@ impl MagicSock { self.my_relay.get() } + /// Get the current proxy configuration. + pub fn proxy_url(&self) -> Option<&Url> { + self.proxy_url.as_ref() + } + /// Sets the relay node with the best latency. /// /// If we are not connected to any relay nodes, set this to `None`. @@ -1283,6 +1296,7 @@ impl Handle { discovery, nodes_path, dns_resolver, + proxy_url, #[cfg(any(test, feature = "test-utils"))] insecure_skip_relay_cert_verify, } = opts; @@ -1341,6 +1355,7 @@ impl Handle { me, port: AtomicU16::new(port), secret_key, + proxy_url, local_addrs: std::sync::RwLock::new((ipv4_addr, ipv6_addr)), closing: AtomicBool::new(false), closed: AtomicBool::new(false), diff --git a/iroh-net/src/magicsock/relay_actor.rs b/iroh-net/src/magicsock/relay_actor.rs index ca46d2109e..ecae432cf3 100644 --- a/iroh-net/src/magicsock/relay_actor.rs +++ b/iroh-net/src/magicsock/relay_actor.rs @@ -479,7 +479,11 @@ impl RelayActor { let url1 = url.clone(); // building a client dials the relay - let builder = relay::http::ClientBuilder::new(url1.clone()) + let mut builder = relay::http::ClientBuilder::new(url1.clone()); + if let Some(url) = self.msock.proxy_url() { + builder = builder.proxy_url(url.clone()); + } + let builder = builder .address_family_selector(move || { let ipv6_reported = ipv6_reported.clone(); Box::pin(async move { ipv6_reported.load(Ordering::Relaxed) }) diff --git a/iroh-net/src/relay/http/client.rs b/iroh-net/src/relay/http/client.rs index 2de2e5fda3..a381dc6970 100644 --- a/iroh-net/src/relay/http/client.rs +++ b/iroh-net/src/relay/http/client.rs @@ -297,17 +297,6 @@ impl ClientBuilder { self } - /// Set the proxy url from the environment, in this order: - /// - /// - `HTTP_PROXY` - /// - `http_proxy` - /// - `HTTPS_PROXY` - /// - `https_proxy` - pub fn proxy_from_env(mut self) -> Self { - self.proxy_url = proxy_url_from_env(); - self - } - /// Disable http proxy entirely, no matter the environment variables. pub fn no_proxy(mut self) -> Self { self.no_proxy = true; @@ -1045,53 +1034,6 @@ fn url_port(url: &Url) -> Option { } } -/// Read a proxy url from the environemnt, in this order -/// -/// - `HTTP_PROXY` -/// - `http_proxy` -/// - `HTTPS_PROXY` -/// - `https_proxy` -fn proxy_url_from_env() -> Option { - if let Some(url) = std::env::var("HTTP_PROXY") - .ok() - .and_then(|s| s.parse::().ok()) - { - if is_cgi() { - warn!("HTTP_PROXY environment variable ignored in CGI"); - } else { - return Some(url); - } - } - if let Some(url) = std::env::var("http_proxy") - .ok() - .and_then(|s| s.parse::().ok()) - { - return Some(url); - } - if let Some(url) = std::env::var("HTTPS_PROXY") - .ok() - .and_then(|s| s.parse::().ok()) - { - return Some(url); - } - if let Some(url) = std::env::var("https_proxy") - .ok() - .and_then(|s| s.parse::().ok()) - { - return Some(url); - } - - None -} - -/// Check if we are being executed in a CGI context. -/// -/// If so, a malicious client can send the `Proxy:` header, and it will -/// be in the `HTTP_PROXY` env var. So we don't use it :) -fn is_cgi() -> bool { - std::env::var_os("REQUEST_METHOD").is_some() -} - #[cfg(test)] mod tests { use anyhow::{bail, Result};