Implement HTTP proxy authentication

This commit is contained in:
B. Blechschmidt 2023-03-22 10:12:32 +01:00
parent d2aef08e3c
commit 0fb58bec5d
4 changed files with 57 additions and 27 deletions

View file

@ -5,6 +5,7 @@ name = "tun2proxy"
version = "0.1.1" version = "0.1.1"
[dependencies] [dependencies]
base64 = { version = "0.21" }
clap = { version = "4.1", features = ["derive"] } clap = { version = "4.1", features = ["derive"] }
env_logger = "0.10" env_logger = "0.10"
log = "0.4" log = "0.4"

View file

@ -36,21 +36,39 @@ Note that if you paste these commands into a shell script, which you then run wi
For DNS to work, you might need an additional tool like [dnsproxy](https://github.com/AdguardTeam/dnsproxy) that is For DNS to work, you might need an additional tool like [dnsproxy](https://github.com/AdguardTeam/dnsproxy) that is
configured to listen on a local UDP port and communicates with the upstream DNS server via TCP. configured to listen on a local UDP port and communicates with the upstream DNS server via TCP.
## CLI ## CLI (unstable)
``` ```
Tunnel interface to proxy. Tunnel interface to proxy.
Usage: tun2proxy --tun <name> --proxy <type> --addr <ip:port> Usage: tun2proxy [OPTIONS] --tun <name> --proxy <type> --addr <ip:port>
Options: Options:
-t, --tun <name> Name of the tun interface -t, --tun <name>
-p, --proxy <type> What proxy type to run [possible values: socks5, http] Name of the tun interface
-a, --addr <ip:port> Server address with format ip:port
-h, --help Print help (see more with '--help') -p, --proxy <type>
-V, --version Print version What proxy type to run
Possible values:
- socks5: SOCKS5 server to use
- http: HTTP server to use
-a, --addr <ip:port>
Server address with format ip:port
--username <username>
Username for authentication
--password <password>
Password for authentication
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
``` ```
## TODO ## TODO
- Authentication for SOCKS (plain) and HTTP (base64)
- UDP support for SOCKS - UDP support for SOCKS
- Virtual DNS - Virtual DNS

View file

@ -2,6 +2,7 @@ use crate::tun2proxy::{
Connection, ConnectionManager, Credentials, IncomingDataEvent, IncomingDirection, Connection, ConnectionManager, Credentials, IncomingDataEvent, IncomingDirection,
OutgoingDataEvent, OutgoingDirection, ProxyError, TcpProxy, OutgoingDataEvent, OutgoingDirection, ProxyError, TcpProxy,
}; };
use base64::Engine;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::net::SocketAddr; use std::net::SocketAddr;
@ -25,28 +26,36 @@ pub struct HttpConnection {
} }
impl HttpConnection { impl HttpConnection {
fn new(connection: &Connection) -> Self { fn new(connection: &Connection, manager: std::rc::Rc<dyn ConnectionManager>) -> Self {
let mut result = Self { let mut server_outbuf: VecDeque<u8> = VecDeque::new();
{
let credentials = manager.get_credentials();
server_outbuf.extend(b"CONNECT ".iter());
server_outbuf.extend(connection.dst.to_string().as_bytes());
server_outbuf.extend(b" HTTP/1.1\r\nHost: ".iter());
server_outbuf.extend(connection.dst.to_string().as_bytes());
server_outbuf.extend(b"\r\n".iter());
if credentials.authenticate {
server_outbuf.extend(b"Proxy-Authorization: Basic ");
let mut auth_plain = credentials.username.clone();
auth_plain.extend(b":".iter());
auth_plain.extend(&credentials.password);
let auth_b64 = base64::engine::general_purpose::STANDARD.encode(auth_plain);
server_outbuf.extend(auth_b64.as_bytes().iter());
server_outbuf.extend(b"\r\n".iter());
}
server_outbuf.extend(b"\r\n".iter());
}
Self {
state: HttpState::ExpectStatusCode, state: HttpState::ExpectStatusCode,
client_inbuf: Default::default(), client_inbuf: Default::default(),
server_inbuf: Default::default(), server_inbuf: Default::default(),
client_outbuf: Default::default(), client_outbuf: Default::default(),
server_outbuf: Default::default(), server_outbuf,
data_buf: Default::default(), data_buf: Default::default(),
crlf_state: Default::default(), crlf_state: Default::default(),
}; }
result.server_outbuf.extend(b"CONNECT ".iter());
result
.server_outbuf
.extend(connection.dst.to_string().as_bytes());
result.server_outbuf.extend(b" HTTP/1.1\r\nHost: ".iter());
result
.server_outbuf
.extend(connection.dst.to_string().as_bytes());
result.server_outbuf.extend(b"\r\n\r\n".iter());
result
} }
fn state_change(&mut self) -> Result<(), ProxyError> { fn state_change(&mut self) -> Result<(), ProxyError> {
@ -167,12 +176,14 @@ impl ConnectionManager for HttpManager {
fn new_connection( fn new_connection(
&self, &self,
connection: &Connection, connection: &Connection,
_manager: std::rc::Rc<dyn ConnectionManager>, manager: std::rc::Rc<dyn ConnectionManager>,
) -> Option<std::boxed::Box<dyn TcpProxy>> { ) -> Option<std::boxed::Box<dyn TcpProxy>> {
if connection.proto != smoltcp::wire::IpProtocol::Tcp.into() { if connection.proto != smoltcp::wire::IpProtocol::Tcp.into() {
return None; return None;
} }
Some(std::boxed::Box::new(HttpConnection::new(connection))) Some(std::boxed::Box::new(HttpConnection::new(
connection, manager,
)))
} }
fn close_connection(&self, _: &Connection) {} fn close_connection(&self, _: &Connection) {}

View file

@ -26,7 +26,7 @@ struct Args {
#[clap(long, value_name = "username")] #[clap(long, value_name = "username")]
username: Option<String>, username: Option<String>,
/// Username for authentication /// Password for authentication
#[clap(long, value_name = "password")] #[clap(long, value_name = "password")]
password: Option<String>, password: Option<String>,
} }