Add new test for TLS mutual authentication

This commit is contained in:
Pierre HUBERT 2022-09-02 10:57:53 +02:00
parent 019ae92605
commit 4f89bc06a0
6 changed files with 134 additions and 16 deletions

View File

@ -50,7 +50,7 @@ impl ClientConfig {
self._keys_cache = KeysCache { self._keys_cache = KeysCache {
_root_certificate_cache: load_pem_file(&self.root_certificate, "root certificate")?, _root_certificate_cache: load_pem_file(&self.root_certificate, "root certificate")?,
_tls_cert_cache: load_pem_file(&self.tls_cert, "client 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(()) Ok(())
} }

View File

@ -95,7 +95,7 @@ pub async fn run_app(mut config: ServerConfig) -> std::io::Result<()> {
let config = match args.has_tls_client_auth() { let config = match args.has_tls_client_auth() {
true => config 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(), false => config.with_no_client_auth(),
}; };

View File

@ -7,7 +7,7 @@ use rustls::{Certificate, DistinguishedNames, Error, RootCertStore};
use x509_parser::prelude::{CertificateRevocationList, FromDer, X509Certificate}; use x509_parser::prelude::{CertificateRevocationList, FromDer, X509Certificate};
use crate::base::cert_utils::parse_pem_certificates; 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; use crate::tcp_relay_server::server_config::ServerConfig;
pub struct CustomCertClientVerifier { pub struct CustomCertClientVerifier {
@ -16,21 +16,28 @@ pub struct CustomCertClientVerifier {
} }
impl CustomCertClientVerifier { impl CustomCertClientVerifier {
pub fn new(conf: Arc<ServerConfig>) -> Self { pub fn new(conf: Arc<ServerConfig>) -> std::io::Result<Self> {
// Build root certifications list // Build root certifications list
let cert_path = conf let cert_path = conf
.tls_client_auth_root_cert .tls_client_auth_root_cert
.as_deref() .as_deref()
.expect("No root certificates for client authentication provided!"); .expect("No root certificates for client authentication provided!");
let cert_file = std::fs::read(cert_path) let cert_file = std::fs::read(cert_path).map_err(|e| {
.expect("Failed to read root certificates for client authentication!"); encpasulate_error(
e,
"Failed to read root certificates for client authentication!",
)
})?;
let root_certs = parse_pem_certificates(&cert_file) let root_certs = parse_pem_certificates(&cert_file).map_err(|e| {
.expect("Failed to read root certificates for server authentication!"); encpasulate_error(
e,
"Failed to read root certificates for server authentication!",
)
})?;
if root_certs.is_empty() { if root_certs.is_empty() {
log::error!("No certificates found for client authentication!"); return Err(new_err("No certificates found for client authentication!"));
panic!();
} }
let mut store = RootCertStore::empty(); let mut store = RootCertStore::empty();
@ -42,19 +49,21 @@ impl CustomCertClientVerifier {
// Parse CRL file (if any) // Parse CRL file (if any)
let crl = if let Some(crl_file) = &conf.tls_revocation_list { 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) Some(parsed_crl.contents)
} else { } else {
None None
}; };
Self { Ok(Self {
upstream_cert_verifier: Box::new(AllowAnyAuthenticatedClient::new(store)), upstream_cert_verifier: Box::new(AllowAnyAuthenticatedClient::new(store)),
crl, crl,
} })
} }
} }

View File

@ -3,6 +3,7 @@ enum PortsAllocation {
TestsWithoutPortOpened, TestsWithoutPortOpened,
DummyTCPServer, DummyTCPServer,
ValidWithTokenAuth, ValidWithTokenAuth,
ValidWithTLSAuth,
InvalidWithTokenAuth, InvalidWithTokenAuth,
ValidWithMultipleTokenAuth, ValidWithMultipleTokenAuth,
ValidWithTokenFile, ValidWithTokenFile,
@ -34,6 +35,7 @@ mod server_invalid_tls_config_missing_key;
mod server_invalid_token_file; mod server_invalid_token_file;
mod valid_token_with_custom_increment; mod valid_token_with_custom_increment;
mod valid_with_multiple_token_auth; mod valid_with_multiple_token_auth;
mod valid_with_tls_auth;
mod valid_with_token_auth; mod valid_with_token_auth;
mod valid_with_token_auth_and_server_tls; mod valid_with_token_auth_and_server_tls;
mod valid_with_token_auth_multiple_ports; mod valid_with_token_auth_multiple_ports;

View File

@ -4,6 +4,8 @@ use crate::test::{get_port_number, PortsAllocation};
const TOKEN: &str = "mytok"; const TOKEN: &str = "mytok";
const BAD_PATH: &str = "/bad/path/to/key/file";
fn port(index: u16) -> u16 { fn port(index: u16) -> u16 {
get_port_number(PortsAllocation::TestsWithoutPortOpened, index) get_port_number(PortsAllocation::TestsWithoutPortOpened, index)
} }
@ -22,7 +24,7 @@ async fn invalid_key_path() {
listen_address: format!("127.0.0.1:{}", port(0)), listen_address: format!("127.0.0.1:{}", port(0)),
increment_ports: 1, increment_ports: 1,
tls_cert: Some(pki.localhost_crt.file_path()), 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_client_auth_root_cert: None,
tls_revocation_list: None, tls_revocation_list: None,
}) })
@ -43,7 +45,7 @@ async fn invalid_cert_path() {
upstream_server: "127.0.0.1".to_string(), upstream_server: "127.0.0.1".to_string(),
listen_address: format!("127.0.0.1:{}", port(0)), listen_address: format!("127.0.0.1:{}", port(0)),
increment_ports: 1, 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_key: Some(pki.localhost_key.file_path()),
tls_client_auth_root_cert: None, tls_client_auth_root_cert: None,
tls_revocation_list: None, tls_revocation_list: None,
@ -51,3 +53,47 @@ async fn invalid_cert_path() {
.await .await
.unwrap_err(); .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();
}

View File

@ -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;
}