Pārlūkot izejas kodu

feat: https support transparent proxy

zu1k 3 gadi atpakaļ
vecāks
revīzija
86fb6d7483
5 mainītis faili ar 124 papildinājumiem un 26 dzēšanām
  1. 1 1
      crates/core/Cargo.toml
  2. 3 6
      crates/core/src/ca.rs
  3. 27 0
      crates/core/src/lib.rs
  4. 67 16
      crates/core/src/mitm.rs
  5. 26 3
      src/main.rs

+ 1 - 1
crates/core/Cargo.toml

@@ -12,7 +12,7 @@ async-trait = "0.1"
 bytes = { version = "1", features = ["serde"] }
 cfg-if = "1"
 http = "0.2"
-hyper = { version = "0.14", features = ["client", "http1", "server", "stream", "tcp"]  }
+hyper = { version = "0.14", features = ["http1", "http2", "server", "stream", "tcp", "runtime"]  }
 hyper-proxy = { version = "0.9" }
 hyper-rustls = { version = "0.23" }
 hyper-tls = { version = "0.5", optional = true }

+ 3 - 6
crates/core/src/ca.rs

@@ -146,11 +146,8 @@ impl CertificateAuthority {
 
 impl ResolvesServerCert for CertificateAuthority {
     fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
-        if let Some(name) = client_hello.server_name() {
-            Some(self.get_certified_key(name))
-        } else {
-            // This kind of resolver requires SNI
-            None
-        }
+        client_hello
+            .server_name()
+            .map(|name| self.get_certified_key(name))
     }
 }

+ 27 - 0
crates/core/src/lib.rs

