2023-10-01 15:16:23 +00:00
|
|
|
use crate::{TlsClientConfig, TlsServerConfig, WsClientConfig};
|
|
|
|
use anyhow::{anyhow, Context};
|
|
|
|
use std::fs::File;
|
|
|
|
|
2023-12-04 17:21:55 +00:00
|
|
|
use log::warn;
|
2023-10-01 15:16:23 +00:00
|
|
|
use std::io::BufReader;
|
|
|
|
use std::path::Path;
|
|
|
|
use std::sync::Arc;
|
|
|
|
use std::time::SystemTime;
|
|
|
|
use tokio::net::TcpStream;
|
|
|
|
use tokio_rustls::client::TlsStream;
|
|
|
|
use tokio_rustls::rustls::client::{ServerCertVerified, ServerCertVerifier};
|
|
|
|
|
|
|
|
use tokio_rustls::rustls::{Certificate, ClientConfig, PrivateKey, ServerName};
|
|
|
|
use tokio_rustls::{rustls, TlsAcceptor, TlsConnector};
|
|
|
|
use tracing::info;
|
|
|
|
|
2023-10-27 17:51:42 +00:00
|
|
|
struct NullVerifier;
|
2023-10-01 15:16:23 +00:00
|
|
|
impl ServerCertVerifier for NullVerifier {
|
|
|
|
fn verify_server_cert(
|
|
|
|
&self,
|
|
|
|
_end_entity: &Certificate,
|
|
|
|
_intermediates: &[Certificate],
|
|
|
|
_server_name: &ServerName,
|
|
|
|
_scts: &mut dyn Iterator<Item = &[u8]>,
|
|
|
|
_ocsp_response: &[u8],
|
|
|
|
_now: SystemTime,
|
|
|
|
) -> Result<ServerCertVerified, rustls::Error> {
|
|
|
|
Ok(ServerCertVerified::assertion())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_certificates_from_pem(path: &Path) -> anyhow::Result<Vec<Certificate>> {
|
2023-12-04 17:21:55 +00:00
|
|
|
info!("Loading tls certificate from {:?}", path);
|
|
|
|
|
2023-10-01 15:16:23 +00:00
|
|
|
let file = File::open(path)?;
|
|
|
|
let mut reader = BufReader::new(file);
|
2023-12-04 17:21:55 +00:00
|
|
|
let certs = rustls_pemfile::certs(&mut reader);
|
2023-10-01 15:16:23 +00:00
|
|
|
|
2023-12-04 17:21:55 +00:00
|
|
|
Ok(certs
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|cert| match cert {
|
|
|
|
Ok(cert) => Some(Certificate(cert.to_vec())),
|
|
|
|
Err(err) => {
|
|
|
|
warn!("Error while parsing tls certificate: {:?}", err);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect())
|
2023-10-01 15:16:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_private_key_from_file(path: &Path) -> anyhow::Result<PrivateKey> {
|
2023-12-04 17:21:55 +00:00
|
|
|
info!("Loading tls private key from {:?}", path);
|
|
|
|
|
2023-10-01 15:16:23 +00:00
|
|
|
let file = File::open(path)?;
|
|
|
|
let mut reader = BufReader::new(file);
|
|
|
|
|
2023-12-04 17:21:55 +00:00
|
|
|
let Some(private_key) = rustls_pemfile::private_key(&mut reader)? else {
|
|
|
|
return Err(anyhow!("No private key found in {path:?}"));
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(PrivateKey(private_key.secret_der().to_vec()))
|
2023-10-01 15:16:23 +00:00
|
|
|
}
|
|
|
|
|
2023-10-30 07:13:38 +00:00
|
|
|
fn tls_connector(tls_cfg: &TlsClientConfig, alpn_protocols: Option<Vec<Vec<u8>>>) -> anyhow::Result<TlsConnector> {
|
2023-10-01 15:16:23 +00:00
|
|
|
let mut root_store = rustls::RootCertStore::empty();
|
|
|
|
|
|
|
|
// Load system certificates and add them to the root store
|
2023-10-30 07:13:38 +00:00
|
|
|
let certs = rustls_native_certs::load_native_certs().with_context(|| "Cannot load system certificates")?;
|
2023-10-01 15:16:23 +00:00
|
|
|
for cert in certs {
|
2023-12-04 17:21:55 +00:00
|
|
|
root_store.add(&Certificate(cert.as_ref().to_vec()))?;
|
2023-10-01 15:16:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut config = ClientConfig::builder()
|
|
|
|
.with_safe_defaults()
|
|
|
|
.with_root_certificates(root_store)
|
|
|
|
.with_no_client_auth();
|
|
|
|
|
|
|
|
// To bypass certificate verification
|
|
|
|
if !tls_cfg.tls_verify_certificate {
|
2023-10-30 07:13:38 +00:00
|
|
|
config.dangerous().set_certificate_verifier(Arc::new(NullVerifier));
|
2023-10-01 15:16:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(alpn_protocols) = alpn_protocols {
|
|
|
|
config.alpn_protocols = alpn_protocols;
|
|
|
|
}
|
|
|
|
let tls_connector = TlsConnector::from(Arc::new(config));
|
|
|
|
Ok(tls_connector)
|
|
|
|
}
|
|
|
|
|
2023-10-30 07:13:38 +00:00
|
|
|
pub fn tls_acceptor(tls_cfg: &TlsServerConfig, alpn_protocols: Option<Vec<Vec<u8>>>) -> anyhow::Result<TlsAcceptor> {
|
2023-10-01 15:16:23 +00:00
|
|
|
let mut config = rustls::ServerConfig::builder()
|
|
|
|
.with_safe_defaults()
|
|
|
|
.with_no_client_auth()
|
|
|
|
.with_single_cert(tls_cfg.tls_certificate.clone(), tls_cfg.tls_key.clone())
|
|
|
|
.with_context(|| "invalid tls certificate or private key")?;
|
|
|
|
|
|
|
|
if let Some(alpn_protocols) = alpn_protocols {
|
|
|
|
config.alpn_protocols = alpn_protocols;
|
|
|
|
}
|
|
|
|
Ok(TlsAcceptor::from(Arc::new(config)))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn connect(
|
2023-10-23 17:11:12 +00:00
|
|
|
client_cfg: &WsClientConfig,
|
2023-10-01 15:16:23 +00:00
|
|
|
tls_cfg: &TlsClientConfig,
|
|
|
|
tcp_stream: TcpStream,
|
|
|
|
) -> anyhow::Result<TlsStream<TcpStream>> {
|
2023-10-23 17:11:12 +00:00
|
|
|
let sni = client_cfg.tls_server_name();
|
2023-10-01 15:16:23 +00:00
|
|
|
info!(
|
|
|
|
"Doing TLS handshake using sni {sni:?} with the server {}:{}",
|
2023-10-23 17:11:12 +00:00
|
|
|
client_cfg.remote_addr.0, client_cfg.remote_addr.1
|
2023-10-01 15:16:23 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let tls_connector = tls_connector(tls_cfg, Some(vec![b"http/1.1".to_vec()]))?;
|
|
|
|
let tls_stream = tls_connector
|
|
|
|
.connect(sni, tcp_stream)
|
|
|
|
.await
|
2023-10-30 07:13:38 +00:00
|
|
|
.with_context(|| format!("failed to do TLS handshake with the server {:?}", client_cfg.remote_addr))?;
|
2023-10-01 15:16:23 +00:00
|
|
|
|
|
|
|
Ok(tls_stream)
|
|
|
|
}
|