Files
wgcloak/tests/integration_transports.rs
T

230 lines
7.9 KiB
Rust

use std::{process::Command, sync::Arc, time::Duration};
use bytes::Bytes;
use rustls::RootCertStore;
use tempfile::tempdir;
use tokio::time::timeout;
use wgcloak::{
config::Config,
protocol, transport,
transport::{
AdmissionController, TlsTcpConnector, TlsTcpServer, TransportConnector, TransportError,
},
};
const SECRET: &str = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
fn free_port() -> u16 {
std::net::TcpListener::bind("127.0.0.1:0")
.unwrap()
.local_addr()
.unwrap()
.port()
}
#[tokio::test]
async fn tls_tcp_authenticates_round_trips_and_classifies_failures() {
if Command::new("openssl").arg("version").output().is_err() {
return;
}
let dir = tempdir().unwrap();
let cert = dir.path().join("cert.pem");
let key = dir.path().join("key.pem");
let generated = Command::new("openssl")
.args([
"req",
"-x509",
"-newkey",
"rsa:2048",
"-nodes",
"-subj",
"/CN=localhost",
"-addext",
"subjectAltName=DNS:localhost",
"-addext",
"basicConstraints=critical,CA:FALSE",
"-days",
"1",
"-keyout",
])
.arg(&key)
.args(["-out"])
.arg(&cert)
.output()
.unwrap();
assert!(
generated.status.success(),
"openssl failed: {:?}",
generated
);
let port = free_port();
let server_config = Config::from_str(&format!(
"[server]\nlisten = \"127.0.0.1\"\n[backend]\ntarget = \"127.0.0.1:51820\"\n[transports.direct_udp]\nenabled = false\n[transports.quic]\nenabled = false\n[transports.http3]\nenabled = false\n[transports.tls_tcp]\nenabled = true\nport = {port}\n[limits]\nhandshake_timeout_ms = 500\n[tls]\ncertificate = \"{}\"\nprivate_key = \"{}\"\n[[auth.clients]]\nid = \"test\"\nsecret = \"{SECRET}\"\n",
cert.display(),
key.display()
))
.unwrap()
.into_server()
.unwrap();
let client_config = Config::from_str(&format!(
"[client]\nserver = \"localhost\"\nid = \"test\"\nsecret = \"{SECRET}\"\n[transport]\nmode = \"tls_tcp\"\n[transport.tls_tcp]\nport = {port}\nkeepalive_secs = 1\n[network]\nmax_datagram_size = 1200\n"
))
.unwrap()
.into_client()
.unwrap();
let mismatch_config = client_config.clone();
let certs = transport::tls_tcp::load_certificates(&cert).unwrap();
let mut roots = RootCertStore::empty();
roots.add(certs[0].clone()).unwrap();
let client_tls = (*transport::tls_tcp::client_tls_config(roots)).clone();
let admission = AdmissionController::new(&server_config.limits);
let server = Arc::new(
TlsTcpServer::bind_with_admission(&server_config, admission.clone())
.await
.unwrap(),
);
let slow_server = server.clone();
let slow_handshake = tokio::spawn(async move { slow_server.accept_session().await });
let slow_peer = tokio::net::TcpStream::connect(server.local_addr().unwrap())
.await
.unwrap();
tokio::time::sleep(Duration::from_millis(50)).await;
let accept = {
let server = server.clone();
tokio::spawn(async move { server.accept_session().await })
};
let client = TlsTcpConnector::with_client_config(client_config, client_tls.clone());
let client_session = client.connect().await.unwrap();
let server_session = accept.await.unwrap().unwrap();
assert_eq!(
client_session.authenticated_keepalive_interval(),
Some(Duration::from_secs(1))
);
assert_eq!(client_session.rtt(), None);
assert_eq!(
client_session.remote_addr().unwrap().ip(),
"127.0.0.1".parse::<std::net::IpAddr>().unwrap()
);
assert_eq!(admission.active_sessions(), 1);
assert_eq!(admission.pending_handshakes(), 1);
client_session.send_keepalive().await.unwrap();
client_session
.send_datagram(Bytes::from_static(b"tls-opaque"))
.await
.unwrap();
assert_eq!(
timeout(Duration::from_secs(2), server_session.recv_datagram())
.await
.unwrap()
.unwrap(),
Bytes::from_static(b"tls-opaque")
);
client_session.close().await.unwrap();
server_session.close().await.unwrap();
assert!(matches!(
timeout(Duration::from_secs(1), slow_handshake)
.await
.unwrap()
.unwrap(),
Err(TransportError::HandshakeTimeout)
));
drop(slow_peer);
assert_eq!(admission.active_sessions(), 0);
assert_eq!(admission.pending_handshakes(), 0);
let mut wrong_credentials = mismatch_config.clone();
wrong_credentials.client.secret.secret =
Some("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB".to_owned());
let server_accept = {
let server = server.clone();
tokio::spawn(async move { server.accept_session().await })
};
let wrong_client = TlsTcpConnector::with_client_config(wrong_credentials, client_tls.clone());
assert!(matches!(
timeout(Duration::from_secs(2), wrong_client.connect())
.await
.unwrap(),
Err(TransportError::AuthenticationRejected(
protocol::RejectReason::AuthenticationFailed
))
));
assert!(matches!(
timeout(Duration::from_secs(2), server_accept)
.await
.unwrap()
.unwrap(),
Err(TransportError::AuthenticationRejected(
protocol::RejectReason::AuthenticationFailed
))
));
let server_accept = {
let server = server.clone();
tokio::spawn(async move { server.accept_session().await })
};
let mut mismatch_config = mismatch_config;
mismatch_config.client.server = "127.0.0.1".to_owned();
let mismatch_client = TlsTcpConnector::with_client_config(mismatch_config, client_tls);
assert!(matches!(
timeout(Duration::from_secs(2), mismatch_client.connect())
.await
.unwrap(),
Err(TransportError::Io(_))
));
assert!(timeout(Duration::from_secs(2), server_accept)
.await
.unwrap()
.unwrap()
.is_err());
}
#[tokio::test]
async fn tls_tcp_ipv6_loopback_listener_accepts_bracketed_configuration() {
if Command::new("openssl").arg("version").output().is_err() {
return;
}
let probe = match tokio::net::TcpListener::bind("[::1]:0").await {
Ok(listener) => listener,
Err(_) => return,
};
let port = probe.local_addr().unwrap().port();
drop(probe);
let dir = tempdir().unwrap();
let cert = dir.path().join("cert.pem");
let key = dir.path().join("key.pem");
let generated = Command::new("openssl")
.args([
"req",
"-x509",
"-newkey",
"rsa:2048",
"-nodes",
"-subj",
"/CN=localhost",
"-addext",
"subjectAltName=IP:::1",
"-days",
"1",
"-keyout",
])
.arg(&key)
.args(["-out"])
.arg(&cert)
.output()
.unwrap();
assert!(generated.status.success(), "openssl failed: {generated:?}");
let config = Config::from_str(&format!(
"[server]\nlisten = \"::1\"\n[backend]\ntarget = \"127.0.0.1:51820\"\n[transports.direct_udp]\nenabled = false\n[transports.quic]\nenabled = false\n[transports.http3]\nenabled = false\n[transports.tls_tcp]\nenabled = true\nport = {port}\n[tls]\ncertificate = \"{}\"\nprivate_key = \"{}\"\n[[auth.clients]]\nid = \"test\"\nsecret = \"{SECRET}\"\n",
cert.display(),
key.display()
))
.unwrap()
.into_server()
.unwrap();
let server =
TlsTcpServer::bind_with_admission(&config, AdmissionController::new(&config.limits))
.await
.unwrap();
assert!(server.local_addr().unwrap().ip().is_ipv6());
}