@@ -9,6 +9,7 @@ use hyper::{
 use hyper_proxy::Proxy as UpstreamProxy;
 use mitm::MitmProxy;
 use std::{future::Future, marker::PhantomData, net::SocketAddr, sync::Arc};
+use tokio::net::TcpListener;
 use typed_builder::TypedBuilder;
 
 pub use ca::CertificateAuthority;
@@ -63,6 +64,8 @@ where
             let http_handler = Arc::clone(&http_handler);
             let mitm_filter = Arc::clone(&mitm_filter);
 
+            // TODO: conn tls or http?
+
             async move {
                 Ok::<_, Error>(service_fn(move |req| {
                     MitmProxy {
@@ -87,4 +90,28 @@ where
             .await
             .map_err(Error::from)
     }
+
+    pub async fn start_https_transparent_proxy(self) -> Result<(), Error> {
+        let client = gen_client(self.upstream_proxy)?;
+        let ca = Arc::new(self.ca);
+        let http_handler = Arc::new(self.handler);
+        let mitm_filter = Arc::new(MitmFilter::new(self.mitm_filters));
+
+        let tcp_listener = TcpListener::bind(self.listen_addr).await?;
+
+        loop {
+            let (tcp_stream, _) = tcp_listener.accept().await?;
+            MitmProxy {
+                ca: Arc::clone(&ca),
+                client: client.clone(),
+
+                http_handler: Arc::clone(&http_handler),
+                mitm_filter: Arc::clone(&mitm_filter),
+
+                custom_contex_data: Default::default(),
+            }
+            .serve_tls(tcp_stream)
+            .await;
+        }
+    }
 }

+ 67 - 16
crates/core/src/mitm.rs

@@ -14,7 +14,10 @@ use hyper::{
 };
 use log::*;
 use std::{marker::PhantomData, sync::Arc};
-use tokio::net::TcpStream;
+use tokio::{
+    io::{AsyncRead, AsyncWrite},
+    net::TcpStream,
+};
 use tokio_rustls::TlsAcceptor;
 
 /// Enum representing either an HTTP request or response.
@@ -136,6 +139,50 @@ where
         Ok(res)
     }
 
+    // async fn process_connect(self, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
+    //     let ctx = HttpContext {
+    //         uri: None,
+    //         should_modify_response: false,
+    //         ..Default::default()
+    //     };
+
+    //     if self.mitm_filter.filter(&ctx, &req).await {
+    //         tokio::task::spawn(async move {
+    //             let authority = req
+    //                 .uri()
+    //                 .authority()
+    //                 .expect("URI does not contain authority")
+    //                 .clone();
+
+    //             match hyper::upgrade::on(req).await {
+    //                 Ok(upgraded) => {
+    //                     let server_config = self.ca.clone().gen_server_config();
+
+    //                     let stream = TlsAcceptor::from(server_config)
+    //                         .accept(upgraded)
+    //                         .await
+    //                         .expect("Failed to establish TLS connection with client");
+
+    //                     if let Err(e) = self.serve_tls_stream(stream).await {
+    //                         let e_string = e.to_string();
+    //                         if !e_string.starts_with("error shutting down connection") {
+    //                             debug!("res:: {}", e);
+    //                         }
+    //                     }
+    //                 }
+    //                 Err(e) => debug!("upgrade error for {}: {}", authority, e),
+    //             };
+    //         });
+    //     } else {
+    //         tokio::task::spawn(async move {
+    //             let remote_addr = host_addr(req.uri()).unwrap();
+    //             let upgraded = hyper::upgrade::on(req).await.unwrap();
+    //             tunnel(upgraded, remote_addr).await
+    //         });
+    //     }
+    //     Ok(Response::new(Body::empty()))
+    // }
+
     async fn process_connect(self, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
         let ctx = HttpContext {
             uri: None,
@@ -153,19 +200,7 @@ where
 
                 match hyper::upgrade::on(req).await {
                     Ok(upgraded) => {
-                        let server_config = self.ca.clone().gen_server_config();
-
-                        let stream = TlsAcceptor::from(server_config)
-                            .accept(upgraded)
-                            .await
-                            .expect("Failed to establish TLS connection with client");
-
-                        if let Err(e) = self.serve_https(stream).await {
-                            let e_string = e.to_string();
-                            if !e_string.starts_with("error shutting down connection") {
-                                debug!("res:: {}", e);
-                            }
-                        }
+                        self.serve_tls(upgraded).await;
                     }
                     Err(e) => debug!("upgrade error for {}: {}", authority, e),
                 };
@@ -180,9 +215,25 @@ where
         Ok(Response::new(Body::empty()))
     }
 
-    async fn serve_https(
+    pub async fn serve_tls<IO: AsyncRead + AsyncWrite + Unpin + Send + 'static>(self, stream: IO) {
+        let server_config = self.ca.clone().gen_server_config();
+
+        let stream = TlsAcceptor::from(server_config)
+            .accept(stream)
+            .await
+            .expect("Failed to establish TLS connection with client");
+
+        if let Err(e) = self.serve_tls_stream(stream).await {
+            let e_string = e.to_string();
+            if !e_string.starts_with("error shutting down connection") {
+                debug!("res:: {}", e);
+            }
+        }
+    }
+
+    async fn serve_tls_stream<IO: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
         self,
-        stream: tokio_rustls::server::TlsStream<Upgraded>,
+        stream: tokio_rustls::server::TlsStream<IO>,
     ) -> Result<(), hyper::Error> {
         let service = service_fn(|mut req| {
             if req.version() == http::Version::HTTP_10 || req.version() == http::Version::HTTP_11 {

+ 26 - 3
src/main.rs

@@ -7,7 +7,7 @@ use log::*;
 use mitm_core::{CertificateAuthority, Proxy};
 use rule::RuleHttpHandler;
 use rustls_pemfile as pemfile;
-use std::{fs, sync::Arc};
+use std::{fs, net::SocketAddr, sync::Arc};
 
 use good_mitm::*;
 
@@ -97,7 +97,7 @@ async fn run(opts: &Run) -> Result<()> {
     let http_handler = RuleHttpHandler::new(rules);
 
     let proxy = Proxy::builder()
-        .ca(ca)
+        .ca(ca.clone())
         .listen_addr(opts.bind.parse().expect("bind address not valid!"))
         .upstream_proxy(
             opts.proxy
@@ -105,9 +105,32 @@ async fn run(opts: &Run) -> Result<()> {
                 .map(|proxy| hyper_proxy::Proxy::new(Intercept::All, proxy.parse().unwrap())),
         )
         .shutdown_signal(shutdown_signal())
+        .mitm_filters(mitm_filters.clone())
+        .handler(http_handler.clone())
+        .build();
+
+    tokio::spawn(proxy.start_proxy());
+
+    let mut bind: SocketAddr = opts.bind.parse().expect("bind address not valid!");
+    bind.set_port(bind.port() + 1);
+    info!("Https Transparent Proxy listen on: {}", bind);
+    let proxy2 = Proxy::builder()
+        .ca(ca)
+        .listen_addr(bind)
+        .upstream_proxy(
+            opts.proxy
+                .clone()
+                .map(|proxy| hyper_proxy::Proxy::new(Intercept::All, proxy.parse().unwrap())),
+        )
+        .shutdown_signal(shutdown_signal())
         .mitm_filters(mitm_filters)
         .handler(http_handler)
         .build();
-    proxy.start_proxy().await?;
+
+    tokio::spawn(proxy2.start_https_transparent_proxy());
+
+    tokio::signal::ctrl_c()
+        .await
+        .expect("failed to listen for event");
     Ok(())
 }