feat(tls): Add flag to not send SNI during tls handshake

This commit is contained in:
Σrebe - Romain GERARD 2024-03-16 22:12:31 +01:00
parent 1c393afe4f
commit 3129fe3219
No known key found for this signature in database
GPG key ID: 7A42B4B97E0332F4
2 changed files with 42 additions and 14 deletions

View file

@ -138,6 +138,11 @@ struct Client {
#[arg(long, value_name = "DOMAIN_NAME", value_parser = parse_sni_override, verbatim_doc_comment)] #[arg(long, value_name = "DOMAIN_NAME", value_parser = parse_sni_override, verbatim_doc_comment)]
tls_sni_override: Option<DnsName>, tls_sni_override: Option<DnsName>,
/// Disable sending SNI during TLS handshake
/// Warning: Most reverse proxies rely on it
#[arg(long, verbatim_doc_comment)]
tls_sni_disable: bool,
/// Enable TLS certificate verification. /// Enable TLS certificate verification.
/// Disabled by default. The client will happily connect to any server with self signed certificate. /// Disabled by default. The client will happily connect to any server with self signed certificate.
#[arg(long, verbatim_doc_comment)] #[arg(long, verbatim_doc_comment)]
@ -557,6 +562,7 @@ fn parse_server_url(arg: &str) -> Result<Url, io::Error> {
#[derive(Clone)] #[derive(Clone)]
pub struct TlsClientConfig { pub struct TlsClientConfig {
pub tls_sni_disabled: bool,
pub tls_sni_override: Option<DnsName>, pub tls_sni_override: Option<DnsName>,
pub tls_verify_certificate: bool, pub tls_verify_certificate: bool,
pub tls_connector: TlsConnector, pub tls_connector: TlsConnector,
@ -680,16 +686,26 @@ async fn main() {
{ {
TransportScheme::Ws | TransportScheme::Http => None, TransportScheme::Ws | TransportScheme::Http => None,
TransportScheme::Wss => Some(TlsClientConfig { TransportScheme::Wss => Some(TlsClientConfig {
tls_connector: tls::tls_connector(args.tls_verify_certificate, Some(vec![b"http/1.1".to_vec()])) tls_connector: tls::tls_connector(
.expect("Cannot create tls connector"), args.tls_verify_certificate,
Some(vec![b"http/1.1".to_vec()]),
!args.tls_sni_disable,
)
.expect("Cannot create tls connector"),
tls_sni_override: args.tls_sni_override, tls_sni_override: args.tls_sni_override,
tls_verify_certificate: args.tls_verify_certificate, tls_verify_certificate: args.tls_verify_certificate,
tls_sni_disabled: args.tls_sni_disable,
}), }),
TransportScheme::Https => Some(TlsClientConfig { TransportScheme::Https => Some(TlsClientConfig {
tls_connector: tls::tls_connector(args.tls_verify_certificate, Some(vec![b"h2".to_vec()])) tls_connector: tls::tls_connector(
.expect("Cannot create tls connector"), args.tls_verify_certificate,
Some(vec![b"h2".to_vec()]),
!args.tls_sni_disable,
)
.expect("Cannot create tls connector"),
tls_sni_override: args.tls_sni_override, tls_sni_override: args.tls_sni_override,
tls_verify_certificate: args.tls_verify_certificate, tls_verify_certificate: args.tls_verify_certificate,
tls_sni_disabled: args.tls_sni_disable,
}), }),
}; };

View file

@ -66,6 +66,7 @@ pub fn load_private_key_from_file(path: &Path) -> anyhow::Result<PrivateKey> {
pub fn tls_connector( pub fn tls_connector(
tls_verify_certificate: bool, tls_verify_certificate: bool,
alpn_protocols: Option<Vec<Vec<u8>>>, alpn_protocols: Option<Vec<Vec<u8>>>,
enable_sni: bool,
) -> anyhow::Result<TlsConnector> { ) -> anyhow::Result<TlsConnector> {
let mut root_store = rustls::RootCertStore::empty(); let mut root_store = rustls::RootCertStore::empty();
@ -74,7 +75,7 @@ pub fn tls_connector(
for cert in certs { for cert in certs {
if let Err(err) = root_store.add(&Certificate(cert.as_ref().to_vec())) { if let Err(err) = root_store.add(&Certificate(cert.as_ref().to_vec())) {
warn!("cannot load a system certificate: {:?}", err); warn!("cannot load a system certificate: {:?}", err);
continue continue;
} }
} }
@ -83,6 +84,8 @@ pub fn tls_connector(
.with_root_certificates(root_store) .with_root_certificates(root_store)
.with_no_client_auth(); .with_no_client_auth();
config.enable_sni = enable_sni;
// To bypass certificate verification // To bypass certificate verification
if !tls_verify_certificate { if !tls_verify_certificate {
config.dangerous().set_certificate_verifier(Arc::new(NullVerifier)); config.dangerous().set_certificate_verifier(Arc::new(NullVerifier));
@ -110,19 +113,28 @@ pub fn tls_acceptor(tls_cfg: &TlsServerConfig, alpn_protocols: Option<Vec<Vec<u8
pub async fn connect(client_cfg: &WsClientConfig, tcp_stream: TcpStream) -> anyhow::Result<TlsStream<TcpStream>> { pub async fn connect(client_cfg: &WsClientConfig, tcp_stream: TcpStream) -> anyhow::Result<TlsStream<TcpStream>> {
let sni = client_cfg.tls_server_name(); let sni = client_cfg.tls_server_name();
info!( let (tls_connector, sni_disabled) = match &client_cfg.remote_addr {
"Doing TLS handshake using sni {sni:?} with the server {}:{}", TransportAddr::Wss { tls, .. } => (&tls.tls_connector, tls.tls_sni_disabled),
client_cfg.remote_addr.host(), TransportAddr::Https { tls, .. } => (&tls.tls_connector, tls.tls_sni_disabled),
client_cfg.remote_addr.port()
);
let tls_connector = match &client_cfg.remote_addr {
TransportAddr::Wss { tls, .. } => &tls.tls_connector,
TransportAddr::Https { tls, .. } => &tls.tls_connector,
TransportAddr::Http { .. } | TransportAddr::Ws { .. } => { TransportAddr::Http { .. } | TransportAddr::Ws { .. } => {
return Err(anyhow!("Transport does not support TLS: {}", client_cfg.remote_addr.scheme())) return Err(anyhow!("Transport does not support TLS: {}", client_cfg.remote_addr.scheme()))
} }
}; };
if sni_disabled {
info!(
"Doing TLS handshake without SNI with the server {}:{}",
client_cfg.remote_addr.host(),
client_cfg.remote_addr.port()
);
} else {
info!(
"Doing TLS handshake using SNI {sni:?} with the server {}:{}",
client_cfg.remote_addr.host(),
client_cfg.remote_addr.port()
);
}
let tls_stream = tls_connector.connect(sni, tcp_stream).await.with_context(|| { let tls_stream = tls_connector.connect(sni, tcp_stream).await.with_context(|| {
format!( format!(
"failed to do TLS handshake with the server {}:{}", "failed to do TLS handshake with the server {}:{}",