Add end2end tests #2
| @@ -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(()) | ||||
|     } | ||||
|   | ||||
| @@ -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(), | ||||
|         }; | ||||
|  | ||||
|   | ||||
| @@ -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<ServerConfig>) -> Self { | ||||
|     pub fn new(conf: Arc<ServerConfig>) -> std::io::Result<Self> { | ||||
|         // 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, | ||||
|         } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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(); | ||||
| } | ||||
|   | ||||
							
								
								
									
										61
									
								
								src/test/valid_with_tls_auth.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/test/valid_with_tls_auth.rs
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user