changed how to support multiple domains and support client authentication directly by rustls using split server_config

This commit is contained in:
Jun Kurihara 2022-10-14 22:45:13 +09:00
commit 512690fce5
No known key found for this signature in database
GPG key ID: 48ADFD173ED22B03
11 changed files with 218 additions and 184 deletions

View file

@ -1,9 +1,6 @@
use super::{
proxy_client_cert::check_client_authentication,
proxy_main::{LocalExecutor, Proxy},
};
use super::proxy_main::{LocalExecutor, Proxy};
use crate::{
backend::{ServerCrypto, SniKeyIdsMap},
backend::{ServerCrypto, SniServerCryptoMap},
constants::*,
error::*,
log::*,
@ -17,7 +14,6 @@ use tokio::{
sync::watch,
time::{sleep, timeout, Duration},
};
use tokio_rustls::TlsAcceptor;
#[cfg(feature = "http3")]
use futures::StreamExt;
@ -31,7 +27,7 @@ where
async fn cert_service(&self, server_crypto_tx: watch::Sender<Option<Arc<ServerCrypto>>>) {
info!("Start cert watch service");
loop {
if let Ok(server_crypto) = self.globals.backends.generate_server_crypto_with_cert_resolver().await {
if let Ok(server_crypto) = self.globals.backends.generate_server_crypto().await {
if let Err(_e) = server_crypto_tx.send(Some(Arc::new(server_crypto))) {
error!("Failed to populate server crypto");
break;
@ -52,61 +48,61 @@ where
let tcp_listener = TcpListener::bind(&self.listening_on).await?;
info!("Start TCP proxy serving with HTTPS request for configured host names");
// let mut server_crypto: Option<Arc<ServerConfig>> = None;
let mut tls_acceptor: Option<TlsAcceptor> = None;
let mut sni_client_ca_keyid_map: Option<Arc<SniKeyIdsMap>> = None;
let mut server_crypto_map: Option<Arc<SniServerCryptoMap>> = None;
loop {
tokio::select! {
tcp_cnx = tcp_listener.accept() => {
if tls_acceptor.is_none() || tcp_cnx.is_err() || sni_client_ca_keyid_map.is_none() {
if tcp_cnx.is_err() || server_crypto_map.is_none() {
continue;
}
let (raw_stream, client_addr) = tcp_cnx.unwrap();
let acceptor = tls_acceptor.clone().unwrap();
let sni_cc_map = sni_client_ca_keyid_map.clone().unwrap();
let sc_map_inner = server_crypto_map.clone();
let server_clone = server.clone();
let self_inner = self.clone();
// spawns async handshake to avoid blocking thread by sequential handshake.
let handshake_fut = async move {
// timeout is introduced to avoid get stuck here.
let accepted = match timeout(Duration::from_secs(TLS_HANDSHAKE_TIMEOUT_SEC), acceptor.accept(raw_stream)).await {
Ok(a) => a,
Err(e) => {
return Err(RpxyError::Proxy(format!("Timeout to handshake TLS: {}", e)));
}
};
let stream = match accepted {
let acceptor = tokio_rustls::LazyConfigAcceptor::new(rustls::server::Acceptor::new().unwrap(), raw_stream).await;
if let Err(e) = acceptor {
return Err(RpxyError::Proxy(format!("Failed to handshake TLS: {}", e)));
}
let start = acceptor.unwrap();
let client_hello = start.client_hello();
let server_name = client_hello.server_name();
debug!("HTTP/2 or 1.1: SNI in ClientHello: {:?}", server_name);
let server_name = server_name.map_or_else(|| None, |v| Some(v.to_server_name_vec()));
if server_name.is_none(){
return Err(RpxyError::Proxy("No SNI is given".to_string()));
}
let server_crypto = sc_map_inner.as_ref().unwrap().get(server_name.as_ref().unwrap());
if server_crypto.is_none() {
return Err(RpxyError::Proxy(format!("No TLS serving app for {:?}", "xx")));
}
let stream = match start.into_stream(server_crypto.unwrap().clone()).await {
Ok(s) => s,
Err(e) => {
return Err(RpxyError::Proxy(format!("Failed to handshake TLS: {}", e)));
}
};
// Retrieve SNI
let (_, conn) = stream.get_ref();
let server_name = conn.sni_hostname();
debug!("HTTP/2 or 1.1: SNI in ClientHello: {:?}", server_name);
let server_name = server_name.map_or_else(|| None, |v| Some(v.to_server_name_vec()));
if server_name.is_none(){
Err(RpxyError::Proxy("No SNI is given".to_string()))
} else {
//////////////////////////////
// Check client certificate
let client_certs = conn.peer_certificates();
let client_ca_keyids_set_for_sni = sni_cc_map.get(&server_name.clone().unwrap());
// TODO: pass this value to the layer of handle_request (L7) to return 403
let client_certs_auth_result = check_client_authentication(client_certs, client_ca_keyids_set_for_sni);
//////////////////////////////
// this immediately spawns another future to actually handle stream. so it is okay to introduce timeout for handshake.
// TODO: don't want to pass copied value...
self_inner.client_serve(stream, server_clone, client_addr, server_name, Some(client_certs_auth_result));
Ok(())
}
self_inner.client_serve(stream, server_clone, client_addr, server_name);
Ok(())
};
self.globals.runtime_handle.spawn( async move {
if let Err(e) = handshake_fut.await {
error!("{}", e);
}
// timeout is introduced to avoid get stuck here.
match timeout(
Duration::from_secs(TLS_HANDSHAKE_TIMEOUT_SEC),
handshake_fut
).await {
Ok(a) => {
if let Err(e) = a {
error!("{}", e);
}
},
Err(e) => {
error!("Timeout to handshake TLS: {}", e);
}
};
});
}
_ = server_crypto_rx.changed() => {
@ -114,8 +110,7 @@ where
break;
}
let server_crypto = server_crypto_rx.borrow().clone().unwrap();
tls_acceptor = Some(TlsAcceptor::from(server_crypto.inner.clone()));
sni_client_ca_keyid_map = Some(server_crypto.server_name_client_ca_keyids_map.clone());
server_crypto_map = Some(server_crypto.inner_local_map.clone());
}
else => break
}
@ -143,11 +138,10 @@ where
let (endpoint, mut incoming) = Endpoint::server(server_config_h3, self.listening_on)?;
let mut server_crypto: Option<Arc<ServerCrypto>> = None;
let mut sni_client_ca_keyid_map: Option<Arc<SniKeyIdsMap>> = None;
loop {
tokio::select! {
new_conn = incoming.next() => {
if server_crypto.is_none() || new_conn.is_none() || sni_client_ca_keyid_map.is_none() {
if server_crypto.is_none() || new_conn.is_none() {
continue;
}
let mut conn = new_conn.unwrap();
@ -173,7 +167,7 @@ where
);
// TODO: server_nameをここで出してどんどん深く投げていくのは効率が悪い。connecting -> connectionsの後でいいのでは
// TODO: 通常のTLSと同じenumか何かにまとめたい
let fut = self.clone().connection_serve_h3(conn, new_server_name, sni_client_ca_keyid_map.clone().unwrap());
let fut = self.clone().connection_serve_h3(conn, new_server_name);
self.globals.runtime_handle.spawn(async move {
// Timeout is based on underlying quic
if let Err(e) = fut.await {
@ -187,8 +181,7 @@ where
}
server_crypto = server_crypto_rx.borrow().clone();
if server_crypto.is_some(){
endpoint.set_server_config(Some(QuicServerConfig::with_crypto(server_crypto.clone().unwrap().inner.clone())));
sni_client_ca_keyid_map = Some(server_crypto.clone().unwrap().server_name_client_ca_keyids_map.clone());
endpoint.set_server_config(Some(QuicServerConfig::with_crypto(server_crypto.clone().unwrap().inner_global_no_client_auth.clone())));
}
}
else => break