-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Client With Proxy #67
Comments
@hatoo are you planning to implement this feature? Or at least write how to implement it, I'm trying to do it myself. but it doesn't work. |
|
I should have said that I want to use an https proxy with custom authentication. async fn tunnel_via_proxy(
&self,
mut client: TokioIo<hyper::upgrade::Upgraded>,
addr: String,
proxy_config: &ProxyConfig,
) {
tracing::info!("Using external proxy for addr: {}", addr);
// Establish a TCP connection with a timeout
let tcp = match timeout(
Duration::from_secs(10),
TcpStream::connect(&proxy_config.addr),
)
.await
{
Ok(Ok(tcp)) => tcp,
Ok(Err(e)) => {
tracing::error!("Failed to connect to proxy: {}", e);
return;
}
Err(_) => {
tracing::error!("Timeout connecting to proxy");
return;
}
};
// Establish a TLS connection with a timeout
let tls = tokio_native_tls::TlsConnector::from(native_tls::TlsConnector::new().unwrap());
let mut stream = match timeout(
Duration::from_secs(10),
tls.connect(proxy_config.addr.split(':').next().unwrap(), tcp),
)
.await
{
Ok(Ok(stream)) => stream,
Ok(Err(e)) => {
tracing::error!("TLS connection failed: {}", e);
return;
}
Err(_) => {
tracing::error!("TLS connection timeout");
return;
}
};
// Formulate and send the CONNECT request
let connect_req = format!(
"CONNECT {addr} HTTP/1.1\r\n\
Host: {addr}\r\n\
Proxy-Connection: Keep-Alive\r\n\
X-Auth-Token: {}\r\n\r\n",
&proxy_config.auth_token,
);
if let Err(e) = stream.write_all(connect_req.as_bytes()).await {
tracing::error!("Failed to send CONNECT request: {}", e);
return;
}
// Read the response with a timeout
let mut response = [0u8; 1024];
let n = match timeout(Duration::from_secs(5), stream.read(&mut response)).await {
Ok(Ok(n)) => n,
Ok(Err(e)) => {
tracing::error!("Failed to read proxy response: {}", e);
return;
}
Err(_) => {
tracing::error!("Timeout reading proxy response");
return;
}
};
// Check the response status
if !response[..n].windows(3).any(|window| window == b"200") {
tracing::error!(
"Proxy connection failed: {}",
String::from_utf8_lossy(&response[..n])
);
return;
}
// Copy data between the client and the proxy
let _ = tokio::io::copy_bidirectional(&mut client, &mut stream).await;
} This is how I handle requests directly /// Establishes a direct tunnel to the specified authority using an upgraded HTTP connection.
///
/// # Arguments
///
/// * `client` - The upgraded client connection to communicate with.
/// * `connect_authority` - The target authority to connect to, represented as a URI Authority.
async fn direct_tunnel(
&self,
client: hyper::upgrade::Upgraded,
connect_authority: hyper::http::uri::Authority,
) {
// Attempt to connect to the specified authority using TcpStream.
match TcpStream::connect(connect_authority.as_str()).await {
// If the connection is successful, proceed with bidirectional data transfer.
Ok(mut server) => {
// Use Tokio's IO utilities to copy data bidirectionally between the client and server.
let _ = tokio::io::copy_bidirectional(&mut TokioIo::new(client), &mut server).await;
}
// If the connection fails, log the error.
Err(err) => {
tracing::error!("Failed to connect to {}: {}", connect_authority, err);
}
}
} And now as an example you can put a condition for CONNECT requests #[derive(Clone)]
pub struct ProxyConfig {
pub addr: String,
pub auth_token: String,
pub domains: Vec<String>,
}
#[derive(Clone)]
/// The main struct to run proxy server
pub struct MitmProxy<C> {
/// Root certificate to sign fake certificates. You may need to trust this certificate on client application to use HTTPS.
///
/// If None, proxy will just tunnel HTTPS traffic and will not observe HTTPS traffic.
pub root_cert: Option<C>,
/// Cache to store generated certificates. If None, cache will not be used.
/// If root_cert is None, cache will not be used.
///
/// The key of cache is hostname.
pub cert_cache: Option<Cache<String, CertifiedKeyDer>>,
pub proxy_config: Option<ProxyConfig>,
}
//.........
// Check if an external proxy should be used first
if let Some(proxy_config) = &proxy.proxy_config {
let addr = connect_authority.to_string();
let host = connect_authority.host().to_string();
// Check if the host matches any of the specified domains for the proxy
if proxy_config.domains.iter().any(|domain| host.contains(domain)) {
// Use the proxy to tunnel the connection for matching domains
proxy.tunnel_via_proxy(TokioIo::new(client), addr, proxy_config).await;
} else {
// Direct connection for any other hosts that do not match the proxy domains
proxy.direct_tunnel(client, connect_authority).await;
}
} else {
// If proxy_config is not provided, use a direct connection
proxy.direct_tunnel(client, connect_authority).await;
} Question, how to implement proxying of MITM requests? |
Are you asking how to do the same thing in the above codes using |
I have exactly https proxies being used, with authentication happening during CONNECT. |
Ok, if you want to do something on Please see https://github.com/hatoo/http-mitm-proxy/blob/master/examples/https.rs http-mitm-proxy/examples/https.rs Line 135 in 0efd900
You can create your own hyper service wrapping MITM service .
|
I understood this solution to have MITM proxy on HTTPS (https://127.0.0.1:3003), but I don't need it). I just want to proxy requests through my external https proxy with custom header in CONNECT and decrypt them like mitmproxy can do for example. But I can't figure out how to do it ( |
What is the problem with using |
I use my own HTTP over TLS proxy server |
I gave above an example of |
Maybe I don't quite understand how MITM works. |
Yes, we can see data from a client after CONNECT. We parse HTTP requests after CONNECT and return a response. There is no tunneling.
We have |
I will try to implement proxy support as it is done in tunnel_via_proxy function in default_client.rs file. |
Could you please tell me how to modify the request and response when using reqwest? No way, this eternal struggle with moving a variable. PS.: I realized, it seems to be possible to modify only in hyper, in reqwest it is not clear how, for the sake of interest if you want you can add a modification to the example reqwest_proxy.rs) let res = client.execute(req).await?;
// You can modify response here
let (parts, body) = from_reqwest(res).into_parts();
Ok::<_, http_mitm_proxy::default_client::Error>(Response::from_parts(
parts,
http_body_util::Full::new(body),
)) |
When using the reqwest client there is a problem with redirects, we get the body of the final url but it loads at the original URL. ![]() |
Can you write a client code that will use an external proxy to access blocked and sites and still be able to decrypt traffic?
You can use the standard Basic header as authentication, but in my case the authentication is custom and just uses the
x-token: auth_token
header.In the end we will have:
so that you can send the desired hosts through an external proxy
The text was updated successfully, but these errors were encountered: