diff --git a/Cargo.lock b/Cargo.lock index 1800b9d..d3851e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -315,6 +315,8 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" name = "base" version = "0.1.0" dependencies = [ + "rustls", + "rustls-pemfile", "serde", ] @@ -1574,7 +1576,6 @@ dependencies = [ "log", "reqwest", "rustls", - "rustls-pemfile", "tokio", "tokio-tungstenite", "urlencoding", @@ -1594,7 +1595,6 @@ dependencies = [ "futures", "log", "rustls", - "rustls-pemfile", "serde", "tokio", "webpki", diff --git a/base/Cargo.toml b/base/Cargo.toml index 4882e73..63654c4 100644 --- a/base/Cargo.toml +++ b/base/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] -serde = { version = "1.0.144", features = ["derive"] } \ No newline at end of file +serde = { version = "1.0.144", features = ["derive"] } +rustls-pemfile = "1.0.1" +rustls = "0.20.6" \ No newline at end of file diff --git a/base/src/cert_utils.rs b/base/src/cert_utils.rs new file mode 100644 index 0000000..acf6efc --- /dev/null +++ b/base/src/cert_utils.rs @@ -0,0 +1,37 @@ +use std::error::Error; +use std::io::{Cursor, ErrorKind}; + +use rustls::{Certificate, PrivateKey}; +use rustls_pemfile::{read_one, Item}; + +/// Parse PEM certificates bytes into a [`rustls::Certificate`] structure +pub fn parse_pem_certificates(certs: &[u8]) -> Result, Box> { + Ok(rustls_pemfile::certs(&mut Cursor::new(certs))? + .into_iter() + .map(Certificate) + .collect()) +} + +/// Parse PEM private key bytes into a [`rustls::PrivateKey`] structure +pub fn parse_pem_private_key(privkey: &[u8]) -> Result> { + let key = match read_one(&mut Cursor::new(privkey))? { + None => { + Err(std::io::Error::new( + ErrorKind::Other, + "Failed to extract private key!", + ))?; + unreachable!() + } + Some(Item::PKCS8Key(key)) => key, + Some(Item::RSAKey(key)) => key, + _ => { + Err(std::io::Error::new( + ErrorKind::Other, + "Unsupported private key type!", + ))?; + unreachable!(); + } + }; + + Ok(PrivateKey(key)) +} diff --git a/base/src/lib.rs b/base/src/lib.rs index 6a5a62a..c09934b 100644 --- a/base/src/lib.rs +++ b/base/src/lib.rs @@ -1,7 +1,4 @@ -#[derive(serde::Serialize, serde::Deserialize, Copy, Clone, Debug)] -pub struct RelayedPort { - pub id: usize, - pub port: u16, -} +pub mod cert_utils; +mod structs; -pub type RemoteConfig = Vec; +pub use structs::{RelayedPort, RemoteConfig}; diff --git a/base/src/structs.rs b/base/src/structs.rs new file mode 100644 index 0000000..6a5a62a --- /dev/null +++ b/base/src/structs.rs @@ -0,0 +1,7 @@ +#[derive(serde::Serialize, serde::Deserialize, Copy, Clone, Debug)] +pub struct RelayedPort { + pub id: usize, + pub port: u16, +} + +pub type RemoteConfig = Vec; diff --git a/tcp_relay_client/Cargo.toml b/tcp_relay_client/Cargo.toml index e6195e7..16f5217 100644 --- a/tcp_relay_client/Cargo.toml +++ b/tcp_relay_client/Cargo.toml @@ -15,5 +15,4 @@ tokio-tungstenite = { version = "0.17.2", features = ["__rustls-tls", "rustls-tl urlencoding = "2.1.0" rustls = { version = "0.20.6" } hyper-rustls = { version = "0.23.0", features = ["rustls-native-certs"] } -rustls-pemfile = { version = "1.0.1" } bytes = "1.2.1" \ No newline at end of file diff --git a/tcp_relay_client/src/relay_client.rs b/tcp_relay_client/src/relay_client.rs index 33277f4..e77a4b3 100644 --- a/tcp_relay_client/src/relay_client.rs +++ b/tcp_relay_client/src/relay_client.rs @@ -1,14 +1,14 @@ -use std::io::Cursor; use std::sync::Arc; use futures::{SinkExt, StreamExt}; use hyper_rustls::ConfigBuilderExt; -use rustls::{Certificate, PrivateKey, RootCertStore}; -use rustls_pemfile::{read_one, Item}; +use rustls::RootCertStore; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; use tokio_tungstenite::tungstenite::Message; +use base::cert_utils; + use crate::client_config::ClientConfig; pub async fn relay_client(ws_url: String, listen_address: String, config: Arc) { @@ -46,11 +46,10 @@ async fn relay_connection(ws_url: String, socket: TcpStream, conf: Arc { log::debug!("Using custom root certificates"); let mut store = RootCertStore::empty(); - rustls_pemfile::certs(&mut Cursor::new(cert)) - .expect("Failed to parse root certificates!") - .into_iter() - .map(Certificate) - .for_each(|c| store.add(&c).expect("Failed to add certificate to chain!")); + cert_utils::parse_pem_certificates(&cert) + .unwrap() + .iter() + .for_each(|c| store.add(c).expect("Failed to add certificate to chain!")); config.with_root_certificates(store) } @@ -59,29 +58,14 @@ async fn relay_connection(ws_url: String, socket: TcpStream, conf: Arc config.with_no_client_auth(), Some((certs, key)) => { - let certs = rustls_pemfile::certs(&mut Cursor::new(certs)) - .expect("Failed to parse client certificates!") - .into_iter() - .map(Certificate) - .collect::>(); + let certs = cert_utils::parse_pem_certificates(certs) + .expect("Failed to parse client certificate!"); - let key = match read_one(&mut Cursor::new(key)) - .expect("Failed to read client private key!") - { - None => { - log::error!("Failed to extract private key!"); - panic!(); - } - Some(Item::PKCS8Key(key)) => key, - Some(Item::RSAKey(key)) => key, - _ => { - log::error!("Unsupported private key type!"); - panic!(); - } - }; + let key = cert_utils::parse_pem_private_key(key) + .expect("Failed to parse client auth private key!"); config - .with_single_cert(certs, PrivateKey(key)) + .with_single_cert(certs, key) .expect("Failed to set client certificate!") } }; diff --git a/tcp_relay_server/Cargo.toml b/tcp_relay_server/Cargo.toml index 32c9df7..82df512 100644 --- a/tcp_relay_server/Cargo.toml +++ b/tcp_relay_server/Cargo.toml @@ -16,5 +16,4 @@ serde = { version = "1.0.144", features = ["derive"] } tokio = { version = "1", features = ["full"] } futures = "0.3.24" rustls = "0.20.6" -rustls-pemfile = "1.0.1" webpki = "0.22.0" \ No newline at end of file diff --git a/tcp_relay_server/src/main.rs b/tcp_relay_server/src/main.rs index 4f448c1..4b4d63a 100644 --- a/tcp_relay_server/src/main.rs +++ b/tcp_relay_server/src/main.rs @@ -1,14 +1,10 @@ -use std::fs::File; -use std::io::BufReader; use std::sync::Arc; use actix_web::web::Data; use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer, Responder}; use clap::Parser; -use rustls::{Certificate, PrivateKey}; -use rustls_pemfile::{certs, read_one, Item}; -use base::RelayedPort; +use base::{cert_utils, RelayedPort}; use tcp_relay_server::relay_ws::relay_ws; use tcp_relay_server::server_config::ServerConfig; use tcp_relay_server::tls_cert_client_verifier::CustomCertClientVerifier; @@ -80,29 +76,16 @@ async fn main() -> std::io::Result<()> { // Load TLS configuration, if any let tls_config = if let (Some(cert), Some(key)) = (&args.tls_cert, &args.tls_key) { // Load TLS certificate & private key - let cert_file = &mut BufReader::new(File::open(cert).unwrap()); - let key_file = &mut BufReader::new(File::open(key).unwrap()); + let cert_file = std::fs::read(cert).expect("Failed to read certificate file"); + let key_file = std::fs::read(key).expect("Failed to read server private key"); // Get certificates chain - let cert_chain = certs(cert_file) - .unwrap() - .into_iter() - .map(Certificate) - .collect(); + let cert_chain = + cert_utils::parse_pem_certificates(&cert_file).expect("Failed to extract certificates"); // Get private key - let key = match read_one(key_file).expect("Failed to read private key!") { - None => { - log::error!("Failed to extract private key!"); - panic!(); - } - Some(Item::PKCS8Key(key)) => key, - Some(Item::RSAKey(key)) => key, - _ => { - log::error!("Unsupported private key type!"); - panic!(); - } - }; + let key = + cert_utils::parse_pem_private_key(&key_file).expect("Failed to extract private key!"); let config = rustls::ServerConfig::builder().with_safe_defaults(); @@ -113,7 +96,7 @@ async fn main() -> std::io::Result<()> { }; let config = config - .with_single_cert(cert_chain, PrivateKey(key)) + .with_single_cert(cert_chain, key) .expect("Failed to load TLS certificate!"); Some(config) diff --git a/tcp_relay_server/src/tls_cert_client_verifier.rs b/tcp_relay_server/src/tls_cert_client_verifier.rs index 97be55c..36f3148 100644 --- a/tcp_relay_server/src/tls_cert_client_verifier.rs +++ b/tcp_relay_server/src/tls_cert_client_verifier.rs @@ -1,11 +1,10 @@ -use std::fs::File; -use std::io::BufReader; use std::sync::Arc; use std::time::SystemTime; use rustls::server::{AllowAnyAuthenticatedClient, ClientCertVerified, ClientCertVerifier}; use rustls::{Certificate, DistinguishedNames, Error, RootCertStore}; -use rustls_pemfile::certs; + +use base::cert_utils::parse_pem_certificates; use crate::server_config::ServerConfig; @@ -19,16 +18,11 @@ impl CustomCertClientVerifier { .tls_client_auth_root_cert .as_deref() .expect("No root certificates for client authentication provided!"); - let cert_file = &mut BufReader::new( - File::open(cert_path) - .expect("Failed to read root certificates for client authentication!"), - ); + let cert_file = std::fs::read(cert_path) + .expect("Failed to read root certificates for client authentication!"); - let root_certs = certs(cert_file) - .unwrap() - .into_iter() - .map(Certificate) - .collect::>(); + let root_certs = parse_pem_certificates(&cert_file) + .expect("Failed to read root certificates for server authentication!"); if root_certs.is_empty() { log::error!("No certificates found for client authentication!");