Added TLS --pinnedpubkey support
This commit is contained in:
parent
9d37f3d929
commit
1e7fa84a42
@ -19,7 +19,13 @@ usage: wireguard-proxy [options...]
|
|||||||
-uh, --udp-host <ip:port> UDP host to listen on, point wireguard
|
-uh, --udp-host <ip:port> UDP host to listen on, point wireguard
|
||||||
client here, default: 127.0.0.1:51820
|
client here, default: 127.0.0.1:51820
|
||||||
--tls use TLS when connecting to tcp-target
|
--tls use TLS when connecting to tcp-target
|
||||||
WARNING: currently verifies nothing!
|
WARNING: authenticates/verifies nothing
|
||||||
|
without --pinnedpubkey below!!
|
||||||
|
--pinnedpubkey <sha256_hashes> Public key to verify peer against,
|
||||||
|
format is any number of base64 encoded
|
||||||
|
sha256 hashes preceded by "sha256//"
|
||||||
|
and separated by ";". Identical to curl's
|
||||||
|
--pinnedpubkey and CURLOPT_PINNEDPUBLICKEY
|
||||||
--tls-hostname send this in SNI instead of host
|
--tls-hostname send this in SNI instead of host
|
||||||
from --tcp-target, useful for avoiding
|
from --tcp-target, useful for avoiding
|
||||||
DNS lookup on connect
|
DNS lookup on connect
|
||||||
|
@ -54,8 +54,10 @@ test_script:
|
|||||||
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test &&
|
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test &&
|
||||||
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test -- -is
|
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test -- -is
|
||||||
)
|
)
|
||||||
|
# todo: should run --pinnedpubkey test here where we expect failure, but unsure how to do that with windows, help?
|
||||||
- if [%CARGO_FEATURES%]==[openssl_vendored] (
|
- if [%CARGO_FEATURES%]==[openssl_vendored] (
|
||||||
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem
|
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem &&
|
||||||
|
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4dE=
|
||||||
)
|
)
|
||||||
|
|
||||||
before_deploy:
|
before_deploy:
|
||||||
|
@ -19,6 +19,14 @@ main() {
|
|||||||
if [ $CARGO_FEATURES != "default" ]; then
|
if [ $CARGO_FEATURES != "default" ]; then
|
||||||
# run TLS tests then too
|
# run TLS tests then too
|
||||||
cross run --target $TARGET --release --features $CARGO_FEATURES --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem
|
cross run --target $TARGET --release --features $CARGO_FEATURES --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem
|
||||||
|
|
||||||
|
# now pubkey tests
|
||||||
|
|
||||||
|
# one that should fail (wrong pinnedpubkey lowercase e at end instead of uppercase E)
|
||||||
|
cross run --target $TARGET --release --features $CARGO_FEATURES --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4de= && exit 1 || true
|
||||||
|
|
||||||
|
# and one that should pass
|
||||||
|
cross run --target $TARGET --release --features $CARGO_FEATURES --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4dE=
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ fn main() {
|
|||||||
|
|
||||||
let tls_key = args.get_option(&["-tk", "--tls-key"]);
|
let tls_key = args.get_option(&["-tk", "--tls-key"]);
|
||||||
let tls_cert = args.get_option(&["-tc", "--tls-cert"]);
|
let tls_cert = args.get_option(&["-tc", "--tls-cert"]);
|
||||||
|
let pinnedpubkey = args.get_option(&["--pinnedpubkey"]);
|
||||||
|
|
||||||
let tls = if tls_key.is_some() && tls_cert.is_some() {
|
let tls = if tls_key.is_some() && tls_cert.is_some() {
|
||||||
true
|
true
|
||||||
@ -98,10 +99,15 @@ fn main() {
|
|||||||
before terminating, default: {}
|
before terminating, default: {}
|
||||||
|
|
||||||
TLS option for self tests only, otherwise self tests are plaintext only:
|
TLS option for self tests only, otherwise self tests are plaintext only:
|
||||||
-tk, --tls-key <ip:port> TLS key to listen with,
|
-tk, --tls-key <ip:port> TLS key to listen with,
|
||||||
requires --tls-cert also
|
requires --tls-cert also
|
||||||
-tc, --tls-cert <ip:port> TLS cert to listen with,
|
-tc, --tls-cert <ip:port> TLS cert to listen with,
|
||||||
requires --tls-key also
|
requires --tls-key also
|
||||||
|
--pinnedpubkey <sha256_hashes> Public key to verify peer against,
|
||||||
|
format is any number of base64 encoded
|
||||||
|
sha256 hashes preceded by "sha256//"
|
||||||
|
and separated by ";". Identical to curl's
|
||||||
|
--pinnedpubkey and CURLOPT_PINNEDPUBLICKEY
|
||||||
"#, default_udp_host_target, default_udp_host_target, default_socket_timeout);
|
"#, default_udp_host_target, default_udp_host_target, default_socket_timeout);
|
||||||
return;
|
return;
|
||||||
} else if args.flag("-s") || args.flag("--self-test") {
|
} else if args.flag("-s") || args.flag("--self-test") {
|
||||||
@ -113,50 +119,37 @@ fn main() {
|
|||||||
let udp_test = args.get_str_idx(0, "udp-test");
|
let udp_test = args.get_str_idx(0, "udp-test");
|
||||||
let proxy = udp_test.clone().replace("udp-test", "wireguard-proxy");
|
let proxy = udp_test.clone().replace("udp-test", "wireguard-proxy");
|
||||||
|
|
||||||
let mut proxyd = if tls {
|
let mut proxyd_args = vec!["-th", tcp_host, "-ut", host];
|
||||||
|
|
||||||
|
if tls {
|
||||||
let tls_key = tls_key.unwrap();
|
let tls_key = tls_key.unwrap();
|
||||||
let tls_cert = tls_cert.unwrap();
|
let tls_cert = tls_cert.unwrap();
|
||||||
println!("executing: {} -th '{}' -ut '{}' -tk '{}' -tc '{}'", proxy, tcp_host, host, tls_key, tls_cert);
|
proxyd_args.extend(["-tk", tls_key, "-tc", tls_cert].iter().cloned());
|
||||||
Command::new(proxy.clone())
|
}
|
||||||
.arg("-th")
|
|
||||||
.arg(tcp_host)
|
println!("executing: {} {}", proxy, proxyd_args.join(" "));
|
||||||
.arg("-ut")
|
let mut proxyd = Command::new(proxy.clone())
|
||||||
.arg(host)
|
.args(&proxyd_args)
|
||||||
.arg("-tk")
|
|
||||||
.arg(tls_key)
|
|
||||||
.arg("-tc")
|
|
||||||
.arg(tls_cert)
|
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("wireguard-proxy TLS server failed to launch")
|
.expect("wireguard-proxy server failed to launch");
|
||||||
} else {
|
|
||||||
println!("executing: {} -th '{}' -ut '{}'", proxy, tcp_host, host);
|
|
||||||
Command::new(proxy.clone())
|
|
||||||
.arg("-th")
|
|
||||||
.arg(tcp_host)
|
|
||||||
.arg("-ut")
|
|
||||||
.arg(host)
|
|
||||||
.spawn()
|
|
||||||
.expect("wireguard-proxy server failed to launch")
|
|
||||||
};
|
|
||||||
println!("waiting: {:?} for wireguard-proxy server to come up.....", sleep);
|
println!("waiting: {:?} for wireguard-proxy server to come up.....", sleep);
|
||||||
thread::sleep(sleep);
|
thread::sleep(sleep);
|
||||||
|
|
||||||
let mut proxy = if tls {
|
let mut proxy_args = vec!["-tt", tcp_host];
|
||||||
println!("executing: {} -tt {} --tls", proxy, tcp_host);
|
|
||||||
Command::new(proxy)
|
if tls {
|
||||||
.arg("-tt")
|
proxy_args.push("--tls");
|
||||||
.arg(tcp_host)
|
if pinnedpubkey.is_some() {
|
||||||
.arg("--tls")
|
proxy_args.push("--pinnedpubkey");
|
||||||
|
proxy_args.push(pinnedpubkey.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("executing: {} {}", proxy, proxy_args.join(" "));
|
||||||
|
let mut proxy = Command::new(proxy)
|
||||||
|
.args(proxy_args)
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("wireguard-proxy TLS client failed to launch")
|
.expect("wireguard-proxy TLS client failed to launch");
|
||||||
} else {
|
|
||||||
println!("executing: {} -tt {}", proxy, tcp_host);
|
|
||||||
Command::new(proxy)
|
|
||||||
.arg("-tt")
|
|
||||||
.arg(tcp_host)
|
|
||||||
.spawn()
|
|
||||||
.expect("wireguard-proxy client failed to launch")
|
|
||||||
};
|
|
||||||
println!("waiting: {:?} for wireguard-proxy client to come up.....", sleep);
|
println!("waiting: {:?} for wireguard-proxy client to come up.....", sleep);
|
||||||
thread::sleep(sleep);
|
thread::sleep(sleep);
|
||||||
|
|
||||||
@ -226,9 +219,17 @@ fn main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if tls {
|
if tls {
|
||||||
println!("executing: wireguard-proxy -tt {} --tls", tcp_host);
|
let hostname = tcp_host.split(":").next();
|
||||||
let hostname = tcp_host.split(":").next().expect("cannot extract hostname from tcp_host");
|
match pinnedpubkey {
|
||||||
thread::spawn(move || proxy_client.start_tls(hostname).expect("error running proxy_client"));
|
Some(pinnedpubkey) =>
|
||||||
|
println!("executing: wireguard-proxy -tt {} --tls --pinnedpubkey {}", tcp_host, pinnedpubkey),
|
||||||
|
None =>
|
||||||
|
println!("executing: wireguard-proxy -tt {} --tls", tcp_host),
|
||||||
|
}
|
||||||
|
// this is a little funky, is this the only way to do it?
|
||||||
|
let pinnedpubkey = pinnedpubkey.map(&str::to_owned);
|
||||||
|
// can use pinnedpubkey.as_deref() below when it's stabilized
|
||||||
|
thread::spawn(move || proxy_client.start_tls(hostname, pinnedpubkey.as_ref().map(String::as_str)).expect("error running proxy_client"));
|
||||||
} else {
|
} else {
|
||||||
println!("executing: wireguard-proxy -tt {}", tcp_host);
|
println!("executing: wireguard-proxy -tt {}", tcp_host);
|
||||||
thread::spawn(move || proxy_client.start().expect("error running proxy_client"));
|
thread::spawn(move || proxy_client.start().expect("error running proxy_client"));
|
||||||
|
@ -24,7 +24,13 @@ fn main() {
|
|||||||
-uh, --udp-host <ip:port> UDP host to listen on, point wireguard
|
-uh, --udp-host <ip:port> UDP host to listen on, point wireguard
|
||||||
client here, default: {}
|
client here, default: {}
|
||||||
--tls use TLS when connecting to tcp-target
|
--tls use TLS when connecting to tcp-target
|
||||||
WARNING: currently verifies nothing!
|
WARNING: authenticates/verifies nothing
|
||||||
|
without --pinnedpubkey below!!
|
||||||
|
--pinnedpubkey <sha256_hashes> Public key to verify peer against,
|
||||||
|
format is any number of base64 encoded
|
||||||
|
sha256 hashes preceded by "sha256//"
|
||||||
|
and separated by ";". Identical to curl's
|
||||||
|
--pinnedpubkey and CURLOPT_PINNEDPUBLICKEY
|
||||||
--tls-hostname send this in SNI instead of host
|
--tls-hostname send this in SNI instead of host
|
||||||
from --tcp-target, useful for avoiding
|
from --tcp-target, useful for avoiding
|
||||||
DNS lookup on connect
|
DNS lookup on connect
|
||||||
@ -79,9 +85,9 @@ fn client(tcp_target: &str, socket_timeout: u64, args: Args) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if tls {
|
if tls {
|
||||||
let hostname = args.get_option(&["--tls-hostname"]).or_else(|| tcp_target.split(":").next())
|
let hostname = args.get_option(&["--tls-hostname"]).or_else(|| tcp_target.split(":").next());
|
||||||
.expect("--tls-hostname not set and cannot extract hostname from --tcp-target");
|
let pinnedpubkey = args.get_option(&["--pinnedpubkey"]);
|
||||||
proxy_client.start_tls(hostname).expect("error running tls proxy_client");
|
proxy_client.start_tls(hostname, pinnedpubkey).expect("error running tls proxy_client");
|
||||||
} else {
|
} else {
|
||||||
proxy_client.start().expect("error running proxy_client");
|
proxy_client.start().expect("error running proxy_client");
|
||||||
}
|
}
|
||||||
|
@ -188,10 +188,10 @@ impl ProxyClient {
|
|||||||
TcpUdpPipe::new(tcp_stream, udp_socket).shuffle_after_first_udp()
|
TcpUdpPipe::new(tcp_stream, udp_socket).shuffle_after_first_udp()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_tls(&self, hostname: &str) -> Result<usize> {
|
pub fn start_tls(&self, hostname: Option<&str>, pinnedpubkey: Option<&str>) -> Result<usize> {
|
||||||
let tcp_stream = self.tcp_connect()?;
|
let tcp_stream = self.tcp_connect()?;
|
||||||
|
|
||||||
let tcp_stream = TlsStream::client(hostname, tcp_stream)?;
|
let tcp_stream = TlsStream::client(hostname, pinnedpubkey, tcp_stream)?;
|
||||||
|
|
||||||
let udp_socket = self.udp_connect()?;
|
let udp_socket = self.udp_connect()?;
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ fn err() -> Error {
|
|||||||
pub struct TlsStream;
|
pub struct TlsStream;
|
||||||
|
|
||||||
impl TlsStream {
|
impl TlsStream {
|
||||||
pub fn client(_host_name: &str, _tcp_stream: TcpStream) -> Result<TlsStream> {
|
pub fn client(_hostname: Option<&str>, _pinnedpubkey: Option<&str>, _tcp_stream: TcpStream) -> Result<TlsStream> {
|
||||||
Err(err())
|
Err(err())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,38 @@ impl TlsStream {
|
|||||||
sess: Arc::new(UnsafeCell::new(stream))
|
sess: Arc::new(UnsafeCell::new(stream))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn client(host_name: &str, tcp_stream: TcpStream) -> Result<TlsStream> {
|
pub fn client(hostname: Option<&str>, pinnedpubkey: Option<&str>, tcp_stream: TcpStream) -> Result<TlsStream> {
|
||||||
let mut connector = SslConnector::builder(SslMethod::tls())?.build().configure()?;
|
let mut connector = SslConnector::builder(SslMethod::tls())?.build().configure()?;
|
||||||
|
connector.set_use_server_name_indication(hostname.is_some());
|
||||||
connector.set_verify_hostname(false);
|
connector.set_verify_hostname(false);
|
||||||
connector.set_verify(SslVerifyMode::NONE);
|
connector.set_verify(SslVerifyMode::NONE);
|
||||||
let tcp_stream = connector.connect(host_name, tcp_stream)?;
|
if pinnedpubkey.is_some() {
|
||||||
|
let pinnedpubkey = pinnedpubkey.unwrap().to_owned();
|
||||||
|
connector.set_verify_callback(SslVerifyMode::PEER, move|_preverify_ok, x509_store_ctx| {
|
||||||
|
//println!("preverify_ok: {}", preverify_ok);
|
||||||
|
let cert = x509_store_ctx.current_cert().expect("could not get TLS cert");
|
||||||
|
let pubkey = cert.public_key().expect("could not get public key from TLS cert");
|
||||||
|
let pubkey = pubkey.public_key_to_der().expect("could not get TLS public key bytes");
|
||||||
|
//println!("pubkey.len(): {}", pubkey.len());
|
||||||
|
|
||||||
|
let mut sha256 = openssl::sha::Sha256::new();
|
||||||
|
sha256.update(&pubkey);
|
||||||
|
let pubkey = sha256.finish();
|
||||||
|
|
||||||
|
let pubkey = ["sha256//", &openssl::base64::encode_block(&pubkey)].join("");
|
||||||
|
println!("pubkey from cert: {}", pubkey);
|
||||||
|
|
||||||
|
for key in pinnedpubkey.split(";") {
|
||||||
|
if key == pubkey {
|
||||||
|
println!("SUCCESS: pubkey match found!",);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("ERROR: pubkey match not found!");
|
||||||
|
false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let tcp_stream = connector.connect(hostname.unwrap_or(""), tcp_stream)?;
|
||||||
Ok(TlsStream::new(tcp_stream))
|
Ok(TlsStream::new(tcp_stream))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
test.sh
10
test.sh
@ -60,4 +60,14 @@ run_tests || exit 1
|
|||||||
# then TLS tests
|
# then TLS tests
|
||||||
run_tests --tls --tls-key ci/cert.key --tls-cert ci/cert.pem || exit 1
|
run_tests --tls --tls-key ci/cert.key --tls-cert ci/cert.pem || exit 1
|
||||||
|
|
||||||
|
# now pubkey tests
|
||||||
|
|
||||||
|
# these should fail
|
||||||
|
udp-test -s --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4de= && exit 1
|
||||||
|
udp-test -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4de= && exit 1
|
||||||
|
|
||||||
|
# these should pass
|
||||||
|
udp-test -s --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4dE= || exit 1
|
||||||
|
udp-test -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4dE= || exit 1
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
Loading…
Reference in New Issue
Block a user