From f6817210c96c15b5edbcfeab5f971b364825b83d Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 19 Mar 2026 11:57:37 +0100 Subject: [PATCH 01/11] Make both nested and inner only listeners --- src/attested_get.rs | 6 +- src/file_server.rs | 16 +- src/lib.rs | 593 ++++++++++++++++++++++++++++++++------------ src/main.rs | 54 ++-- 4 files changed, 474 insertions(+), 195 deletions(-) diff --git a/src/attested_get.rs b/src/attested_get.rs index 3b4575f..97b346d 100644 --- a/src/attested_get.rs +++ b/src/attested_get.rs @@ -78,12 +78,14 @@ mod tests { // Setup a proxy server targetting the static file server let proxy_server = ProxyServer::new_with_tls_config( - cert_chain, - server_config, + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), + false, + Some("localhost".to_string()), ) .await .unwrap(); diff --git a/src/file_server.rs b/src/file_server.rs index 424008e..8390c9f 100644 --- a/src/file_server.rs +++ b/src/file_server.rs @@ -7,8 +7,9 @@ use tower_http::services::ServeDir; /// Setup a static file server serving the given directory, and a proxy server targetting it pub async fn attested_file_server( path_to_serve: PathBuf, - cert_and_key: TlsCertAndKey, - listen_addr: impl ToSocketAddrs, + outer_cert_and_key: Option, + outer_listen_addr: impl ToSocketAddrs, + inner_listen_addr: impl ToSocketAddrs, attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, client_auth: bool, @@ -16,8 +17,9 @@ pub async fn attested_file_server( let target_addr = static_file_server(path_to_serve).await?; let server = ProxyServer::new( - cert_and_key, - listen_addr, + outer_cert_and_key, + Some(outer_listen_addr), + inner_listen_addr, target_addr.to_string(), attestation_generator, attestation_verifier, @@ -99,12 +101,14 @@ mod tests { // Setup a proxy server targetting the static file server let proxy_server = ProxyServer::new_with_tls_config( - cert_chain, - server_config, + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), + false, + Some("localhost".to_string()), ) .await .unwrap(); diff --git a/src/lib.rs b/src/lib.rs index cf4c327..a36d5b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ use thiserror::Error; use tokio::io::{self, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream, ToSocketAddrs}; use tokio::sync::{mpsc, oneshot}; +use tokio_rustls::TlsAcceptor; use tokio_rustls::rustls::server::{VerifierBuilderError, WebPkiClientVerifier}; use tokio_rustls::rustls::{ self, ClientConfig, RootCertStore, ServerConfig, @@ -46,6 +47,7 @@ const SERVER_RECONNECT_MAX_BACKOFF_SECS: u64 = 120; const KEEP_ALIVE_INTERVAL: u64 = 30; const KEEP_ALIVE_TIMEOUT: u64 = 10; +const DEFAULT_INNER_CERTIFICATE_NAME: &str = "localhost"; type RequestWithResponseSender = ( http::Request, @@ -132,111 +134,123 @@ pub async fn get_inner_tls_cert_with_config( /// A TLS over TCP server which provides an attestation before forwarding traffic to a given target address pub struct ProxyServer { - nesting_tls_acceptor: NestingTlsAcceptor, - /// The underlying TCP listener - listener: Arc, + outer_listener: Option>, + outer_tls_acceptor: Option, + inner_listener: Arc, + inner_tls_acceptor: TlsAcceptor, /// The address/hostname of the target service we are proxying to target: String, } impl ProxyServer { - pub async fn new( - cert_and_key: TlsCertAndKey, - local: impl ToSocketAddrs, + /// Start with dual listeners. The outer nested-TLS listener is optional. + pub async fn new( + outer_cert_and_key: Option, + outer_local: Option, + inner_local: impl ToSocketAddrs, target: String, attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, client_auth: bool, - ) -> Result { - let outer_server_config = if client_auth { - let root_store = - RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); - let verifier = WebPkiClientVerifier::builder(Arc::new(root_store)).build()?; - - ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_client_cert_verifier(verifier) - .with_single_cert( - cert_and_key.cert_chain.clone(), - cert_and_key.key.clone_key(), - )? - } else { - ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_no_client_auth() - .with_single_cert( - cert_and_key.cert_chain.clone(), - cert_and_key.key.clone_key(), - )? + ) -> Result + where + O: ToSocketAddrs, + { + if outer_cert_and_key.is_some() && outer_local.is_none() { + return Err(ProxyError::OuterTlsWithoutOuterListener); + } + + let outer_certificate_name = outer_cert_and_key + .as_ref() + .map(|cert_and_key| certificate_identity_from_chain(&cert_and_key.cert_chain)) + .transpose()?; + let outer_server_config = match outer_cert_and_key { + Some(cert_and_key) => { + let config = if client_auth { + let root_store = + RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); + let verifier = WebPkiClientVerifier::builder(Arc::new(root_store)).build()?; + + ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) + .with_client_cert_verifier(verifier) + .with_single_cert( + cert_and_key.cert_chain.clone(), + cert_and_key.key.clone_key(), + )? + } else { + ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) + .with_no_client_auth() + .with_single_cert( + cert_and_key.cert_chain.clone(), + cert_and_key.key.clone_key(), + )? + }; + Some(config) + } + None => None, }; - Self::new_with_tls_config_and_client_auth( - cert_and_key.cert_chain, + Self::new_with_tls_config( outer_server_config, - local, + outer_local, + inner_local, target, attestation_generator, attestation_verifier, client_auth, + outer_certificate_name, ) .await } /// Start with preconfigured TLS - pub async fn new_with_tls_config( - cert_chain: Vec>, - outer_server_config: ServerConfig, - local: impl ToSocketAddrs, - target: String, - attestation_generator: AttestationGenerator, - attestation_verifier: AttestationVerifier, - ) -> Result { - Self::new_with_tls_config_and_client_auth( - cert_chain, - outer_server_config, - local, - target, - attestation_generator, - attestation_verifier, - false, - ) - .await - } - - /// Start with preconfigured TLS and require client auth on both nested sessions - pub async fn new_with_tls_config_and_client_auth( - cert_chain: Vec>, - outer_server_config: ServerConfig, - local: impl ToSocketAddrs, + pub async fn new_with_tls_config( + outer_server_config: Option, + outer_local: Option, + inner_local: impl ToSocketAddrs, target: String, attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, client_auth: bool, - ) -> Result { - let server_name = certificate_identity_from_chain(&cert_chain)?; - let inner_cert_resolver = - build_attested_cert_resolver(attestation_generator, &cert_chain).await?; - - let mut inner_server_config = if client_auth { - let attested_cert_verifier = - AttestedCertificateVerifier::new(None, attestation_verifier)?; - ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_client_cert_verifier(Arc::new(attested_cert_verifier)) - .with_cert_resolver(Arc::new(inner_cert_resolver)) - } else { - let _ = server_name; - ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_no_client_auth() - .with_cert_resolver(Arc::new(inner_cert_resolver)) - }; - - ensure_proxy_alpn_protocols(&mut inner_server_config.alpn_protocols); + certificate_name: Option, + ) -> Result + where + O: ToSocketAddrs, + { + if outer_server_config.is_some() && outer_local.is_none() { + return Err(ProxyError::OuterTlsWithoutOuterListener); + } - let nesting_tls_acceptor = - NestingTlsAcceptor::new(Arc::new(outer_server_config), Arc::new(inner_server_config)); - let listener = TcpListener::bind(local).await?; + let inner_server_config = Arc::new( + build_inner_server_config( + attestation_generator, + attestation_verifier, + client_auth, + certificate_name, + ) + .await?, + ); + let inner_listener = Arc::new(TcpListener::bind(inner_local).await?); + let inner_tls_acceptor = TlsAcceptor::from(inner_server_config.clone()); + + let (outer_listener, outer_tls_acceptor) = match (outer_server_config, outer_local) { + (Some(outer_server_config), Some(outer_local)) => { + let outer_listener = Arc::new(TcpListener::bind(outer_local).await?); + let acceptor = NestingTlsAcceptor::new( + Arc::new(outer_server_config), + inner_server_config.clone(), + ); + (Some(outer_listener), Some(acceptor)) + } + (Some(_), None) => return Err(ProxyError::OuterTlsWithoutOuterListener), + (None, _) => (None, None), + }; Ok(Self { - nesting_tls_acceptor, - listener: listener.into(), + outer_listener, + outer_tls_acceptor, + inner_listener, + inner_tls_acceptor, target, }) } @@ -246,33 +260,92 @@ impl ProxyServer { /// Returns the handle for the task handling the connection pub async fn accept(&self) -> Result, ProxyError> { let target = self.target.clone(); - let (inbound, client_addr) = self.listener.accept().await?; - let nesting_tls_acceptor = self.nesting_tls_acceptor.clone(); + let outer_listener = self.outer_listener.clone(); + let outer_tls_acceptor = self.outer_tls_acceptor.clone(); + let inner_listener = self.inner_listener.clone(); + let inner_tls_acceptor = self.inner_tls_acceptor.clone(); + + let join_handle = match (outer_listener, outer_tls_acceptor) { + (Some(outer_listener), Some(outer_tls_acceptor)) => { + let ((inbound, client_addr), use_outer) = tokio::select! { + accepted = outer_listener.accept() => (accepted?, true), + accepted = inner_listener.accept() => (accepted?, false), + }; - let join_handle = tokio::spawn(async move { - match nesting_tls_acceptor.accept(inbound).await { - Ok(tls_stream) => { - if let Err(err) = Self::handle_connection(tls_stream, target, client_addr).await - { - warn!("Failed to handle connection: {err}"); + tokio::spawn(async move { + if use_outer { + match outer_tls_acceptor.accept(inbound).await { + Ok(tls_stream) => { + if let Err(err) = + Self::handle_outer_connection(tls_stream, target, client_addr) + .await + { + warn!("Failed to handle outer connection: {err}"); + } + } + Err(err) => { + warn!("Outer attestation exchange failed: {err}"); + } + } + } else { + match inner_tls_acceptor.accept(inbound).await { + Ok(tls_stream) => { + if let Err(err) = + Self::handle_inner_connection(tls_stream, target, client_addr) + .await + { + warn!("Failed to handle inner connection: {err}"); + } + } + Err(err) => { + warn!("Inner attestation exchange failed: {err}"); + } + } } - } - Err(err) => { - warn!("Attestation exchange failed: {err}"); - } + }) } - }); + _ => { + let (inbound, client_addr) = inner_listener.accept().await?; + tokio::spawn(async move { + match inner_tls_acceptor.accept(inbound).await { + Ok(tls_stream) => { + if let Err(err) = + Self::handle_inner_connection(tls_stream, target, client_addr).await + { + warn!("Failed to handle inner connection: {err}"); + } + } + Err(err) => { + warn!("Inner attestation exchange failed: {err}"); + } + } + }) + } + }; Ok(join_handle) } /// Helper to get the socket address of the underlying TCP listener pub fn local_addr(&self) -> std::io::Result { - self.listener.local_addr() + match &self.outer_listener { + Some(listener) => listener.local_addr(), + None => self.inner_listener.local_addr(), + } } - /// Handle an incoming connection from a proxy-client - async fn handle_connection( + pub fn outer_local_addr(&self) -> std::io::Result> { + self.outer_listener + .as_ref() + .map(|listener| listener.local_addr()) + .transpose() + } + + pub fn inner_local_addr(&self) -> std::io::Result { + self.inner_listener.local_addr() + } + + async fn handle_outer_connection( tls_stream: NestingTlsStream, target: String, client_addr: SocketAddr, @@ -280,7 +353,29 @@ impl ProxyServer { debug!("[proxy-server] accepted connection"); let http_version = HttpVersion::from_negotiated_protocol_server(&tls_stream); + Self::serve_tls_stream(tls_stream, http_version, target, client_addr).await + } + async fn handle_inner_connection( + tls_stream: tokio_rustls::server::TlsStream, + target: String, + client_addr: SocketAddr, + ) -> Result<(), ProxyError> { + debug!("[proxy-server] accepted inner-only connection"); + + let http_version = HttpVersion::from_negotiated_protocol_server(&tls_stream); + Self::serve_tls_stream(tls_stream, http_version, target, client_addr).await + } + + async fn serve_tls_stream( + tls_stream: IO, + http_version: HttpVersion, + target: String, + client_addr: SocketAddr, + ) -> Result<(), ProxyError> + where + IO: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static, + { // Setup a request handler let service = service_fn(move |mut req| { debug!("[proxy-server] Handling request {req:?}"); @@ -457,8 +552,11 @@ impl ProxyClient { let attested_cert_verifier = AttestedCertificateVerifier::new(None, attestation_verifier)?; let mut inner_client_config = if let Some(cert_chain) = cert_chain.as_ref() { - let inner_cert_resolver = - build_attested_cert_resolver(attestation_generator, cert_chain).await?; + let inner_cert_resolver = build_attested_cert_resolver( + attestation_generator, + Some(certificate_identity_from_chain(cert_chain)?), + ) + .await?; ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) .dangerous() .with_custom_certificate_verifier(Arc::new(attested_cert_verifier)) @@ -803,6 +901,8 @@ pub enum ProxyError { MpscSend, #[error("Client auth must be configured on both the inner and outer TLS sessions")] ClientAuthMisconfigured, + #[error("Outer TLS configuration requires an outer listener address")] + OuterTlsWithoutOuterListener, } impl From> for ProxyError { @@ -835,13 +935,40 @@ fn certificate_identity_from_chain( async fn build_attested_cert_resolver( attestation_generator: AttestationGenerator, - cert_chain: &[CertificateDer<'static>], + certificate_name: Option, ) -> Result { - let certificate_name = certificate_identity_from_chain(cert_chain)?; - Ok( - AttestedCertificateResolver::new(attestation_generator, None, certificate_name, vec![]) - .await?, + Ok(AttestedCertificateResolver::new( + attestation_generator, + None, + certificate_name.unwrap_or_else(|| DEFAULT_INNER_CERTIFICATE_NAME.to_string()), + vec![], ) + .await?) +} + +async fn build_inner_server_config( + attestation_generator: AttestationGenerator, + attestation_verifier: AttestationVerifier, + client_auth: bool, + certificate_name: Option, +) -> Result { + let inner_cert_resolver = + build_attested_cert_resolver(attestation_generator, certificate_name).await?; + + let mut inner_server_config = if client_auth { + let attested_cert_verifier = AttestedCertificateVerifier::new(None, attestation_verifier)?; + ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) + .with_client_cert_verifier(Arc::new(attested_cert_verifier)) + .with_cert_resolver(Arc::new(inner_cert_resolver)) + } else { + ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) + .with_no_client_auth() + .with_cert_resolver(Arc::new(inner_cert_resolver)) + }; + + ensure_proxy_alpn_protocols(&mut inner_server_config.alpn_protocols); + + Ok(inner_server_config) } /// If no port was provided, default to 443 @@ -882,6 +1009,7 @@ where #[cfg(test)] mod tests { use attestation::{AttestationType, measurements::MeasurementPolicy}; + use tokio_rustls::TlsConnector; use super::*; use test_helpers::{ @@ -906,25 +1034,94 @@ mod tests { } #[tokio::test(flavor = "multi_thread")] - async fn http_proxy_negotiates_http2_by_default() { + async fn dual_listener_server_reports_expected_addresses() { let target_addr = example_http_service().await; let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); - let (server_config, outer_client_config) = - generate_tls_config(cert_chain.clone(), private_key); + let tls_cert_and_key = TlsCertAndKey { + cert_chain, + key: private_key, + }; - let proxy_server = ProxyServer::new_with_tls_config( + let dual_listener_server = ProxyServer::new( + Some(tls_cert_and_key), + Some("127.0.0.1:0"), + "127.0.0.1:0", + target_addr.to_string(), + AttestationGenerator::with_no_attestation(), + AttestationVerifier::expect_none(), + false, + ) + .await + .unwrap(); + + let outer_addr = dual_listener_server.outer_local_addr().unwrap().unwrap(); + let inner_addr = dual_listener_server.inner_local_addr().unwrap(); + assert_eq!(dual_listener_server.local_addr().unwrap(), outer_addr); + assert_ne!(outer_addr, inner_addr); + + let inner_only_server = ProxyServer::new( + None, + None::<&str>, + "127.0.0.1:0", + target_addr.to_string(), + AttestationGenerator::with_no_attestation(), + AttestationVerifier::expect_none(), + false, + ) + .await + .unwrap(); + + let inner_only_addr = inner_only_server.inner_local_addr().unwrap(); + assert!(inner_only_server.outer_local_addr().unwrap().is_none()); + assert_eq!(inner_only_server.local_addr().unwrap(), inner_only_addr); + } + + #[tokio::test(flavor = "multi_thread")] + async fn outer_tls_requires_outer_listener_address() { + let target_addr = example_http_service().await; + + let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); + let tls_cert_and_key = TlsCertAndKey { cert_chain, - server_config, + key: private_key, + }; + + let result = ProxyServer::new( + Some(tls_cert_and_key), + None::<&str>, + "127.0.0.1:0", + target_addr.to_string(), + AttestationGenerator::with_no_attestation(), + AttestationVerifier::expect_none(), + false, + ) + .await; + + assert!(matches!( + result, + Err(ProxyError::OuterTlsWithoutOuterListener) + )); + } + + #[tokio::test(flavor = "multi_thread")] + async fn inner_only_listener_negotiates_http2_by_default() { + let _ = rustls::crypto::ring::default_provider().install_default(); + let target_addr = example_http_service().await; + + let proxy_server = ProxyServer::new( + None, + None::<&str>, "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), + false, ) .await .unwrap(); - let proxy_addr = proxy_server.local_addr().unwrap(); + let inner_addr = proxy_server.inner_local_addr().unwrap(); tokio::spawn(async move { proxy_server.accept().await.unwrap(); @@ -932,44 +1129,46 @@ mod tests { let attested_cert_verifier = AttestedCertificateVerifier::new(None, AttestationVerifier::mock()).unwrap(); - let mut inner_client_config = + let mut client_config = ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) .dangerous() .with_custom_certificate_verifier(Arc::new(attested_cert_verifier)) .with_no_client_auth(); - ensure_proxy_alpn_protocols(&mut inner_client_config.alpn_protocols); + ensure_proxy_alpn_protocols(&mut client_config.alpn_protocols); - let nesting_tls_connector = - NestingTlsConnector::new(Arc::new(outer_client_config), Arc::new(inner_client_config)); + let tls_connector = TlsConnector::from(Arc::new(client_config)); + let outbound_stream = TcpStream::connect(inner_addr).await.unwrap(); + let domain = ServerName::try_from("localhost".to_string()).unwrap(); + let mut tls_stream = tls_connector + .connect(domain, outbound_stream) + .await + .unwrap(); - let (sender, conn) = ProxyClient::setup_connection( - &nesting_tls_connector, - &format!("localhost:{}", proxy_addr.port()), - ) - .await - .unwrap(); + assert!(matches!( + HttpVersion::from_negotiated_protocol_client(&tls_stream), + HttpVersion::Http2 + )); - assert!(matches!(sender, HttpSender::Http2(_))); - assert!(matches!(conn, HttpConnection::Http2 { .. })); + tls_stream.shutdown().await.unwrap(); } #[tokio::test(flavor = "multi_thread")] - async fn http_proxy_default_constructors_work() { + async fn http_proxy_negotiates_http2_by_default() { let target_addr = example_http_service().await; let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); - let server_cert = cert_chain[0].clone(); + let (server_config, outer_client_config) = + generate_tls_config(cert_chain.clone(), private_key); - let proxy_server = ProxyServer::new( - TlsCertAndKey { - cert_chain, - key: private_key, - }, + let proxy_server = ProxyServer::new_with_tls_config( + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), false, + Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -980,31 +1179,81 @@ mod tests { proxy_server.accept().await.unwrap(); }); - let proxy_client = ProxyClient::new( - None, - "127.0.0.1:0".to_string(), - format!("localhost:{}", proxy_addr.port()), - AttestationGenerator::with_no_attestation(), - AttestationVerifier::mock(), - Some(server_cert), + let attested_cert_verifier = + AttestedCertificateVerifier::new(None, AttestationVerifier::mock()).unwrap(); + let mut inner_client_config = + ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) + .dangerous() + .with_custom_certificate_verifier(Arc::new(attested_cert_verifier)) + .with_no_client_auth(); + ensure_proxy_alpn_protocols(&mut inner_client_config.alpn_protocols); + + let nesting_tls_connector = + NestingTlsConnector::new(Arc::new(outer_client_config), Arc::new(inner_client_config)); + + let (sender, conn) = ProxyClient::setup_connection( + &nesting_tls_connector, + &format!("localhost:{}", proxy_addr.port()), ) .await .unwrap(); - let proxy_client_addr = proxy_client.local_addr().unwrap(); - - tokio::spawn(async move { - proxy_client.accept().await.unwrap(); - }); - - let res = reqwest::get(format!("http://{}", proxy_client_addr)) - .await - .unwrap(); - - let res_body = res.text().await.unwrap(); - assert_eq!(res_body, "No measurements"); + assert!(matches!(sender, HttpSender::Http2(_))); + assert!(matches!(conn, HttpConnection::Http2 { .. })); } + // #[tokio::test(flavor = "multi_thread")] + // async fn http_proxy_default_constructors_work() { + // let target_addr = example_http_service().await; + // + // let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); + // let server_cert = cert_chain[0].clone(); + // + // let proxy_server = ProxyServer::new( + // TlsCertAndKey { + // cert_chain, + // key: private_key, + // }, + // "127.0.0.1:0", + // target_addr.to_string(), + // AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), + // AttestationVerifier::expect_none(), + // false, + // ) + // .await + // .unwrap(); + // + // let proxy_addr = proxy_server.local_addr().unwrap(); + // + // tokio::spawn(async move { + // proxy_server.accept().await.unwrap(); + // }); + // + // let proxy_client = ProxyClient::new( + // None, + // "127.0.0.1:0".to_string(), + // format!("localhost:{}", proxy_addr.port()), + // AttestationGenerator::with_no_attestation(), + // AttestationVerifier::mock(), + // Some(server_cert), + // ) + // .await + // .unwrap(); + // + // let proxy_client_addr = proxy_client.local_addr().unwrap(); + // + // tokio::spawn(async move { + // proxy_client.accept().await.unwrap(); + // }); + // + // let res = reqwest::get(format!("http://{}", proxy_client_addr)) + // .await + // .unwrap(); + // + // let res_body = res.text().await.unwrap(); + // assert_eq!(res_body, "No measurements"); + // } + // Server has mock DCAP, client has no attestation and no client auth #[tokio::test(flavor = "multi_thread")] async fn http_proxy_with_server_attestation() { @@ -1015,12 +1264,14 @@ mod tests { let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); let proxy_server = ProxyServer::new_with_tls_config( - cert_chain, - server_config, + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), + false, + Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1076,14 +1327,15 @@ mod tests { server_private_key, ); - let proxy_server = ProxyServer::new_with_tls_config_and_client_auth( - server_cert_chain, - server_tls_server_config, + let proxy_server = ProxyServer::new_with_tls_config( + Some(server_tls_server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::mock(), true, + Some(certificate_identity_from_chain(&server_cert_chain).unwrap()), ) .await .unwrap(); @@ -1130,12 +1382,14 @@ mod tests { generate_tls_config(server_cert_chain.clone(), server_private_key); let proxy_server = ProxyServer::new_with_tls_config( - server_cert_chain, - server_config, + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::mock(), + false, + Some(certificate_identity_from_chain(&server_cert_chain).unwrap()), ) .await .unwrap(); @@ -1193,14 +1447,15 @@ mod tests { server_private_key, ); - let proxy_server = ProxyServer::new_with_tls_config_and_client_auth( - server_cert_chain, - server_tls_server_config, + let proxy_server = ProxyServer::new_with_tls_config( + Some(server_tls_server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::mock(), true, + Some(certificate_identity_from_chain(&server_cert_chain).unwrap()), ) .await .unwrap(); @@ -1249,12 +1504,14 @@ mod tests { let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); let proxy_server = ProxyServer::new_with_tls_config( - cert_chain.clone(), - server_config, + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), + false, + Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1291,12 +1548,14 @@ mod tests { let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); let proxy_server = ProxyServer::new_with_tls_config( - cert_chain, - server_config, + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::expect_none(), + false, + Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1331,12 +1590,14 @@ mod tests { let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); let proxy_server = ProxyServer::new_with_tls_config( - cert_chain, - server_config, + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), + false, + Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1396,12 +1657,14 @@ mod tests { let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); let proxy_server = ProxyServer::new_with_tls_config( - cert_chain, - server_config, + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), + false, + Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1469,12 +1732,14 @@ mod tests { server_config.alpn_protocols.push(ALPN_HTTP11.to_vec()); let proxy_server = ProxyServer::new_with_tls_config( - cert_chain, - server_config, + Some(server_config), + Some("127.0.0.1:0"), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), + false, + Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); diff --git a/src/main.rs b/src/main.rs index c14c414..e198b3a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,9 +77,12 @@ enum CliCommand { }, /// Run a proxy server Server { - /// Socket address to listen on - #[arg(short, long, default_value = "0.0.0.0:0", env = "LISTEN_ADDR")] - listen_addr: SocketAddr, + /// Socket address to listen on for the outer nested-TLS listener + #[arg(long, default_value = "0.0.0.0:443")] + outer_listen_addr: SocketAddr, + /// Socket address to listen on for the inner-only attested TLS listener + #[arg(long, default_value = "0.0.0.0:4433")] + inner_listen_addr: SocketAddr, /// The hostname:port or ip:port of the target service to forward traffic to target_addr: String, /// Type of attestation to present (dafaults to 'auto' for automatic detection) @@ -119,19 +122,22 @@ enum CliCommand { AttestedFileServer { /// Filesystem path to statically serve path_to_serve: PathBuf, - /// Socket address to listen on - #[arg(short, long, default_value = "0.0.0.0:0", env = "LISTEN_ADDR")] - listen_addr: SocketAddr, + /// Socket address to listen on for the outer nested-TLS listener + #[arg(long, default_value = "0.0.0.0:443")] + outer_listen_addr: SocketAddr, + /// Socket address to listen on for the inner-only attested TLS listener + #[arg(long, default_value = "0.0.0.0:4433")] + inner_listen_addr: SocketAddr, /// Type of attestation to present (dafaults to none) /// If other than None, a TLS key and certicate must also be given #[arg(long, env = "SERVER_ATTESTATION_TYPE")] server_attestation_type: Option, /// The path to a PEM encoded private key #[arg(long, env = "TLS_PRIVATE_KEY_PATH")] - tls_private_key_path: PathBuf, + tls_private_key_path: Option, /// Additional CA certificate to verify against (PEM) Defaults to no additional TLS certs. #[arg(long, env = "TLS_CERTIFICATE_PATH")] - tls_certificate_path: PathBuf, + tls_certificate_path: Option, /// URL of the remote dummy attestation service. Only use with --server-attestation-type /// dummy #[arg(long)] @@ -277,7 +283,8 @@ async fn main() -> anyhow::Result<()> { } } CliCommand::Server { - listen_addr, + outer_listen_addr, + inner_listen_addr, target_addr, tls_private_key_path, tls_certificate_path, @@ -299,7 +306,8 @@ async fn main() -> anyhow::Result<()> { let server = ProxyServer::new( tls_cert_and_chain, - listen_addr, + Some(outer_listen_addr), + inner_listen_addr, target_addr, local_attestation_generator, attestation_verifier, @@ -344,14 +352,15 @@ async fn main() -> anyhow::Result<()> { } CliCommand::AttestedFileServer { path_to_serve, - listen_addr, + outer_listen_addr, + inner_listen_addr, server_attestation_type, tls_private_key_path, tls_certificate_path, dev_dummy_dcap, } => { let tls_cert_and_chain = - load_tls_cert_and_key(tls_certificate_path, tls_private_key_path)?; + load_tls_cert_and_key_server(tls_certificate_path, tls_private_key_path)?; let server_attestation_type: AttestationType = serde_json::from_value( serde_json::Value::String(server_attestation_type.unwrap_or("none".to_string())), @@ -363,7 +372,8 @@ async fn main() -> anyhow::Result<()> { attested_file_server( path_to_serve, tls_cert_and_chain, - listen_addr, + outer_listen_addr, + inner_listen_addr, attestation_generator, attestation_verifier, false, @@ -410,16 +420,14 @@ async fn main() -> anyhow::Result<()> { fn load_tls_cert_and_key_server( cert_chain: Option, private_key: Option, -) -> anyhow::Result { - if let Some(private_key) = private_key { - load_tls_cert_and_key( - cert_chain.ok_or(anyhow!("Private key given but no certificate chain"))?, - private_key, - ) - } else if cert_chain.is_some() { - Err(anyhow!("Certificate chain provided but no private key")) - } else { - Err(anyhow!("No private key provided")) +) -> anyhow::Result> { + match (cert_chain, private_key) { + (Some(cert_chain), Some(private_key)) => { + Ok(Some(load_tls_cert_and_key(cert_chain, private_key)?)) + } + (Some(_), None) => Err(anyhow!("Certificate chain provided but no private key")), + (None, Some(_)) => Err(anyhow!("Private key given but no certificate chain")), + (None, None) => Ok(None), } } From b52645ac6e1afcd44e04ed7aefa8f586f68c453f Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 19 Mar 2026 12:14:12 +0100 Subject: [PATCH 02/11] Clippy --- src/file_server.rs | 18 +++++++----------- src/lib.rs | 12 +++++++----- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/file_server.rs b/src/file_server.rs index 8390c9f..1e07e0d 100644 --- a/src/file_server.rs +++ b/src/file_server.rs @@ -142,27 +142,23 @@ mod tests { let client = reqwest::Client::new(); // This makes the request - let (body, content_type) = get_body_and_content_type( - format!("http://{}/foo.txt", proxy_client_addr.to_string()), - &client, - ) - .await; + let (body, content_type) = + get_body_and_content_type(format!("http://{}/foo.txt", proxy_client_addr), &client) + .await; assert_eq!(content_type, "text/plain"); assert_eq!(body, b"bar"); let (body, content_type) = get_body_and_content_type( - format!("http://{}/index.html", proxy_client_addr.to_string()), + format!("http://{}/index.html", proxy_client_addr), &client, ) .await; assert_eq!(content_type, "text/html"); assert_eq!(body, b"foo"); - let (body, content_type) = get_body_and_content_type( - format!("http://{}/data.bin", proxy_client_addr.to_string()), - &client, - ) - .await; + let (body, content_type) = + get_body_and_content_type(format!("http://{}/data.bin", proxy_client_addr), &client) + .await; assert_eq!(content_type, "application/octet-stream"); assert_eq!(body, [0u8; 32]); } diff --git a/src/lib.rs b/src/lib.rs index a36d5b3..e6098bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,7 @@ pub struct ProxyServer { impl ProxyServer { /// Start with dual listeners. The outer nested-TLS listener is optional. + #[allow(clippy::too_many_arguments)] pub async fn new( outer_cert_and_key: Option, outer_local: Option, @@ -204,6 +205,7 @@ impl ProxyServer { } /// Start with preconfigured TLS + #[allow(clippy::too_many_arguments)] pub async fn new_with_tls_config( outer_server_config: Option, outer_local: Option, @@ -1299,7 +1301,7 @@ mod tests { proxy_client.accept().await.unwrap(); }); - let res = reqwest::get(format!("http://{}", proxy_client_addr.to_string())) + let res = reqwest::get(format!("http://{}", proxy_client_addr)) .await .unwrap(); @@ -1420,7 +1422,7 @@ mod tests { proxy_client.accept().await.unwrap(); }); - let res = reqwest::get(format!("http://{}", proxy_client_addr.to_string())) + let res = reqwest::get(format!("http://{}", proxy_client_addr)) .await .unwrap(); @@ -1704,7 +1706,7 @@ mod tests { proxy_client.accept().await.unwrap(); }); - let _initial_response = reqwest::get(format!("http://{}", proxy_client_addr.to_string())) + let _initial_response = reqwest::get(format!("http://{}", proxy_client_addr)) .await .unwrap(); @@ -1712,7 +1714,7 @@ mod tests { connection_breaker_tx.send(()).unwrap(); // Make another request - let res = reqwest::get(format!("http://{}", proxy_client_addr.to_string())) + let res = reqwest::get(format!("http://{}", proxy_client_addr)) .await .unwrap(); @@ -1767,7 +1769,7 @@ mod tests { proxy_client.accept().await.unwrap(); }); - let res = reqwest::get(format!("http://{}", proxy_client_addr.to_string())) + let res = reqwest::get(format!("http://{}", proxy_client_addr)) .await .unwrap(); From edf728ee65cc617ebeae6b640feb55c4cdade4cd Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 19 Mar 2026 12:26:57 +0100 Subject: [PATCH 03/11] Improve constructors --- src/attested_get.rs | 14 ++- src/file_server.rs | 33 +++--- src/lib.rs | 263 ++++++++++++++++++++++++++------------------ src/main.rs | 12 +- 4 files changed, 194 insertions(+), 128 deletions(-) diff --git a/src/attested_get.rs b/src/attested_get.rs index 97b346d..16fd82e 100644 --- a/src/attested_get.rs +++ b/src/attested_get.rs @@ -55,7 +55,7 @@ async fn attested_get_with_client( mod tests { use super::*; use crate::{ - ProxyServer, + OuterTlsConfig, OuterTlsMode, ProxyServer, attestation::AttestationType, file_server::static_file_server, test_helpers::{generate_certificate_chain_for_host, generate_tls_config}, @@ -77,15 +77,19 @@ mod tests { let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); // Setup a proxy server targetting the static file server - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name: Some("localhost".to_string()), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), false, - Some("localhost".to_string()), ) .await .unwrap(); diff --git a/src/file_server.rs b/src/file_server.rs index 1e07e0d..d2cfd90 100644 --- a/src/file_server.rs +++ b/src/file_server.rs @@ -1,5 +1,8 @@ //! Static HTTP file server provided by an attested TLS proxy server -use crate::{AttestationGenerator, AttestationVerifier, ProxyError, ProxyServer, TlsCertAndKey}; +use crate::{ + AttestationGenerator, AttestationVerifier, OuterTlsConfig, OuterTlsMode, ProxyError, + ProxyServer, TlsCertAndKey, +}; use std::{net::SocketAddr, path::PathBuf}; use tokio::net::ToSocketAddrs; use tower_http::services::ServeDir; @@ -17,8 +20,10 @@ pub async fn attested_file_server( let target_addr = static_file_server(path_to_serve).await?; let server = ProxyServer::new( - outer_cert_and_key, - Some(outer_listen_addr), + outer_cert_and_key.map(|cert_and_key| OuterTlsConfig { + listen_addr: outer_listen_addr, + tls: OuterTlsMode::CertAndKey(cert_and_key), + }), inner_listen_addr, target_addr.to_string(), attestation_generator, @@ -54,7 +59,7 @@ pub(crate) async fn static_file_server(path: PathBuf) -> Resultfoo"); diff --git a/src/lib.rs b/src/lib.rs index e6098bb..64bb2de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,19 @@ pub struct TlsCertAndKey { pub key: PrivateKeyDer<'static>, } +pub struct OuterTlsConfig { + pub listen_addr: A, + pub tls: OuterTlsMode, +} + +pub enum OuterTlsMode { + CertAndKey(TlsCertAndKey), + Preconfigured { + server_config: ServerConfig, + certificate_name: Option, + }, +} + /// Adds HTTP 1 and 2 to the list of allowed protocols fn ensure_proxy_alpn_protocols(alpn_protocols: &mut Vec>) { for protocol in [ALPN_H2, ALPN_HTTP11] { @@ -144,10 +157,8 @@ pub struct ProxyServer { impl ProxyServer { /// Start with dual listeners. The outer nested-TLS listener is optional. - #[allow(clippy::too_many_arguments)] pub async fn new( - outer_cert_and_key: Option, - outer_local: Option, + outer_session: Option>, inner_local: impl ToSocketAddrs, target: String, attestation_generator: AttestationGenerator, @@ -157,17 +168,14 @@ impl ProxyServer { where O: ToSocketAddrs, { - if outer_cert_and_key.is_some() && outer_local.is_none() { - return Err(ProxyError::OuterTlsWithoutOuterListener); - } - - let outer_certificate_name = outer_cert_and_key - .as_ref() - .map(|cert_and_key| certificate_identity_from_chain(&cert_and_key.cert_chain)) - .transpose()?; - let outer_server_config = match outer_cert_and_key { - Some(cert_and_key) => { - let config = if client_auth { + let outer_session = match outer_session { + Some(OuterTlsConfig { + listen_addr, + tls: OuterTlsMode::CertAndKey(cert_and_key), + }) => { + let certificate_name = + Some(certificate_identity_from_chain(&cert_and_key.cert_chain)?); + let server_config = if client_auth { let root_store = RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); let verifier = WebPkiClientVerifier::builder(Arc::new(root_store)).build()?; @@ -186,43 +194,69 @@ impl ProxyServer { cert_and_key.key.clone_key(), )? }; - Some(config) + + Some(OuterTlsConfig { + listen_addr, + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name, + }, + }) } + Some(OuterTlsConfig { + listen_addr, + tls: + OuterTlsMode::Preconfigured { + server_config, + certificate_name, + }, + }) => Some(OuterTlsConfig { + listen_addr, + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name, + }, + }), None => None, }; - Self::new_with_tls_config( - outer_server_config, - outer_local, + Self::new_inner( + outer_session, inner_local, target, attestation_generator, attestation_verifier, client_auth, - outer_certificate_name, ) .await } - /// Start with preconfigured TLS - #[allow(clippy::too_many_arguments)] - pub async fn new_with_tls_config( - outer_server_config: Option, - outer_local: Option, + async fn new_inner( + outer_session: Option>, inner_local: impl ToSocketAddrs, target: String, attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, client_auth: bool, - certificate_name: Option, ) -> Result where O: ToSocketAddrs, { - if outer_server_config.is_some() && outer_local.is_none() { - return Err(ProxyError::OuterTlsWithoutOuterListener); - } - + let (outer_server_config, certificate_name, outer_local) = match outer_session { + Some(OuterTlsConfig { + listen_addr, + tls: + OuterTlsMode::Preconfigured { + server_config, + certificate_name, + }, + }) => (Some(server_config), certificate_name, Some(listen_addr)), + Some(OuterTlsConfig { + listen_addr: _, + tls: OuterTlsMode::CertAndKey(_), + }) => unreachable!("cert/key outer session should be normalized via ProxyServer::new"), + None => (None, None, None), + }; let inner_server_config = Arc::new( build_inner_server_config( attestation_generator, @@ -244,7 +278,9 @@ impl ProxyServer { ); (Some(outer_listener), Some(acceptor)) } - (Some(_), None) => return Err(ProxyError::OuterTlsWithoutOuterListener), + (Some(_), None) => { + unreachable!("outer config without outer listener is unrepresentable") + } (None, _) => (None, None), }; @@ -903,8 +939,6 @@ pub enum ProxyError { MpscSend, #[error("Client auth must be configured on both the inner and outer TLS sessions")] ClientAuthMisconfigured, - #[error("Outer TLS configuration requires an outer listener address")] - OuterTlsWithoutOuterListener, } impl From> for ProxyError { @@ -1046,8 +1080,10 @@ mod tests { }; let dual_listener_server = ProxyServer::new( - Some(tls_cert_and_key), - Some("127.0.0.1:0"), + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::CertAndKey(tls_cert_and_key), + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::with_no_attestation(), @@ -1063,8 +1099,7 @@ mod tests { assert_ne!(outer_addr, inner_addr); let inner_only_server = ProxyServer::new( - None, - None::<&str>, + None::>, "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::with_no_attestation(), @@ -1079,41 +1114,13 @@ mod tests { assert_eq!(inner_only_server.local_addr().unwrap(), inner_only_addr); } - #[tokio::test(flavor = "multi_thread")] - async fn outer_tls_requires_outer_listener_address() { - let target_addr = example_http_service().await; - - let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); - let tls_cert_and_key = TlsCertAndKey { - cert_chain, - key: private_key, - }; - - let result = ProxyServer::new( - Some(tls_cert_and_key), - None::<&str>, - "127.0.0.1:0", - target_addr.to_string(), - AttestationGenerator::with_no_attestation(), - AttestationVerifier::expect_none(), - false, - ) - .await; - - assert!(matches!( - result, - Err(ProxyError::OuterTlsWithoutOuterListener) - )); - } - #[tokio::test(flavor = "multi_thread")] async fn inner_only_listener_negotiates_http2_by_default() { let _ = rustls::crypto::ring::default_provider().install_default(); let target_addr = example_http_service().await; let proxy_server = ProxyServer::new( - None, - None::<&str>, + None::>, "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), @@ -1162,15 +1169,19 @@ mod tests { let (server_config, outer_client_config) = generate_tls_config(cert_chain.clone(), private_key); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), false, - Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1265,15 +1276,19 @@ mod tests { let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), false, - Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1329,15 +1344,21 @@ mod tests { server_private_key, ); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_tls_server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config: server_tls_server_config, + certificate_name: Some( + certificate_identity_from_chain(&server_cert_chain).unwrap(), + ), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::mock(), true, - Some(certificate_identity_from_chain(&server_cert_chain).unwrap()), ) .await .unwrap(); @@ -1383,15 +1404,21 @@ mod tests { let (server_config, client_config) = generate_tls_config(server_cert_chain.clone(), server_private_key); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name: Some( + certificate_identity_from_chain(&server_cert_chain).unwrap(), + ), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::mock(), false, - Some(certificate_identity_from_chain(&server_cert_chain).unwrap()), ) .await .unwrap(); @@ -1449,15 +1476,21 @@ mod tests { server_private_key, ); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_tls_server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config: server_tls_server_config, + certificate_name: Some( + certificate_identity_from_chain(&server_cert_chain).unwrap(), + ), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::mock(), true, - Some(certificate_identity_from_chain(&server_cert_chain).unwrap()), ) .await .unwrap(); @@ -1505,15 +1538,19 @@ mod tests { let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), false, - Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1549,15 +1586,19 @@ mod tests { let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::expect_none(), false, - Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1591,15 +1632,19 @@ mod tests { let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), false, - Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1658,15 +1703,19 @@ mod tests { let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); let (server_config, client_config) = generate_tls_config(cert_chain.clone(), private_key); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), false, - Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); @@ -1733,15 +1782,19 @@ mod tests { server_config.alpn_protocols.push(ALPN_HTTP11.to_vec()); - let proxy_server = ProxyServer::new_with_tls_config( - Some(server_config), - Some("127.0.0.1:0"), + let proxy_server = ProxyServer::new( + Some(OuterTlsConfig { + listen_addr: "127.0.0.1:0", + tls: OuterTlsMode::Preconfigured { + server_config, + certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + }, + }), "127.0.0.1:0", target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), false, - Some(certificate_identity_from_chain(&cert_chain).unwrap()), ) .await .unwrap(); diff --git a/src/main.rs b/src/main.rs index e198b3a..ad8ad33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,9 +7,9 @@ use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer}; use tracing::level_filters::LevelFilter; use attested_tls_proxy::{ - AttestationGenerator, ProxyClient, ProxyServer, TlsCertAndKey, attested_get::attested_get, - file_server::attested_file_server, get_inner_tls_cert, health_check, - normalize_pem::normalize_private_key_pem_to_pkcs8, + AttestationGenerator, OuterTlsConfig, OuterTlsMode, ProxyClient, ProxyServer, TlsCertAndKey, + attested_get::attested_get, file_server::attested_file_server, get_inner_tls_cert, + health_check, normalize_pem::normalize_private_key_pem_to_pkcs8, }; const GIT_REV: &str = match option_env!("GIT_REV") { @@ -305,8 +305,10 @@ async fn main() -> anyhow::Result<()> { .await?; let server = ProxyServer::new( - tls_cert_and_chain, - Some(outer_listen_addr), + tls_cert_and_chain.map(|cert_and_key| OuterTlsConfig { + listen_addr: outer_listen_addr, + tls: OuterTlsMode::CertAndKey(cert_and_key), + }), inner_listen_addr, target_addr, local_attestation_generator, From 22991befcf33c086230330fa8b017c1b37972fba Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 19 Mar 2026 12:37:24 +0100 Subject: [PATCH 04/11] Improve constructors --- src/lib.rs | 177 +++++++++++++++++++++++------------------------------ 1 file changed, 76 insertions(+), 101 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 64bb2de..9b9f4fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,19 +62,83 @@ pub struct TlsCertAndKey { pub key: PrivateKeyDer<'static>, } +/// Configuration for the optional outer nested-TLS listener. pub struct OuterTlsConfig { + /// The socket address to bind for the outer listener. pub listen_addr: A, + /// How the outer TLS server configuration should be constructed. pub tls: OuterTlsMode, } +/// TLS configuration sources for the outer nested-TLS listener. pub enum OuterTlsMode { + /// Build the outer TLS server config from certificate and key material. CertAndKey(TlsCertAndKey), + /// Use an already-constructed outer TLS server config. Preconfigured { + /// The outer TLS server configuration to expose on the listener. server_config: ServerConfig, + /// The server identity to embed into the inner attested certificate. certificate_name: Option, }, } +impl OuterTlsConfig +where + A: ToSocketAddrs, +{ + fn certificate_name(&self) -> Result, ProxyError> { + match &self.tls { + OuterTlsMode::CertAndKey(cert_and_key) => { + Ok(Some(certificate_identity_from_chain(&cert_and_key.cert_chain)?)) + } + OuterTlsMode::Preconfigured { + certificate_name, .. + } => Ok(certificate_name.clone()), + } + } + + async fn into_listener_and_acceptor( + self, + inner_server_config: Arc, + client_auth: bool, + ) -> Result<(Arc, NestingTlsAcceptor), ProxyError> { + let listen_addr = self.listen_addr; + let outer_server_config = match self.tls { + OuterTlsMode::CertAndKey(cert_and_key) => { + if client_auth { + let root_store = + RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); + let verifier = WebPkiClientVerifier::builder(Arc::new(root_store)).build()?; + + ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) + .with_client_cert_verifier(verifier) + .with_single_cert( + cert_and_key.cert_chain.clone(), + cert_and_key.key.clone_key(), + )? + } else { + ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) + .with_no_client_auth() + .with_single_cert( + cert_and_key.cert_chain.clone(), + cert_and_key.key.clone_key(), + )? + } + } + OuterTlsMode::Preconfigured { server_config, .. } => server_config, + }; + + let outer_listener = Arc::new(TcpListener::bind(listen_addr).await?); + let outer_tls_acceptor = NestingTlsAcceptor::new( + Arc::new(outer_server_config), + inner_server_config, + ); + + Ok((outer_listener, outer_tls_acceptor)) + } +} + /// Adds HTTP 1 and 2 to the list of allowed protocols fn ensure_proxy_alpn_protocols(alpn_protocols: &mut Vec>) { for protocol in [ALPN_H2, ALPN_HTTP11] { @@ -168,95 +232,11 @@ impl ProxyServer { where O: ToSocketAddrs, { - let outer_session = match outer_session { - Some(OuterTlsConfig { - listen_addr, - tls: OuterTlsMode::CertAndKey(cert_and_key), - }) => { - let certificate_name = - Some(certificate_identity_from_chain(&cert_and_key.cert_chain)?); - let server_config = if client_auth { - let root_store = - RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); - let verifier = WebPkiClientVerifier::builder(Arc::new(root_store)).build()?; - - ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_client_cert_verifier(verifier) - .with_single_cert( - cert_and_key.cert_chain.clone(), - cert_and_key.key.clone_key(), - )? - } else { - ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) - .with_no_client_auth() - .with_single_cert( - cert_and_key.cert_chain.clone(), - cert_and_key.key.clone_key(), - )? - }; - - Some(OuterTlsConfig { - listen_addr, - tls: OuterTlsMode::Preconfigured { - server_config, - certificate_name, - }, - }) - } - Some(OuterTlsConfig { - listen_addr, - tls: - OuterTlsMode::Preconfigured { - server_config, - certificate_name, - }, - }) => Some(OuterTlsConfig { - listen_addr, - tls: OuterTlsMode::Preconfigured { - server_config, - certificate_name, - }, - }), - None => None, - }; - - Self::new_inner( - outer_session, - inner_local, - target, - attestation_generator, - attestation_verifier, - client_auth, - ) - .await - } - - async fn new_inner( - outer_session: Option>, - inner_local: impl ToSocketAddrs, - target: String, - attestation_generator: AttestationGenerator, - attestation_verifier: AttestationVerifier, - client_auth: bool, - ) -> Result - where - O: ToSocketAddrs, - { - let (outer_server_config, certificate_name, outer_local) = match outer_session { - Some(OuterTlsConfig { - listen_addr, - tls: - OuterTlsMode::Preconfigured { - server_config, - certificate_name, - }, - }) => (Some(server_config), certificate_name, Some(listen_addr)), - Some(OuterTlsConfig { - listen_addr: _, - tls: OuterTlsMode::CertAndKey(_), - }) => unreachable!("cert/key outer session should be normalized via ProxyServer::new"), - None => (None, None, None), - }; + let certificate_name = outer_session + .as_ref() + .map(OuterTlsConfig::certificate_name) + .transpose()? + .flatten(); let inner_server_config = Arc::new( build_inner_server_config( attestation_generator, @@ -269,19 +249,14 @@ impl ProxyServer { let inner_listener = Arc::new(TcpListener::bind(inner_local).await?); let inner_tls_acceptor = TlsAcceptor::from(inner_server_config.clone()); - let (outer_listener, outer_tls_acceptor) = match (outer_server_config, outer_local) { - (Some(outer_server_config), Some(outer_local)) => { - let outer_listener = Arc::new(TcpListener::bind(outer_local).await?); - let acceptor = NestingTlsAcceptor::new( - Arc::new(outer_server_config), - inner_server_config.clone(), - ); - (Some(outer_listener), Some(acceptor)) - } - (Some(_), None) => { - unreachable!("outer config without outer listener is unrepresentable") + let (outer_listener, outer_tls_acceptor) = match outer_session { + Some(outer_session) => { + let (outer_listener, outer_tls_acceptor) = outer_session + .into_listener_and_acceptor(inner_server_config.clone(), client_auth) + .await?; + (Some(outer_listener), Some(outer_tls_acceptor)) } - (None, _) => (None, None), + None => (None, None), }; Ok(Self { From f82d5632bebbb6f0c63ac7f48d008f6229710a5c Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 19 Mar 2026 13:37:49 +0100 Subject: [PATCH 05/11] Fmt --- src/lib.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9b9f4fd..9c636e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,9 +89,9 @@ where { fn certificate_name(&self) -> Result, ProxyError> { match &self.tls { - OuterTlsMode::CertAndKey(cert_and_key) => { - Ok(Some(certificate_identity_from_chain(&cert_and_key.cert_chain)?)) - } + OuterTlsMode::CertAndKey(cert_and_key) => Ok(Some(certificate_identity_from_chain( + &cert_and_key.cert_chain, + )?)), OuterTlsMode::Preconfigured { certificate_name, .. } => Ok(certificate_name.clone()), @@ -130,10 +130,8 @@ where }; let outer_listener = Arc::new(TcpListener::bind(listen_addr).await?); - let outer_tls_acceptor = NestingTlsAcceptor::new( - Arc::new(outer_server_config), - inner_server_config, - ); + let outer_tls_acceptor = + NestingTlsAcceptor::new(Arc::new(outer_server_config), inner_server_config); Ok((outer_listener, outer_tls_acceptor)) } From cfcb77fba3e0898f44ee2a4080509afae50d8ac4 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 20 Mar 2026 08:32:03 +0100 Subject: [PATCH 06/11] Preconfigured server name should not be optional --- src/attested_get.rs | 2 +- src/file_server.rs | 2 +- src/lib.rs | 61 +++++++++++++++++++-------------------------- 3 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/attested_get.rs b/src/attested_get.rs index 16fd82e..9bd7118 100644 --- a/src/attested_get.rs +++ b/src/attested_get.rs @@ -82,7 +82,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some("localhost".to_string()), + certificate_name: "localhost".to_string(), }, }), "127.0.0.1:0", diff --git a/src/file_server.rs b/src/file_server.rs index d2cfd90..2867972 100644 --- a/src/file_server.rs +++ b/src/file_server.rs @@ -110,7 +110,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some("localhost".to_string()), + certificate_name: "localhost".to_string(), }, }), "127.0.0.1:0", diff --git a/src/lib.rs b/src/lib.rs index 9c636e7..084f70c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,8 +47,6 @@ const SERVER_RECONNECT_MAX_BACKOFF_SECS: u64 = 120; const KEEP_ALIVE_INTERVAL: u64 = 30; const KEEP_ALIVE_TIMEOUT: u64 = 10; -const DEFAULT_INNER_CERTIFICATE_NAME: &str = "localhost"; - type RequestWithResponseSender = ( http::Request, oneshot::Sender>, hyper::Error>>, @@ -79,7 +77,7 @@ pub enum OuterTlsMode { /// The outer TLS server configuration to expose on the listener. server_config: ServerConfig, /// The server identity to embed into the inner attested certificate. - certificate_name: Option, + certificate_name: String, }, } @@ -87,11 +85,11 @@ impl OuterTlsConfig where A: ToSocketAddrs, { - fn certificate_name(&self) -> Result, ProxyError> { + fn certificate_name(&self) -> Result { match &self.tls { - OuterTlsMode::CertAndKey(cert_and_key) => Ok(Some(certificate_identity_from_chain( - &cert_and_key.cert_chain, - )?)), + OuterTlsMode::CertAndKey(cert_and_key) => { + Ok(certificate_identity_from_chain(&cert_and_key.cert_chain)?) + } OuterTlsMode::Preconfigured { certificate_name, .. } => Ok(certificate_name.clone()), @@ -233,8 +231,7 @@ impl ProxyServer { let certificate_name = outer_session .as_ref() .map(OuterTlsConfig::certificate_name) - .transpose()? - .flatten(); + .transpose()?; let inner_server_config = Arc::new( build_inner_server_config( attestation_generator, @@ -565,7 +562,7 @@ impl ProxyClient { let mut inner_client_config = if let Some(cert_chain) = cert_chain.as_ref() { let inner_cert_resolver = build_attested_cert_resolver( attestation_generator, - Some(certificate_identity_from_chain(cert_chain)?), + certificate_identity_from_chain(cert_chain)?, ) .await?; ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) @@ -944,15 +941,12 @@ fn certificate_identity_from_chain( async fn build_attested_cert_resolver( attestation_generator: AttestationGenerator, - certificate_name: Option, + certificate_name: String, ) -> Result { - Ok(AttestedCertificateResolver::new( - attestation_generator, - None, - certificate_name.unwrap_or_else(|| DEFAULT_INNER_CERTIFICATE_NAME.to_string()), - vec![], + Ok( + AttestedCertificateResolver::new(attestation_generator, None, certificate_name, vec![]) + .await?, ) - .await?) } async fn build_inner_server_config( @@ -961,8 +955,11 @@ async fn build_inner_server_config( client_auth: bool, certificate_name: Option, ) -> Result { - let inner_cert_resolver = - build_attested_cert_resolver(attestation_generator, certificate_name).await?; + let inner_cert_resolver = build_attested_cert_resolver( + attestation_generator, + certificate_name.unwrap_or_else(|| "localhost".to_string()), + ) + .await?; let mut inner_server_config = if client_auth { let attested_cert_verifier = AttestedCertificateVerifier::new(None, attestation_verifier)?; @@ -1147,7 +1144,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), "127.0.0.1:0", @@ -1254,7 +1251,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), "127.0.0.1:0", @@ -1322,9 +1319,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config: server_tls_server_config, - certificate_name: Some( - certificate_identity_from_chain(&server_cert_chain).unwrap(), - ), + certificate_name: certificate_identity_from_chain(&server_cert_chain).unwrap(), }, }), "127.0.0.1:0", @@ -1382,9 +1377,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some( - certificate_identity_from_chain(&server_cert_chain).unwrap(), - ), + certificate_name: certificate_identity_from_chain(&server_cert_chain).unwrap(), }, }), "127.0.0.1:0", @@ -1454,9 +1447,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config: server_tls_server_config, - certificate_name: Some( - certificate_identity_from_chain(&server_cert_chain).unwrap(), - ), + certificate_name: certificate_identity_from_chain(&server_cert_chain).unwrap(), }, }), "127.0.0.1:0", @@ -1516,7 +1507,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), "127.0.0.1:0", @@ -1564,7 +1555,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), "127.0.0.1:0", @@ -1610,7 +1601,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), "127.0.0.1:0", @@ -1681,7 +1672,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), "127.0.0.1:0", @@ -1760,7 +1751,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::Preconfigured { server_config, - certificate_name: Some(certificate_identity_from_chain(&cert_chain).unwrap()), + certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), "127.0.0.1:0", From b3ebc9cd6be9378e1f35459b5b13419d71b84190 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 20 Mar 2026 08:38:45 +0100 Subject: [PATCH 07/11] Update CLI documentation --- src/main.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index ad8ad33..0d7eb61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,7 +77,7 @@ enum CliCommand { }, /// Run a proxy server Server { - /// Socket address to listen on for the outer nested-TLS listener + /// Socket address to listen on for the outer nested-TLS listener, if enabled #[arg(long, default_value = "0.0.0.0:443")] outer_listen_addr: SocketAddr, /// Socket address to listen on for the inner-only attested TLS listener @@ -86,13 +86,13 @@ enum CliCommand { /// The hostname:port or ip:port of the target service to forward traffic to target_addr: String, /// Type of attestation to present (dafaults to 'auto' for automatic detection) - /// If other than None, a TLS key and certicate must also be given + /// This configures the inner attested TLS listener and does not require outer TLS certs. #[arg(long, env = "SERVER_ATTESTATION_TYPE")] server_attestation_type: Option, - /// The path to a PEM encoded private key + /// The path to a PEM encoded private key for the optional outer nested-TLS listener #[arg(long, env = "TLS_PRIVATE_KEY_PATH")] tls_private_key_path: Option, - /// Additional CA certificate to verify against (PEM) Defaults to no additional TLS certs. + /// PEM certificate chain for the optional outer nested-TLS listener #[arg(long, env = "TLS_CERTIFICATE_PATH")] tls_certificate_path: Option, /// Whether to use client authentication. If the client is running in a CVM this must be @@ -122,20 +122,20 @@ enum CliCommand { AttestedFileServer { /// Filesystem path to statically serve path_to_serve: PathBuf, - /// Socket address to listen on for the outer nested-TLS listener + /// Socket address to listen on for the outer nested-TLS listener, if enabled #[arg(long, default_value = "0.0.0.0:443")] outer_listen_addr: SocketAddr, /// Socket address to listen on for the inner-only attested TLS listener #[arg(long, default_value = "0.0.0.0:4433")] inner_listen_addr: SocketAddr, /// Type of attestation to present (dafaults to none) - /// If other than None, a TLS key and certicate must also be given + /// This configures the inner attested TLS listener and does not require outer TLS certs. #[arg(long, env = "SERVER_ATTESTATION_TYPE")] server_attestation_type: Option, - /// The path to a PEM encoded private key + /// The path to a PEM encoded private key for the optional outer nested-TLS listener #[arg(long, env = "TLS_PRIVATE_KEY_PATH")] tls_private_key_path: Option, - /// Additional CA certificate to verify against (PEM) Defaults to no additional TLS certs. + /// PEM certificate chain for the optional outer nested-TLS listener #[arg(long, env = "TLS_CERTIFICATE_PATH")] tls_certificate_path: Option, /// URL of the remote dummy attestation service. Only use with --server-attestation-type From a42c81d99c9f09db71e9a85423ea4b3703d167e4 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 20 Mar 2026 09:15:27 +0100 Subject: [PATCH 08/11] Make inner and outer session optional and dont use default ports --- src/attested_get.rs | 2 +- src/file_server.rs | 16 ++--- src/lib.rs | 146 ++++++++++++++++++++++++++++++-------------- src/main.rs | 62 +++++++++++++++---- 4 files changed, 160 insertions(+), 66 deletions(-) diff --git a/src/attested_get.rs b/src/attested_get.rs index 9bd7118..7fc40b9 100644 --- a/src/attested_get.rs +++ b/src/attested_get.rs @@ -85,7 +85,7 @@ mod tests { certificate_name: "localhost".to_string(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), diff --git a/src/file_server.rs b/src/file_server.rs index 2867972..a8dfbd0 100644 --- a/src/file_server.rs +++ b/src/file_server.rs @@ -11,8 +11,8 @@ use tower_http::services::ServeDir; pub async fn attested_file_server( path_to_serve: PathBuf, outer_cert_and_key: Option, - outer_listen_addr: impl ToSocketAddrs, - inner_listen_addr: impl ToSocketAddrs, + outer_listen_addr: Option, + inner_listen_addr: Option, attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, client_auth: bool, @@ -20,10 +20,12 @@ pub async fn attested_file_server( let target_addr = static_file_server(path_to_serve).await?; let server = ProxyServer::new( - outer_cert_and_key.map(|cert_and_key| OuterTlsConfig { - listen_addr: outer_listen_addr, - tls: OuterTlsMode::CertAndKey(cert_and_key), - }), + outer_cert_and_key + .zip(outer_listen_addr) + .map(|(cert_and_key, listen_addr)| OuterTlsConfig { + listen_addr, + tls: OuterTlsMode::CertAndKey(cert_and_key), + }), inner_listen_addr, target_addr.to_string(), attestation_generator, @@ -113,7 +115,7 @@ mod tests { certificate_name: "localhost".to_string(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), diff --git a/src/lib.rs b/src/lib.rs index 084f70c..213ed3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,9 @@ type RequestWithResponseSender = ( oneshot::Sender>, hyper::Error>>, ); +type OuterProxySession = (Arc, NestingTlsAcceptor); +type InnerProxySession = (Arc, TlsAcceptor); + /// TLS Credentials pub struct TlsCertAndKey { /// Der-encoded TLS certificate chain @@ -207,19 +210,17 @@ pub async fn get_inner_tls_cert_with_config( /// A TLS over TCP server which provides an attestation before forwarding traffic to a given target address pub struct ProxyServer { - outer_listener: Option>, - outer_tls_acceptor: Option, - inner_listener: Arc, - inner_tls_acceptor: TlsAcceptor, + outer: Option, + inner: Option, /// The address/hostname of the target service we are proxying to target: String, } impl ProxyServer { /// Start with dual listeners. The outer nested-TLS listener is optional. - pub async fn new( + pub async fn new( outer_session: Option>, - inner_local: impl ToSocketAddrs, + inner_local: Option, target: String, attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, @@ -227,7 +228,12 @@ impl ProxyServer { ) -> Result where O: ToSocketAddrs, + I: ToSocketAddrs, { + if outer_session.is_none() && inner_local.is_none() { + return Err(ProxyError::NoListenersConfigured); + } + let certificate_name = outer_session .as_ref() .map(OuterTlsConfig::certificate_name) @@ -241,24 +247,28 @@ impl ProxyServer { ) .await?, ); - let inner_listener = Arc::new(TcpListener::bind(inner_local).await?); - let inner_tls_acceptor = TlsAcceptor::from(inner_server_config.clone()); + let inner = match inner_local { + Some(inner_local) => { + let inner_listener = Arc::new(TcpListener::bind(inner_local).await?); + let inner_tls_acceptor = TlsAcceptor::from(inner_server_config.clone()); + Some((inner_listener, inner_tls_acceptor)) + } + None => None, + }; - let (outer_listener, outer_tls_acceptor) = match outer_session { + let outer = match outer_session { Some(outer_session) => { let (outer_listener, outer_tls_acceptor) = outer_session .into_listener_and_acceptor(inner_server_config.clone(), client_auth) .await?; - (Some(outer_listener), Some(outer_tls_acceptor)) + Some((outer_listener, outer_tls_acceptor)) } - None => (None, None), + None => None, }; Ok(Self { - outer_listener, - outer_tls_acceptor, - inner_listener, - inner_tls_acceptor, + outer, + inner, target, }) } @@ -268,13 +278,14 @@ impl ProxyServer { /// Returns the handle for the task handling the connection pub async fn accept(&self) -> Result, ProxyError> { let target = self.target.clone(); - let outer_listener = self.outer_listener.clone(); - let outer_tls_acceptor = self.outer_tls_acceptor.clone(); - let inner_listener = self.inner_listener.clone(); - let inner_tls_acceptor = self.inner_tls_acceptor.clone(); - - let join_handle = match (outer_listener, outer_tls_acceptor) { - (Some(outer_listener), Some(outer_tls_acceptor)) => { + let outer = self.outer.clone(); + let inner = self.inner.clone(); + + let join_handle = match (outer, inner) { + ( + Some((outer_listener, outer_tls_acceptor)), + Some((inner_listener, inner_tls_acceptor)), + ) => { let ((inbound, client_addr), use_outer) = tokio::select! { accepted = outer_listener.accept() => (accepted?, true), accepted = inner_listener.accept() => (accepted?, false), @@ -312,7 +323,7 @@ impl ProxyServer { } }) } - _ => { + (None, Some((inner_listener, inner_tls_acceptor))) => { let (inbound, client_addr) = inner_listener.accept().await?; tokio::spawn(async move { match inner_tls_acceptor.accept(inbound).await { @@ -329,6 +340,24 @@ impl ProxyServer { } }) } + (Some((outer_listener, outer_tls_acceptor)), None) => { + let (inbound, client_addr) = outer_listener.accept().await?; + tokio::spawn(async move { + match outer_tls_acceptor.accept(inbound).await { + Ok(tls_stream) => { + if let Err(err) = + Self::handle_outer_connection(tls_stream, target, client_addr).await + { + warn!("Failed to handle outer connection: {err}"); + } + } + Err(err) => { + warn!("Outer attestation exchange failed: {err}"); + } + } + }) + } + _ => return Err(ProxyError::NoListenersConfigured), }; Ok(join_handle) @@ -336,21 +365,29 @@ impl ProxyServer { /// Helper to get the socket address of the underlying TCP listener pub fn local_addr(&self) -> std::io::Result { - match &self.outer_listener { - Some(listener) => listener.local_addr(), - None => self.inner_listener.local_addr(), + match &self.outer { + Some((listener, _)) => listener.local_addr(), + None => self + .inner + .as_ref() + .map(|(listener, _)| listener) + .ok_or_else(|| std::io::Error::other("no listeners configured"))? + .local_addr(), } } pub fn outer_local_addr(&self) -> std::io::Result> { - self.outer_listener + self.outer .as_ref() - .map(|listener| listener.local_addr()) + .map(|(listener, _)| listener.local_addr()) .transpose() } - pub fn inner_local_addr(&self) -> std::io::Result { - self.inner_listener.local_addr() + pub fn inner_local_addr(&self) -> std::io::Result> { + self.inner + .as_ref() + .map(|(listener, _)| listener.local_addr()) + .transpose() } async fn handle_outer_connection( @@ -909,6 +946,8 @@ pub enum ProxyError { MpscSend, #[error("Client auth must be configured on both the inner and outer TLS sessions")] ClientAuthMisconfigured, + #[error("At least one server listener must be configured")] + NoListenersConfigured, } impl From> for ProxyError { @@ -1039,6 +1078,21 @@ mod tests { assert_eq!(protocols, vec![ALPN_HTTP11.to_vec(), ALPN_H2.to_vec()]); } + #[tokio::test(flavor = "multi_thread")] + async fn proxy_server_requires_at_least_one_listener() { + let result = ProxyServer::new( + None::>, + None::<&str>, + "127.0.0.1:1".to_string(), + AttestationGenerator::with_no_attestation(), + AttestationVerifier::expect_none(), + false, + ) + .await; + + assert!(matches!(result, Err(ProxyError::NoListenersConfigured))); + } + #[tokio::test(flavor = "multi_thread")] async fn dual_listener_server_reports_expected_addresses() { let target_addr = example_http_service().await; @@ -1054,7 +1108,7 @@ mod tests { listen_addr: "127.0.0.1:0", tls: OuterTlsMode::CertAndKey(tls_cert_and_key), }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::expect_none(), @@ -1064,13 +1118,13 @@ mod tests { .unwrap(); let outer_addr = dual_listener_server.outer_local_addr().unwrap().unwrap(); - let inner_addr = dual_listener_server.inner_local_addr().unwrap(); + let inner_addr = dual_listener_server.inner_local_addr().unwrap().unwrap(); assert_eq!(dual_listener_server.local_addr().unwrap(), outer_addr); assert_ne!(outer_addr, inner_addr); let inner_only_server = ProxyServer::new( None::>, - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::expect_none(), @@ -1079,7 +1133,7 @@ mod tests { .await .unwrap(); - let inner_only_addr = inner_only_server.inner_local_addr().unwrap(); + let inner_only_addr = inner_only_server.inner_local_addr().unwrap().unwrap(); assert!(inner_only_server.outer_local_addr().unwrap().is_none()); assert_eq!(inner_only_server.local_addr().unwrap(), inner_only_addr); } @@ -1091,7 +1145,7 @@ mod tests { let proxy_server = ProxyServer::new( None::>, - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), @@ -1100,7 +1154,7 @@ mod tests { .await .unwrap(); - let inner_addr = proxy_server.inner_local_addr().unwrap(); + let inner_addr = proxy_server.inner_local_addr().unwrap().unwrap(); tokio::spawn(async move { proxy_server.accept().await.unwrap(); @@ -1147,7 +1201,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), @@ -1254,7 +1308,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), @@ -1322,7 +1376,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&server_cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::mock(), @@ -1380,7 +1434,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&server_cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::mock(), @@ -1450,7 +1504,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&server_cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::mock(), @@ -1510,7 +1564,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), @@ -1558,7 +1612,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::with_no_attestation(), AttestationVerifier::expect_none(), @@ -1604,7 +1658,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), @@ -1675,7 +1729,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), @@ -1754,7 +1808,7 @@ mod tests { certificate_name: certificate_identity_from_chain(&cert_chain).unwrap(), }, }), - "127.0.0.1:0", + Some("127.0.0.1:0"), target_addr.to_string(), AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), AttestationVerifier::expect_none(), diff --git a/src/main.rs b/src/main.rs index 0d7eb61..a80a54b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,11 +78,11 @@ enum CliCommand { /// Run a proxy server Server { /// Socket address to listen on for the outer nested-TLS listener, if enabled - #[arg(long, default_value = "0.0.0.0:443")] - outer_listen_addr: SocketAddr, + #[arg(long)] + outer_listen_addr: Option, /// Socket address to listen on for the inner-only attested TLS listener - #[arg(long, default_value = "0.0.0.0:4433")] - inner_listen_addr: SocketAddr, + #[arg(long)] + inner_listen_addr: Option, /// The hostname:port or ip:port of the target service to forward traffic to target_addr: String, /// Type of attestation to present (dafaults to 'auto' for automatic detection) @@ -123,11 +123,11 @@ enum CliCommand { /// Filesystem path to statically serve path_to_serve: PathBuf, /// Socket address to listen on for the outer nested-TLS listener, if enabled - #[arg(long, default_value = "0.0.0.0:443")] - outer_listen_addr: SocketAddr, + #[arg(long)] + outer_listen_addr: Option, /// Socket address to listen on for the inner-only attested TLS listener - #[arg(long, default_value = "0.0.0.0:4433")] - inner_listen_addr: SocketAddr, + #[arg(long)] + inner_listen_addr: Option, /// Type of attestation to present (dafaults to none) /// This configures the inner attested TLS listener and does not require outer TLS certs. #[arg(long, env = "SERVER_ATTESTATION_TYPE")] @@ -299,16 +299,23 @@ async fn main() -> anyhow::Result<()> { let tls_cert_and_chain = load_tls_cert_and_key_server(tls_certificate_path, tls_private_key_path)?; + validate_listener_args( + inner_listen_addr, + outer_listen_addr, + tls_cert_and_chain.is_some(), + )?; let local_attestation_generator = AttestationGenerator::new_with_detection(server_attestation_type, dev_dummy_dcap) .await?; let server = ProxyServer::new( - tls_cert_and_chain.map(|cert_and_key| OuterTlsConfig { - listen_addr: outer_listen_addr, - tls: OuterTlsMode::CertAndKey(cert_and_key), - }), + tls_cert_and_chain + .zip(outer_listen_addr) + .map(|(cert_and_key, listen_addr)| OuterTlsConfig { + listen_addr, + tls: OuterTlsMode::CertAndKey(cert_and_key), + }), inner_listen_addr, target_addr, local_attestation_generator, @@ -363,6 +370,11 @@ async fn main() -> anyhow::Result<()> { } => { let tls_cert_and_chain = load_tls_cert_and_key_server(tls_certificate_path, tls_private_key_path)?; + validate_listener_args( + inner_listen_addr, + outer_listen_addr, + tls_cert_and_chain.is_some(), + )?; let server_attestation_type: AttestationType = serde_json::from_value( serde_json::Value::String(server_attestation_type.unwrap_or("none".to_string())), @@ -433,6 +445,32 @@ fn load_tls_cert_and_key_server( } } +fn validate_listener_args( + inner_listen_addr: Option, + outer_listen_addr: Option, + has_outer_tls: bool, +) -> anyhow::Result<()> { + if inner_listen_addr.is_none() && outer_listen_addr.is_none() { + return Err(anyhow!( + "At least one of --inner-listen-addr or --outer-listen-addr must be provided" + )); + } + + if has_outer_tls && outer_listen_addr.is_none() { + return Err(anyhow!( + "--outer-listen-addr is required when TLS certificate and key are provided" + )); + } + + if !has_outer_tls && outer_listen_addr.is_some() { + return Err(anyhow!( + "--outer-listen-addr requires TLS certificate and key" + )); + } + + Ok(()) +} + /// Load TLS details from storage fn load_tls_cert_and_key( cert_chain: PathBuf, From d2d9b7c5561d8a684dc273762479d20e4a009102 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 20 Mar 2026 09:18:05 +0100 Subject: [PATCH 09/11] Small fix for attested file server --- src/file_server.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/file_server.rs b/src/file_server.rs index a8dfbd0..4c5c9bb 100644 --- a/src/file_server.rs +++ b/src/file_server.rs @@ -18,14 +18,19 @@ pub async fn attested_file_server( client_auth: bool, ) -> Result<(), ProxyError> { let target_addr = static_file_server(path_to_serve).await?; + let outer_session = match (outer_cert_and_key, outer_listen_addr) { + (Some(cert_and_key), Some(listen_addr)) => Some(OuterTlsConfig { + listen_addr, + tls: OuterTlsMode::CertAndKey(cert_and_key), + }), + (Some(_), None) | (None, Some(_)) => { + return Err(ProxyError::NoListenersConfigured); + } + (None, None) => None, + }; let server = ProxyServer::new( - outer_cert_and_key - .zip(outer_listen_addr) - .map(|(cert_and_key, listen_addr)| OuterTlsConfig { - listen_addr, - tls: OuterTlsMode::CertAndKey(cert_and_key), - }), + outer_session, inner_listen_addr, target_addr.to_string(), attestation_generator, From 634fb4fc94cb73387b72325665b76ee228302207 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 20 Mar 2026 09:28:10 +0100 Subject: [PATCH 10/11] Tidy, rm unneeded test --- src/lib.rs | 58 ++++-------------------------------------------------- 1 file changed, 4 insertions(+), 54 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 213ed3f..b3b69c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -363,7 +363,7 @@ impl ProxyServer { Ok(join_handle) } - /// Helper to get the socket address of the underlying TCP listener + /// Helper to get the socket address of either underlying TCP listener pub fn local_addr(&self) -> std::io::Result { match &self.outer { Some((listener, _)) => listener.local_addr(), @@ -376,6 +376,7 @@ impl ProxyServer { } } + /// Helper to get the socket address of the underlying outer TCP listener if present pub fn outer_local_addr(&self) -> std::io::Result> { self.outer .as_ref() @@ -383,6 +384,7 @@ impl ProxyServer { .transpose() } + /// Helper to get the socket address of the underlying inner TCP listener if present pub fn inner_local_addr(&self) -> std::io::Result> { self.inner .as_ref() @@ -1239,58 +1241,6 @@ mod tests { assert!(matches!(conn, HttpConnection::Http2 { .. })); } - // #[tokio::test(flavor = "multi_thread")] - // async fn http_proxy_default_constructors_work() { - // let target_addr = example_http_service().await; - // - // let (cert_chain, private_key) = generate_certificate_chain_for_host("localhost"); - // let server_cert = cert_chain[0].clone(); - // - // let proxy_server = ProxyServer::new( - // TlsCertAndKey { - // cert_chain, - // key: private_key, - // }, - // "127.0.0.1:0", - // target_addr.to_string(), - // AttestationGenerator::new(AttestationType::DcapTdx, None).unwrap(), - // AttestationVerifier::expect_none(), - // false, - // ) - // .await - // .unwrap(); - // - // let proxy_addr = proxy_server.local_addr().unwrap(); - // - // tokio::spawn(async move { - // proxy_server.accept().await.unwrap(); - // }); - // - // let proxy_client = ProxyClient::new( - // None, - // "127.0.0.1:0".to_string(), - // format!("localhost:{}", proxy_addr.port()), - // AttestationGenerator::with_no_attestation(), - // AttestationVerifier::mock(), - // Some(server_cert), - // ) - // .await - // .unwrap(); - // - // let proxy_client_addr = proxy_client.local_addr().unwrap(); - // - // tokio::spawn(async move { - // proxy_client.accept().await.unwrap(); - // }); - // - // let res = reqwest::get(format!("http://{}", proxy_client_addr)) - // .await - // .unwrap(); - // - // let res_body = res.text().await.unwrap(); - // assert_eq!(res_body, "No measurements"); - // } - // Server has mock DCAP, client has no attestation and no client auth #[tokio::test(flavor = "multi_thread")] async fn http_proxy_with_server_attestation() { @@ -1334,7 +1284,7 @@ mod tests { .await .unwrap(); - let proxy_client_addr = proxy_client.local_addr().unwrap(); + let proy_client_addr = proxy_client.local_addr().unwrap(); tokio::spawn(async move { proxy_client.accept().await.unwrap(); From a9bb332e9f77750b3e04b9d46f1751b67d57d3a6 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 20 Mar 2026 09:35:42 +0100 Subject: [PATCH 11/11] Typo --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b3b69c7..88aa200 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1284,7 +1284,7 @@ mod tests { .await .unwrap(); - let proy_client_addr = proxy_client.local_addr().unwrap(); + let proxy_client_addr = proxy_client.local_addr().unwrap(); tokio::spawn(async move { proxy_client.accept().await.unwrap();