From 4f89bc06a0cd6df625c55b96aba8b50465d2ccf2 Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Fri, 2 Sep 2022 10:57:53 +0200 Subject: [PATCH] Add new test for TLS mutual authentication --- src/tcp_relay_client/client_config.rs | 2 +- src/tcp_relay_server/mod.rs | 2 +- .../tls_cert_client_verifier.rs | 33 ++++++---- src/test/mod.rs | 2 + ...server_invalid_tls_config_invalid_paths.rs | 50 ++++++++++++++- src/test/valid_with_tls_auth.rs | 61 +++++++++++++++++++ 6 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 src/test/valid_with_tls_auth.rs diff --git a/src/tcp_relay_client/client_config.rs b/src/tcp_relay_client/client_config.rs index 3a34913..fd47190 100644 --- a/src/tcp_relay_client/client_config.rs +++ b/src/tcp_relay_client/client_config.rs @@ -50,7 +50,7 @@ impl ClientConfig { self._keys_cache = KeysCache { _root_certificate_cache: load_pem_file(&self.root_certificate, "root certificate")?, _tls_cert_cache: load_pem_file(&self.tls_cert, "client certificate")?, - _tls_key_cache: load_pem_file(&self.tls_cert, "client key")?, + _tls_key_cache: load_pem_file(&self.tls_key, "client key")?, }; Ok(()) } diff --git a/src/tcp_relay_server/mod.rs b/src/tcp_relay_server/mod.rs index 39234dc..7df9054 100644 --- a/src/tcp_relay_server/mod.rs +++ b/src/tcp_relay_server/mod.rs @@ -95,7 +95,7 @@ pub async fn run_app(mut config: ServerConfig) -> std::io::Result<()> { let config = match args.has_tls_client_auth() { true => config - .with_client_cert_verifier(Arc::new(CustomCertClientVerifier::new(args.clone()))), + .with_client_cert_verifier(Arc::new(CustomCertClientVerifier::new(args.clone())?)), false => config.with_no_client_auth(), }; diff --git a/src/tcp_relay_server/tls_cert_client_verifier.rs b/src/tcp_relay_server/tls_cert_client_verifier.rs index 806b9b7..07996fe 100644 --- a/src/tcp_relay_server/tls_cert_client_verifier.rs +++ b/src/tcp_relay_server/tls_cert_client_verifier.rs @@ -7,7 +7,7 @@ use rustls::{Certificate, DistinguishedNames, Error, RootCertStore}; use x509_parser::prelude::{CertificateRevocationList, FromDer, X509Certificate}; use crate::base::cert_utils::parse_pem_certificates; - +use crate::base::err_utils::{encpasulate_error, new_err}; use crate::tcp_relay_server::server_config::ServerConfig; pub struct CustomCertClientVerifier { @@ -16,21 +16,28 @@ pub struct CustomCertClientVerifier { } impl CustomCertClientVerifier { - pub fn new(conf: Arc) -> Self { + pub fn new(conf: Arc) -> std::io::Result { // Build root certifications list let cert_path = conf .tls_client_auth_root_cert .as_deref() .expect("No root certificates for client authentication provided!"); - let cert_file = std::fs::read(cert_path) - .expect("Failed to read root certificates for client authentication!"); + let cert_file = std::fs::read(cert_path).map_err(|e| { + encpasulate_error( + e, + "Failed to read root certificates for client authentication!", + ) + })?; - let root_certs = parse_pem_certificates(&cert_file) - .expect("Failed to read root certificates for server authentication!"); + let root_certs = parse_pem_certificates(&cert_file).map_err(|e| { + encpasulate_error( + e, + "Failed to read root certificates for server authentication!", + ) + })?; if root_certs.is_empty() { - log::error!("No certificates found for client authentication!"); - panic!(); + return Err(new_err("No certificates found for client authentication!")); } let mut store = RootCertStore::empty(); @@ -42,19 +49,21 @@ impl CustomCertClientVerifier { // Parse CRL file (if any) let crl = if let Some(crl_file) = &conf.tls_revocation_list { - let crl_file = std::fs::read(crl_file).expect("Failed to open / read CRL file!"); + let crl_file = std::fs::read(crl_file) + .map_err(|e| encpasulate_error(e, "Failed to open / read CRL file!"))?; - let parsed_crl = pem::parse(crl_file).expect("Failed to decode CRL file!"); + let parsed_crl = pem::parse(crl_file) + .map_err(|e| encpasulate_error(e, "Failed to decode CRL file!"))?; Some(parsed_crl.contents) } else { None }; - Self { + Ok(Self { upstream_cert_verifier: Box::new(AllowAnyAuthenticatedClient::new(store)), crl, - } + }) } } diff --git a/src/test/mod.rs b/src/test/mod.rs index 15551b8..9975407 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -3,6 +3,7 @@ enum PortsAllocation { TestsWithoutPortOpened, DummyTCPServer, ValidWithTokenAuth, + ValidWithTLSAuth, InvalidWithTokenAuth, ValidWithMultipleTokenAuth, ValidWithTokenFile, @@ -34,6 +35,7 @@ mod server_invalid_tls_config_missing_key; mod server_invalid_token_file; mod valid_token_with_custom_increment; mod valid_with_multiple_token_auth; +mod valid_with_tls_auth; mod valid_with_token_auth; mod valid_with_token_auth_and_server_tls; mod valid_with_token_auth_multiple_ports; diff --git a/src/test/server_invalid_tls_config_invalid_paths.rs b/src/test/server_invalid_tls_config_invalid_paths.rs index fe9922b..1977cf5 100644 --- a/src/test/server_invalid_tls_config_invalid_paths.rs +++ b/src/test/server_invalid_tls_config_invalid_paths.rs @@ -4,6 +4,8 @@ use crate::test::{get_port_number, PortsAllocation}; const TOKEN: &str = "mytok"; +const BAD_PATH: &str = "/bad/path/to/key/file"; + fn port(index: u16) -> u16 { get_port_number(PortsAllocation::TestsWithoutPortOpened, index) } @@ -22,7 +24,7 @@ async fn invalid_key_path() { listen_address: format!("127.0.0.1:{}", port(0)), increment_ports: 1, tls_cert: Some(pki.localhost_crt.file_path()), - tls_key: Some("/bad/path/to/key/file".to_string()), + tls_key: Some(BAD_PATH.to_string()), tls_client_auth_root_cert: None, tls_revocation_list: None, }) @@ -43,7 +45,7 @@ async fn invalid_cert_path() { upstream_server: "127.0.0.1".to_string(), listen_address: format!("127.0.0.1:{}", port(0)), increment_ports: 1, - tls_cert: Some("/bad/path/to/key/file".to_string()), + tls_cert: Some(BAD_PATH.to_string()), tls_key: Some(pki.localhost_key.file_path()), tls_client_auth_root_cert: None, tls_revocation_list: None, @@ -51,3 +53,47 @@ async fn invalid_cert_path() { .await .unwrap_err(); } + +#[tokio::test] +async fn invalid_client_root_cert_path() { + let _ = env_logger::builder().is_test(true).try_init(); + + let pki = Pki::load(); + + crate::tcp_relay_server::run_app(ServerConfig { + tokens: vec![TOKEN.to_string()], + tokens_file: None, + ports: vec![port(1)], + upstream_server: "127.0.0.1".to_string(), + listen_address: format!("127.0.0.1:{}", port(0)), + increment_ports: 1, + tls_cert: Some(pki.localhost_crt.file_path()), + tls_key: Some(pki.localhost_key.file_path()), + tls_client_auth_root_cert: Some(BAD_PATH.to_string()), + tls_revocation_list: None, + }) + .await + .unwrap_err(); +} + +#[tokio::test] +async fn invalid_client_root_crl_path() { + let _ = env_logger::builder().is_test(true).try_init(); + + let pki = Pki::load(); + + crate::tcp_relay_server::run_app(ServerConfig { + tokens: vec![TOKEN.to_string()], + tokens_file: None, + ports: vec![port(1)], + upstream_server: "127.0.0.1".to_string(), + listen_address: format!("127.0.0.1:{}", port(0)), + increment_ports: 1, + tls_cert: Some(pki.localhost_crt.file_path()), + tls_key: Some(pki.localhost_key.file_path()), + tls_client_auth_root_cert: Some(pki.root_ca_crl.file_path()), + tls_revocation_list: Some(BAD_PATH.to_string()), + }) + .await + .unwrap_err(); +} diff --git a/src/test/valid_with_tls_auth.rs b/src/test/valid_with_tls_auth.rs new file mode 100644 index 0000000..ac2686b --- /dev/null +++ b/src/test/valid_with_tls_auth.rs @@ -0,0 +1,61 @@ +use tokio::task; + +use crate::tcp_relay_client::client_config::ClientConfig; +use crate::tcp_relay_server::server_config::ServerConfig; +use crate::test::dummy_tcp_sockets::{ + dummy_tcp_client_square_root_requests, wait_for_port, DummyTCPServer, +}; +use crate::test::pki::Pki; +use crate::test::{get_port_number, PortsAllocation, LOCALHOST_IP}; + +fn port(index: u16) -> u16 { + get_port_number(PortsAllocation::ValidWithTLSAuth, index) +} + +#[tokio::test] +async fn test() { + let _ = env_logger::builder().is_test(true).try_init(); + + // Start internal service + let local_server = DummyTCPServer::start(port(1)).await; + tokio::spawn(async move { + local_server.loop_conn_square_operations().await; + }); + + let pki = Pki::load(); + + let local_set = task::LocalSet::new(); + local_set + .run_until(async move { + wait_for_port(port(1)).await; + + // Start server relay + task::spawn_local(crate::tcp_relay_server::run_app(ServerConfig { + tokens: vec![], + tokens_file: None, + ports: vec![port(1)], + upstream_server: "127.0.0.1".to_string(), + listen_address: format!("127.0.0.1:{}", port(0)), + increment_ports: 1, + tls_cert: Some(pki.localhost_crt.file_path()), + tls_key: Some(pki.localhost_key.file_path()), + tls_client_auth_root_cert: Some(pki.root_ca_crt.file_path()), + tls_revocation_list: Some(pki.root_ca_crl.file_path()), + })); + wait_for_port(port(0)).await; + + // Start client relay + task::spawn(crate::tcp_relay_client::run_app(ClientConfig { + relay_url: format!("https://localhost:{}", port(0)), + listen_address: LOCALHOST_IP.to_string(), + root_certificate: Some(pki.root_ca_crt.file_path()), + tls_cert: Some(pki.valid_client_crt.file_path()), + tls_key: Some(pki.valid_client_key.file_path()), + ..Default::default() + })); + wait_for_port(port(2)).await; + + dummy_tcp_client_square_root_requests(port(2), 10).await; + }) + .await; +}