diff --git a/sources/Cargo.lock b/sources/Cargo.lock index 4138810feba..b0ce8cb9f71 100644 --- a/sources/Cargo.lock +++ b/sources/Cargo.lock @@ -3175,6 +3175,7 @@ dependencies = [ "tokio", "tokio-retry", "tokio-rustls", + "url", ] [[package]] diff --git a/sources/api/pluto/Cargo.toml b/sources/api/pluto/Cargo.toml index e948ed40e7d..ba26f3f965f 100644 --- a/sources/api/pluto/Cargo.toml +++ b/sources/api/pluto/Cargo.toml @@ -31,6 +31,7 @@ snafu = "0.7" tokio = { version = "~1.32", default-features = false, features = ["macros", "rt-multi-thread"] } # LTS tokio-retry = "0.3" tokio-rustls = "0.23" +url = "2" [build-dependencies] bottlerocket-variant = { version = "0.1", path = "../../bottlerocket-variant" } diff --git a/sources/api/pluto/src/hyper_proxy/mod.rs b/sources/api/pluto/src/hyper_proxy/mod.rs index a7fbe900a71..7cf60587a6e 100644 --- a/sources/api/pluto/src/hyper_proxy/mod.rs +++ b/sources/api/pluto/src/hyper_proxy/mod.rs @@ -5,10 +5,10 @@ mod stream; mod tunnel; +use futures_util::future::TryFutureExt; +use headers::{authorization::Credentials, Authorization, HeaderMapExt, ProxyAuthorization}; use http::header::HeaderMap; use hyper::{service::Service, Uri}; - -use futures_util::future::TryFutureExt; use std::{fmt, io, sync::Arc}; use std::{ future::Future, @@ -27,7 +27,12 @@ type BoxError = Box; /// The Intercept enum to filter connections #[derive(Debug, Clone)] +#[allow(dead_code)] pub enum Intercept { + /// Only https connections will go through proxy + Https, + /// No connection will go through this proxy + None, /// A custom intercept Custom(Custom), } @@ -84,7 +89,9 @@ impl Intercept { /// A function to check if given `Uri` is proxied pub fn matches(&self, uri: &D) -> bool { match (self, uri.scheme()) { + (&Intercept::Https, Some("https")) => true, (&Intercept::Custom(Custom(ref f)), _) => f(uri.scheme(), uri.host(), uri.port()), + _ => false, } } } @@ -116,6 +123,20 @@ impl Proxy { force_connect: false, } } + + /// Set `Proxy` authorization + pub fn set_authorization(&mut self, credentials: Authorization) { + match self.intercept { + Intercept::Https => { + self.headers.typed_insert(ProxyAuthorization(credentials.0)); + } + _ => { + self.headers + .typed_insert(Authorization(credentials.0.clone())); + self.headers.typed_insert(ProxyAuthorization(credentials.0)); + } + } + } } /// A wrapper around `Proxy`s with a connector. diff --git a/sources/api/pluto/src/proxy.rs b/sources/api/pluto/src/proxy.rs index 24ba7818b8a..b97b98cb139 100644 --- a/sources/api/pluto/src/proxy.rs +++ b/sources/api/pluto/src/proxy.rs @@ -1,8 +1,10 @@ use crate::hyper_proxy::{Proxy, ProxyConnector}; +use headers::Authorization; use hyper::Uri; use hyper_rustls::HttpsConnectorBuilder; use snafu::{ResultExt, Snafu}; use std::env; +use url::Url; #[derive(Debug, Snafu)] pub(super) enum Error { @@ -12,6 +14,12 @@ pub(super) enum Error { source: hyper::http::uri::InvalidUri, }, + #[snafu(display("Unable to parse '{}' as URL: {}", input, source))] + UrlParse { + input: String, + source: url::ParseError, + }, + #[snafu(display("Failed to create proxy creator: {}", source))] ProxyConnector { source: std::io::Error }, } @@ -75,7 +83,19 @@ pub(crate) fn setup_http_client( input: &https_proxy, })?; } - let proxy = Proxy::new(intercept, proxy_uri); + let mut proxy = Proxy::new(intercept, proxy_uri); + // Parse https_proxy as URL to extract out auth information if any + let proxy_url = Url::parse(&https_proxy).context(UrlParseSnafu { + input: &https_proxy, + })?; + + if !proxy_url.username().is_empty() || proxy_url.password().is_some() { + proxy.set_authorization(Authorization::basic( + proxy_url.username(), + proxy_url.password().unwrap_or_default(), + )); + } + let https_connector = HttpsConnectorBuilder::new() .with_native_roots() .https_or_http()