diff --git a/COPYRIGHT b/COPYRIGHT index 17ae94476..86302079a 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -14,12 +14,6 @@ operating system images. =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= -Contains modified hyper-proxy files [mod.rs, stream.rs, tunnel.rs] from -https://github.com/tafia/hyper-proxy 2021-09-20. -Copyright (c) 2017 Johann Tuffe. Licensed under the MIT License. - -=^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= - Contains aws-smithy-experimental from https://github.com/smithy-lang/smithy-rs/tree/release-2024-10-09. Licensed under the Apache-2.0 License. diff --git a/sources/Cargo.lock b/sources/Cargo.lock index ad107b7d5..b18b1b4d1 100644 --- a/sources/Cargo.lock +++ b/sources/Cargo.lock @@ -2543,14 +2543,14 @@ checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "headers" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ "base64 0.21.7", "bytes", "headers-core", - "http 0.2.12", + "http 1.1.0", "httpdate", "mime", "sha1", @@ -2558,11 +2558,11 @@ dependencies = [ [[package]] name = "headers-core" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http 0.2.12", + "http 1.1.0", ] [[package]] @@ -2763,6 +2763,26 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-http-proxy" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d06dbdfbacf34d996c6fb540a71a684a7aae9056c71951163af8a8a4c07b9a4" +dependencies = [ + "bytes", + "futures-util", + "headers", + "http 1.1.0", + "hyper 1.4.1", + "hyper-rustls 0.27.3", + "hyper-util", + "pin-project-lite", + "rustls-native-certs 0.7.3", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -2789,6 +2809,7 @@ dependencies = [ "http 1.1.0", "hyper 1.4.1", "hyper-util", + "log", "rustls 0.23.14", "rustls-native-certs 0.8.0", "rustls-pki-types", @@ -3651,29 +3672,25 @@ dependencies = [ "aws-sdk-ec2", "aws-sdk-eks", "aws-smithy-experimental", - "aws-smithy-runtime", "aws-smithy-types", "aws-types", "bottlerocket-modeled-types", "bottlerocket-settings-models", - "bytes", "constants", - "futures-util", "generate-readme", "headers", - "http 0.2.12", "httptest", - "hyper 0.14.30", - "hyper-rustls 0.24.2", + "hyper 1.4.1", + "hyper-http-proxy", + "hyper-rustls 0.27.3", + "hyper-util", "imdsclient", - "log", "rustls 0.23.14", "serde", "serde_json", "snafu", "tokio", "tokio-retry", - "tokio-rustls 0.24.1", "url", ] @@ -4096,6 +4113,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-native-certs" version = "0.8.0" @@ -5169,9 +5199,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777d57dcc6bb4cf084e3212e1858447222aa451f21b5e2452497d9100da65b91" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -5188,9 +5218,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", diff --git a/sources/Cargo.toml b/sources/Cargo.toml index 7e4ef0b5f..df7139157 100644 --- a/sources/Cargo.toml +++ b/sources/Cargo.toml @@ -143,15 +143,15 @@ glob = "0.3" gptman = { version = "1", default-features = false } handlebars = "4" h2 = "0.4" -headers = "0.3" +headers = "0.4" hex-literal = "0.4" http = "0.2" httparse = "1" httptest = "0.15" -hyper = { version = "0.14", default-features = false } -hyper-util = "0.1" -# FIXME: bump to 0.27 once hyper-proxy is dropped -hyper-rustls = { version = "0.24", default-features = false } +hyper = { version = "1", default-features = false } +hyper-http-proxy = "1" +hyper-rustls = { version = "0.27", default-features = false } +hyper-util = { version = "0.1", default-features = false } hyper-unix-connector = "0.2" indexmap = "2" ipnet = "2" @@ -194,9 +194,8 @@ syn = { version = "2", default-features = false } tar = { version = "0.4", default-features = false } tempfile = "3" test-case = "3" -tokio = { version = "~1.32", default-features = false } # LTS +tokio = { version = "~1.36", default-features = false } # LTS tokio-retry = "0.3" -tokio-rustls = "0.24" tokio-test = "0.4" tokio-tungstenite = { version = "0.20", default-features = false } tokio-util = "0.7" diff --git a/sources/api/apiclient/Cargo.toml b/sources/api/apiclient/Cargo.toml index 6dd71a59a..994640bc5 100644 --- a/sources/api/apiclient/Cargo.toml +++ b/sources/api/apiclient/Cargo.toml @@ -23,7 +23,7 @@ futures.workspace = true futures-channel.workspace = true http.workspace = true httparse.workspace = true -hyper = { workspace = true, features = ["client", "http1", "http2", "tcp"] } +hyper = { version = "0.14", features = ["client", "http1", "http2", "tcp"] } hyper-unix-connector.workspace = true libc.workspace = true log.workspace = true diff --git a/sources/api/pluto/Cargo.toml b/sources/api/pluto/Cargo.toml index 6df0f2f68..d38fcad41 100644 --- a/sources/api/pluto/Cargo.toml +++ b/sources/api/pluto/Cargo.toml @@ -16,33 +16,29 @@ fips = ["aws-lc-rs/fips", "aws-smithy-experimental/crypto-aws-lc-fips", "rustls/ source-groups = ["aws-smithy-experimental"] [dependencies] -bottlerocket-modeled-types.workspace = true -bottlerocket-settings-models.workspace = true -bytes.workspace = true -constants.workspace = true -futures-util.workspace = true -headers.workspace = true -http.workspace = true -hyper = { workspace = true, features = ["default"] } -hyper-rustls = { workspace = true, features = ["http2", "logging", "native-tokio", "tls12"] } -imdsclient = { workspace = true } aws-config.workspace = true aws-lc-rs = { workspace = true, features = ["bindgen"] } aws-sdk-eks.workspace = true aws-sdk-ec2.workspace = true -aws-types.workspace = true +aws-smithy-experimental = {workspace = true, features = ["crypto-aws-lc"]} aws-smithy-types.workspace = true -aws-smithy-runtime.workspace = true -aws-smithy-experimental = { workspace = true, features = ["crypto-aws-lc"] } +aws-types.workspace = true +bottlerocket-modeled-types.workspace = true +bottlerocket-settings-models.workspace = true +constants.workspace = true +headers.workspace = true +hyper = { workspace = true, features = ["default"] } +hyper-http-proxy.workspace = true +hyper-util = { workspace = true, features = ["client", "client-legacy", "tokio"] } +hyper-rustls = { workspace = true, features = ["http1","http2", "logging", "native-tokio", "tls12"] } +imdsclient.workspace = true rustls.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true snafu.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } tokio-retry.workspace = true -tokio-rustls.workspace = true url.workspace = true -log.workspace = true [build-dependencies] generate-readme.workspace = true diff --git a/sources/api/pluto/src/ec2.rs b/sources/api/pluto/src/ec2.rs index 5f3d1d22e..022768dbe 100644 --- a/sources/api/pluto/src/ec2.rs +++ b/sources/api/pluto/src/ec2.rs @@ -1,9 +1,6 @@ use crate::aws::sdk_config; -use crate::proxy; -#[cfg(feature = "fips")] -use aws_smithy_experimental::hyper_1_0::{CryptoMode, HyperClientBuilder as Hyper10ClientBuilder}; -#[cfg(not(feature = "fips"))] -use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder; +use crate::{proxy, PROVIDER}; +use aws_smithy_experimental::hyper_1_0::HyperClientBuilder; use aws_smithy_types::error::display::DisplayErrorContext; use snafu::{OptionExt, ResultExt, Snafu}; use std::time::Duration; @@ -55,13 +52,8 @@ where { let config = sdk_config(region).await; - #[cfg(not(feature = "fips"))] let client = build_client(https_proxy, no_proxy, config)?; - // FIXME!: support proxies in FIPS mode - #[cfg(feature = "fips")] - let client = build_client(config)?; - tokio::time::timeout( FETCH_PRIVATE_DNS_NAME_TIMEOUT, Retry::spawn( @@ -94,7 +86,6 @@ where .context(FetchPrivateDnsNameTimeoutSnafu)? } -#[cfg(not(feature = "fips"))] fn build_client( https_proxy: Option, no_proxy: Option<&[N]>, @@ -104,26 +95,16 @@ where H: AsRef, N: AsRef, { - let client = if let Some(https_proxy) = https_proxy { + let http_client = if let Some(https_proxy) = https_proxy { let http_connector = proxy::setup_http_client(https_proxy, no_proxy)?; - let http_client = HyperClientBuilder::new().build(http_connector); - let ec2_config = aws_sdk_ec2::config::Builder::from(&config) - .http_client(http_client) - .build(); - aws_sdk_ec2::Client::from_conf(ec2_config) + HyperClientBuilder::new() + .crypto_mode(PROVIDER) + .build_with_connector(http_connector) } else { - aws_sdk_ec2::Client::new(&config) + HyperClientBuilder::new() + .crypto_mode(PROVIDER) + .build_https() }; - - Ok(client) -} - -// FIXME!: support proxies in FIPS mode -#[cfg(feature = "fips")] -fn build_client(config: aws_config::SdkConfig) -> Result { - let http_client = Hyper10ClientBuilder::new() - .crypto_mode(CryptoMode::AwsLcFips) - .build_https(); let ec2_config = aws_sdk_ec2::config::Builder::from(&config) .http_client(http_client) .build(); diff --git a/sources/api/pluto/src/eks.rs b/sources/api/pluto/src/eks.rs index 1e6e786dc..8c9d18163 100644 --- a/sources/api/pluto/src/eks.rs +++ b/sources/api/pluto/src/eks.rs @@ -1,10 +1,7 @@ use crate::aws::sdk_config; -use crate::proxy; +use crate::{proxy, PROVIDER}; use aws_sdk_eks::types::KubernetesNetworkConfigResponse; -#[cfg(feature = "fips")] -use aws_smithy_experimental::hyper_1_0::{CryptoMode, HyperClientBuilder as Hyper10ClientBuilder}; -#[cfg(not(feature = "fips"))] -use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder; +use aws_smithy_experimental::hyper_1_0::HyperClientBuilder; use snafu::{OptionExt, ResultExt, Snafu}; use std::time::Duration; @@ -48,13 +45,8 @@ where { let config = sdk_config(region).await; - #[cfg(not(feature = "fips"))] let client = build_client(https_proxy, no_proxy, config)?; - // FIXME!: support proxies in FIPS mode - #[cfg(feature = "fips")] - let client = build_client(config)?; - tokio::time::timeout( EKS_DESCRIBE_CLUSTER_TIMEOUT, client.describe_cluster().name(cluster.to_owned()).send(), @@ -70,7 +62,6 @@ where }) } -#[cfg(not(feature = "fips"))] fn build_client( https_proxy: Option, no_proxy: Option<&[N]>, @@ -80,26 +71,16 @@ where H: AsRef, N: AsRef, { - let client = if let Some(https_proxy) = https_proxy { + let http_client = if let Some(https_proxy) = https_proxy { let http_connector = proxy::setup_http_client(https_proxy, no_proxy)?; - let http_client = HyperClientBuilder::new().build(http_connector); - let eks_config = aws_sdk_eks::config::Builder::from(&config) - .http_client(http_client) - .build(); - aws_sdk_eks::Client::from_conf(eks_config) + HyperClientBuilder::new() + .crypto_mode(PROVIDER) + .build_with_connector(http_connector) } else { - aws_sdk_eks::Client::new(&config) + HyperClientBuilder::new() + .crypto_mode(PROVIDER) + .build_https() }; - - Ok(client) -} - -// FIXME!: support proxies in FIPS mode -#[cfg(feature = "fips")] -fn build_client(config: aws_config::SdkConfig) -> Result { - let http_client = Hyper10ClientBuilder::new() - .crypto_mode(CryptoMode::AwsLcFips) - .build_https(); let eks_config = aws_sdk_eks::config::Builder::from(&config) .http_client(http_client) .build(); diff --git a/sources/api/pluto/src/hyper_proxy/LICENSE-MIT.md b/sources/api/pluto/src/hyper_proxy/LICENSE-MIT.md deleted file mode 100644 index 47d7815df..000000000 --- a/sources/api/pluto/src/hyper_proxy/LICENSE-MIT.md +++ /dev/null @@ -1,23 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 Johann Tuffe - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/sources/api/pluto/src/hyper_proxy/mod.rs b/sources/api/pluto/src/hyper_proxy/mod.rs deleted file mode 100644 index 4cd3fb882..000000000 --- a/sources/api/pluto/src/hyper_proxy/mod.rs +++ /dev/null @@ -1,302 +0,0 @@ -//! A Proxy Connector crate for Hyper based applications - -// Original Copyright 2017 Johann Tuffe. Licensed under the MIT License. -// Modifications Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - -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 std::{fmt, io, sync::Arc}; -use std::{ - future::Future, - pin::Pin, - task::{Context, Poll}, -}; - -pub use stream::ProxyStream; -use tokio::io::{AsyncRead, AsyncWrite}; - -use hyper_rustls::ConfigBuilderExt; -use tokio_rustls::rustls::{ClientConfig, ServerName}; -use tokio_rustls::TlsConnector; - -pub(crate) 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), -} - -/// A trait for matching between Destination and Uri -pub trait Dst { - /// Returns the connection scheme, e.g. "http" or "https" - fn scheme(&self) -> Option<&str>; - /// Returns the host of the connection - fn host(&self) -> Option<&str>; - /// Returns the port for the connection - fn port(&self) -> Option; -} - -impl Dst for Uri { - fn scheme(&self) -> Option<&str> { - self.scheme_str() - } - - fn host(&self) -> Option<&str> { - self.host() - } - - fn port(&self) -> Option { - self.port_u16() - } -} - -#[inline] -pub(crate) fn io_err>>(e: E) -> io::Error { - io::Error::new(io::ErrorKind::Other, e) -} - -/// A Custom struct to proxy custom uris -#[derive(Clone)] -#[allow(clippy::type_complexity)] -pub struct Custom(Arc, Option<&str>, Option) -> bool + Send + Sync>); - -impl fmt::Debug for Custom { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "_") - } -} - -impl, Option<&str>, Option) -> bool + Send + Sync + 'static> From - for Custom -{ - fn from(f: F) -> Custom { - Custom(Arc::new(f)) - } -} - -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, - } - } -} - -impl, Option<&str>, Option) -> bool + Send + Sync + 'static> From - for Intercept -{ - fn from(f: F) -> Intercept { - Intercept::Custom(f.into()) - } -} - -/// A Proxy struct -#[derive(Clone, Debug)] -pub struct Proxy { - intercept: Intercept, - force_connect: bool, - headers: HeaderMap, - uri: Uri, -} - -impl Proxy { - /// Create a new `Proxy` - pub fn new>(intercept: I, uri: Uri) -> Proxy { - Proxy { - intercept: intercept.into(), - uri, - headers: HeaderMap::new(), - force_connect: false, - } - } - - /// Set `Proxy` authorization - pub fn set_authorization(&mut self, credentials: Authorization) { - // In pluto, we use custom intercept for HTTPS traffic we might proxy based on the no proxy specification. - match self.intercept { - Intercept::Custom(_) | Intercept::Https => { - self.headers.typed_insert(ProxyAuthorization(credentials.0)); - } - _ => {} - } - } -} - -/// A wrapper around `Proxy`s with a connector. -#[derive(Clone)] -pub struct ProxyConnector { - proxies: Vec, - connector: C, - tls: Option, -} - -impl fmt::Debug for ProxyConnector { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - f, - "ProxyConnector {}{{ proxies: {:?}, connector: {:?} }}", - if self.tls.is_some() { - "" - } else { - "(unsecured)" - }, - self.proxies, - self.connector - ) - } -} - -impl ProxyConnector { - /// Create a new secured Proxies - pub fn new(connector: C) -> Result { - let config = ClientConfig::builder() - .with_safe_defaults() - .with_native_roots() - .with_no_client_auth(); - - let cfg = Arc::new(config); - let tls = TlsConnector::from(cfg); - - Ok(ProxyConnector { - proxies: Vec::new(), - connector, - tls: Some(tls), - }) - } - - /// Create a proxy connector and attach a particular proxy - pub fn from_proxy(connector: C, proxy: Proxy) -> Result { - let mut c = ProxyConnector::new(connector)?; - c.proxies.push(proxy); - Ok(c) - } - - fn match_proxy(&self, uri: &D) -> Option<&Proxy> { - self.proxies.iter().find(|p| p.intercept.matches(uri)) - } -} - -macro_rules! mtry { - ($e:expr) => { - match $e { - Ok(v) => v, - Err(e) => break Err(e.into()), - } - }; -} - -impl Service for ProxyConnector -where - C: Service, - C::Response: AsyncRead + AsyncWrite + Send + Unpin + 'static, - C::Future: Send + 'static, - C::Error: Into, -{ - type Response = ProxyStream; - type Error = io::Error; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - match self.connector.poll_ready(cx) { - Poll::Ready(Ok(())) => Poll::Ready(Ok(())), - Poll::Ready(Err(e)) => Poll::Ready(Err(io_err(e.into()))), - Poll::Pending => Poll::Pending, - } - } - - fn call(&mut self, uri: Uri) -> Self::Future { - if let (Some(p), Some(host)) = (self.match_proxy(&uri), uri.host()) { - if uri.scheme() == Some(&http::uri::Scheme::HTTPS) || p.force_connect { - let host = host.to_owned(); - let port = - uri.port_u16() - .unwrap_or(if uri.scheme() == Some(&http::uri::Scheme::HTTP) { - 80 - } else { - 443 - }); - let tunnel = tunnel::new(&host, port, &p.headers); - let connection = - proxy_dst(&uri, &p.uri).map(|proxy_url| self.connector.call(proxy_url)); - let tls = if uri.scheme() == Some(&http::uri::Scheme::HTTPS) { - self.tls.clone() - } else { - None - }; - - Box::pin(async move { - #[allow(clippy::never_loop)] - loop { - // this hack will gone once `try_blocks` will eventually stabilized - let proxy_stream = mtry!(mtry!(connection).await.map_err(io_err)); - let tunnel_stream = mtry!(tunnel.with_stream(proxy_stream).await); - - break match tls { - Some(tls) => { - let server_name: ServerName = - mtry!(host.as_str().try_into().map_err(io_err)); - let secure_stream = mtry!(tls - .connect(server_name, tunnel_stream) - .await - .map_err(io_err)); - - Ok(ProxyStream::Secured(Box::new(secure_stream))) - } - - None => Ok(ProxyStream::Regular(tunnel_stream)), - }; - } - }) - } else { - match proxy_dst(&uri, &p.uri) { - Ok(proxy_uri) => Box::pin( - self.connector - .call(proxy_uri) - .map_ok(ProxyStream::Regular) - .map_err(|err| io_err(err.into())), - ), - Err(err) => Box::pin(futures_util::future::err(io_err(err))), - } - } - } else { - Box::pin( - self.connector - .call(uri) - .map_ok(ProxyStream::NoProxy) - .map_err(|err| io_err(err.into())), - ) - } - } -} - -fn proxy_dst(dst: &Uri, proxy: &Uri) -> io::Result { - Uri::builder() - .scheme( - proxy - .scheme_str() - .ok_or_else(|| io_err(format!("proxy uri missing scheme: {}", proxy)))?, - ) - .authority( - proxy - .authority() - .ok_or_else(|| io_err(format!("proxy uri missing host: {}", proxy)))? - .clone(), - ) - .path_and_query(dst.path_and_query().unwrap().clone()) - .build() - .map_err(|err| io_err(format!("other error: {}", err))) -} diff --git a/sources/api/pluto/src/hyper_proxy/stream.rs b/sources/api/pluto/src/hyper_proxy/stream.rs deleted file mode 100644 index b762eaedd..000000000 --- a/sources/api/pluto/src/hyper_proxy/stream.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Original Copyright 2017 Johann Tuffe. Licensed under the MIT License. -// Modifications Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - -use std::io; -use std::pin::Pin; -use std::task::{Context, Poll}; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; - -use tokio_rustls::client::TlsStream as RustlsStream; - -use hyper::client::connect::{Connected, Connection}; - -pub type TlsStream = RustlsStream; - -/// A Proxy Stream wrapper -pub enum ProxyStream { - NoProxy(R), - Regular(R), - Secured(Box>), -} - -macro_rules! match_fn_pinned { - ($self:expr, $fn:ident, $ctx:expr, $buf:expr) => { - match $self.get_mut() { - ProxyStream::NoProxy(s) => Pin::new(s).$fn($ctx, $buf), - ProxyStream::Regular(s) => Pin::new(s).$fn($ctx, $buf), - ProxyStream::Secured(s) => Pin::new(s).$fn($ctx, $buf), - } - }; - - ($self:expr, $fn:ident, $ctx:expr) => { - match $self.get_mut() { - ProxyStream::NoProxy(s) => Pin::new(s).$fn($ctx), - ProxyStream::Regular(s) => Pin::new(s).$fn($ctx), - ProxyStream::Secured(s) => Pin::new(s).$fn($ctx), - } - }; -} - -impl AsyncRead for ProxyStream { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - match_fn_pinned!(self, poll_read, cx, buf) - } -} - -impl AsyncWrite for ProxyStream { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - match_fn_pinned!(self, poll_write, cx, buf) - } - - fn poll_write_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[io::IoSlice<'_>], - ) -> Poll> { - match_fn_pinned!(self, poll_write_vectored, cx, bufs) - } - - fn is_write_vectored(&self) -> bool { - match self { - ProxyStream::NoProxy(s) => s.is_write_vectored(), - ProxyStream::Regular(s) => s.is_write_vectored(), - ProxyStream::Secured(s) => s.is_write_vectored(), - } - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match_fn_pinned!(self, poll_flush, cx) - } - - fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match_fn_pinned!(self, poll_shutdown, cx) - } -} - -impl Connection for ProxyStream { - fn connected(&self) -> Connected { - match self { - ProxyStream::NoProxy(s) => s.connected(), - - ProxyStream::Regular(s) => s.connected().proxy(true), - - ProxyStream::Secured(s) => s.get_ref().0.connected().proxy(true), - } - } -} diff --git a/sources/api/pluto/src/hyper_proxy/tunnel.rs b/sources/api/pluto/src/hyper_proxy/tunnel.rs deleted file mode 100644 index c2e53452a..000000000 --- a/sources/api/pluto/src/hyper_proxy/tunnel.rs +++ /dev/null @@ -1,220 +0,0 @@ -// Original Copyright 2017 Johann Tuffe. Licensed under the MIT License. -// Modifications Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - -use crate::hyper_proxy::io_err; -use bytes::{buf::Buf, BytesMut}; -use http::HeaderMap; -use std::fmt::{self, Display, Formatter}; -use std::future::Future; -use std::io; -use std::pin::Pin; -use std::task::{Context, Poll}; -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; - -macro_rules! try_ready { - ($x:expr) => { - match $x { - core::task::Poll::Ready(Ok(x)) => x, - core::task::Poll::Ready(Err(e)) => return core::task::Poll::Ready(Err(e.into())), - core::task::Poll::Pending => return core::task::Poll::Pending, - } - }; -} - -pub(crate) struct TunnelConnect { - buf: BytesMut, -} - -impl TunnelConnect { - /// Change stream - pub fn with_stream(self, stream: S) -> Tunnel { - Tunnel { - buf: self.buf, - stream: Some(stream), - state: TunnelState::Writing, - } - } -} - -pub(crate) struct Tunnel { - buf: BytesMut, - stream: Option, - state: TunnelState, -} - -#[derive(Debug)] -enum TunnelState { - Writing, - Reading, -} - -struct HeadersDisplay<'a>(&'a HeaderMap); - -impl<'a> Display for HeadersDisplay<'a> { - fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - for (key, value) in self.0 { - let value_str = value.to_str().map_err(|_| fmt::Error)?; - write!(f, "{}: {}\r\n", key.as_str(), value_str)?; - } - - Ok(()) - } -} - -/// Creates a new tunnel through proxy -pub(crate) fn new(host: &str, port: u16, headers: &HeaderMap) -> TunnelConnect { - let buf = format!( - "CONNECT {0}:{1} HTTP/1.1\r\n\ - Host: {0}:{1}\r\n\ - {2}\ - \r\n", - host, - port, - HeadersDisplay(headers) - ) - .into_bytes(); - - TunnelConnect { - buf: buf.as_slice().into(), - } -} - -impl Future for Tunnel { - type Output = Result; - - fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { - if self.stream.is_none() { - panic!("must not poll after future is complete") - } - - let this = self.get_mut(); - - loop { - if let TunnelState::Writing = &this.state { - let fut = this.stream.as_mut().unwrap().write_buf(&mut this.buf); - futures_util::pin_mut!(fut); - let n = try_ready!(fut.poll(ctx)); - - if !this.buf.has_remaining() { - this.state = TunnelState::Reading; - this.buf.truncate(0); - } else if n == 0 { - return Poll::Ready(Err(io_err("unexpected EOF while tunnel writing"))); - } - } else { - let fut = this.stream.as_mut().unwrap().read_buf(&mut this.buf); - futures_util::pin_mut!(fut); - let n = try_ready!(fut.poll(ctx)); - - if n == 0 { - return Poll::Ready(Err(io_err("unexpected EOF while tunnel reading"))); - } else { - let read = &this.buf[..]; - if read.len() > 12 { - if read.starts_with(b"HTTP/1.1 200") || read.starts_with(b"HTTP/1.0 200") { - if read.ends_with(b"\r\n\r\n") { - return Poll::Ready(Ok(this.stream.take().unwrap())); - } - // else read more - } else { - let len = read.len().min(16); - return Poll::Ready(Err(io_err(format!( - "unsuccessful tunnel ({})", - String::from_utf8_lossy(&read[0..len]) - )))); - } - } - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::{HeaderMap, Tunnel}; - use futures_util::future::TryFutureExt; - use std::io::{Read, Write}; - use std::net::TcpListener; - use std::thread; - use tokio::net::TcpStream; - use tokio::runtime::Runtime; - - fn tunnel(conn: S, host: String, port: u16) -> Tunnel { - super::new(&host, port, &HeaderMap::new()).with_stream(conn) - } - - #[rustfmt::skip] - macro_rules! mock_tunnel { - () => {{ - mock_tunnel!( - b"\ - HTTP/1.1 200 OK\r\n\ - \r\n\ - " - ) - }}; - ($write:expr) => {{ - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let addr = listener.local_addr().unwrap(); - let connect_expected = format!( - "\ - CONNECT {0}:{1} HTTP/1.1\r\n\ - Host: {0}:{1}\r\n\ - \r\n\ - ", - addr.ip(), - addr.port() - ).into_bytes(); - - thread::spawn(move || { - let (mut sock, _) = listener.accept().unwrap(); - let mut buf = [0u8; 4096]; - let n = sock.read(&mut buf).unwrap(); - assert_eq!(&buf[..n], &connect_expected[..]); - - sock.write_all($write).unwrap(); - }); - addr - }}; - } - - #[test] - fn test_tunnel() { - let addr = mock_tunnel!(); - - let core = Runtime::new().unwrap(); - let work = TcpStream::connect(&addr); - let host = addr.ip().to_string(); - let port = addr.port(); - let work = work.and_then(|tcp| tunnel(tcp, host, port)); - - core.block_on(work).unwrap(); - } - - #[test] - fn test_tunnel_eof() { - let addr = mock_tunnel!(b"HTTP/1.1 200 OK"); - - let core = Runtime::new().unwrap(); - let work = TcpStream::connect(&addr); - let host = addr.ip().to_string(); - let port = addr.port(); - let work = work.and_then(|tcp| tunnel(tcp, host, port)); - - core.block_on(work).unwrap_err(); - } - - #[test] - fn test_tunnel_bad_response() { - let addr = mock_tunnel!(b"foo bar baz hallo"); - - let core = Runtime::new().unwrap(); - let work = TcpStream::connect(&addr); - let host = addr.ip().to_string(); - let port = addr.port(); - let work = work.and_then(|tcp| tunnel(tcp, host, port)); - - core.block_on(work).unwrap_err(); - } -} diff --git a/sources/api/pluto/src/main.rs b/sources/api/pluto/src/main.rs index 3ca79a56d..545389c3d 100644 --- a/sources/api/pluto/src/main.rs +++ b/sources/api/pluto/src/main.rs @@ -35,10 +35,10 @@ mod api; mod aws; mod ec2; mod eks; -mod hyper_proxy; mod proxy; use api::{settings_view_get, settings_view_set, SettingsViewDelta}; +use aws_smithy_experimental::hyper_1_0::CryptoMode; use bottlerocket_modeled_types::{KubernetesClusterDnsIp, KubernetesHostnameOverrideSource}; use imdsclient::ImdsClient; use snafu::{ensure, OptionExt, ResultExt}; @@ -56,6 +56,12 @@ const DEFAULT_10_RANGE_DNS_CLUSTER_IP: &str = "172.20.0.10"; const ENI_MAX_PODS_PATH: &str = "/usr/share/eks/eni-max-pods"; +// Shared crypto provider for HyperClients +#[cfg(not(feature = "fips"))] +const PROVIDER: CryptoMode = CryptoMode::AwsLc; +#[cfg(feature = "fips")] +const PROVIDER: CryptoMode = CryptoMode::AwsLcFips; + mod error { use crate::{api, ec2, eks}; use snafu::Snafu; diff --git a/sources/api/pluto/src/proxy.rs b/sources/api/pluto/src/proxy.rs index a4e3f6d4e..5aba4b00b 100644 --- a/sources/api/pluto/src/proxy.rs +++ b/sources/api/pluto/src/proxy.rs @@ -1,8 +1,9 @@ -use crate::hyper_proxy::{Proxy, ProxyConnector}; use headers::Authorization; -use hyper::client::HttpConnector; use hyper::Uri; -use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; +use hyper_http_proxy::{Proxy, ProxyConnector}; +use hyper_rustls::{ConfigBuilderExt, HttpsConnector}; +use hyper_util::client::legacy::connect::dns::GaiResolver; +use hyper_util::client::legacy::connect::HttpConnector; use snafu::{ResultExt, Snafu}; use url::Url; @@ -90,11 +91,19 @@ where )); } - let https_connector = HttpsConnectorBuilder::new() - .with_native_roots() + let mut base_connector = HttpConnector::new_with_resolver(GaiResolver::new()); + base_connector.enforce_http(false); + let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + .with_tls_config( + rustls::ClientConfig::builder() + .with_native_roots() + .expect("error with TLS configuration.") + .with_no_client_auth(), + ) .https_or_http() + .enable_http1() .enable_http2() - .build(); + .wrap_connector(base_connector); let proxy_connector = ProxyConnector::from_proxy(https_connector, proxy).context(ProxyConnectorSnafu)?; Ok(proxy_connector) diff --git a/sources/aws-smithy-experimental/Cargo.toml b/sources/aws-smithy-experimental/Cargo.toml index 6d486a7ac..b8fb7815a 100644 --- a/sources/aws-smithy-experimental/Cargo.toml +++ b/sources/aws-smithy-experimental/Cargo.toml @@ -19,7 +19,7 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(crypto_unstable)'] } aws-smithy-types = { workspace = true, features = ["http-body-1-x"] } aws-smithy-runtime-api = { workspace = true, features = ["client", "http-1x"] } aws-smithy-runtime = { workspace = true, features = ["client"] } -aws-smithy-async.workspace = true +aws-smithy-async.workspace = true h2.workspace = true hyper-util.workspace = true once_cell.workspace = true @@ -28,9 +28,8 @@ rustls.workspace = true tracing.workspace = true tokio.workspace = true tower.workspace = true -# FIXME: migrate these dependencies once we migrate to http-hyper-proxy -hyper = { version = "1", features = ["client", "http1", "http2"] } -hyper-rustls = { version = "0.27", features = ["http2", "http1", "native-tokio", "tls12"], default-features = false } +hyper = { workspace = true, features = ["client", "http1", "http2"] } +hyper-rustls = { workspace = true, features = ["http2", "http1", "native-tokio", "tls12"], default-features = false } http = "1" [dev-dependencies] diff --git a/sources/aws-smithy-experimental/src/hyper_1_0.rs b/sources/aws-smithy-experimental/src/hyper_1_0.rs index 82253dc1c..a7ee43edf 100644 --- a/sources/aws-smithy-experimental/src/hyper_1_0.rs +++ b/sources/aws-smithy-experimental/src/hyper_1_0.rs @@ -664,6 +664,18 @@ impl HyperClientBuilder { ) }) } + + /// Create a hyper client using a custom HTTP connector + pub fn build_with_connector(self, tcp_connector: C) -> SharedHttpClient + where + C: Clone + Send + Sync + 'static, + C: tower::Service, + C::Response: Connection + Read + Write + Send + Sync + Unpin + 'static, + C::Future: Unpin + Send + 'static, + C::Error: Into, + { + build_with_fn(self.client_builder, move || tcp_connector.clone()) + } } impl HyperClientBuilder { diff --git a/sources/deny.toml b/sources/deny.toml index 25d648b75..2e256c4d5 100644 --- a/sources/deny.toml +++ b/sources/deny.toml @@ -67,9 +67,9 @@ skip-tree = [ { name = "httptest", version = "=0.15" }, # schnauzer uses cached, which uses an older version of darling { name = "darling", version = "=0.14" }, - # reqwest brings a set of new dependencies that the aws-sdk doesn't - # support - { name = "reqwest", version = "=0.12" }, + # aws-smithy-experimental brings a set of new dependencies that + # the aws-sdk doesn't support + { name = "aws-smithy-experimental", version = "=0.1" }, ] [bans.workspace-dependencies]