@@ -40,6 +40,8 @@ mod ssh_sessions;
4040pub mod supervisor_session;
4141mod telemetry;
4242mod tls;
43+ #[ cfg( test) ]
44+ pub ( crate ) mod tls_test_utils;
4345pub mod tracing_bus;
4446mod ws_tunnel;
4547
@@ -420,14 +422,23 @@ pub async fn run_server(
420422 info ! ( "Metrics server disabled" ) ;
421423 }
422424
425+ let ( shutdown_tx, shutdown_rx) = watch:: channel ( false ) ;
426+
423427 // Build TLS acceptor when TLS is configured; otherwise serve plaintext.
424428 let tls_acceptor = if let Some ( tls) = & config. tls {
425- Some ( TlsAcceptor :: from_files (
429+ let acceptor = TlsAcceptor :: from_files (
426430 & tls. cert_path ,
427431 & tls. key_path ,
428432 tls. client_ca_path . as_deref ( ) ,
429433 tls. require_client_auth ,
430- ) ?)
434+ ) ?;
435+
436+ // Spawn file-watcher-based TLS certificate reload worker.
437+ // Watches parent directories of cert/key/CA files and atomically
438+ // reloads when changes are detected.
439+ acceptor. spawn_reload_worker ( shutdown_rx. clone ( ) ) ;
440+
441+ Some ( acceptor)
431442 } else {
432443 info ! ( "TLS disabled — accepting plaintext connections" ) ;
433444 None
@@ -621,7 +632,10 @@ fn spawn_gateway_connection(
621632 warn ! ( client = %addr, listen = %listen_addr, "Rejected plaintext HTTP on non-loopback gateway listener" ) ;
622633 }
623634 Ok ( ConnectionProtocol :: Tls | ConnectionProtocol :: Unknown ) => {
624- match acceptor. inner ( ) . accept ( stream) . await {
635+ // acceptor.acceptor() snapshots the current TLS config;
636+ // the returned acceptor owns an Arc that stays alive for
637+ // the full duration of the handshake.
638+ match acceptor. acceptor ( ) . accept ( stream) . await {
625639 Ok ( tls_stream) => {
626640 let peer_identity = multiplex:: extract_peer_identity ( & tls_stream) ;
627641 if let Err ( e) = service
@@ -914,8 +928,7 @@ mod tests {
914928 ComputeDriverKind , Config ,
915929 proto:: { HealthRequest , open_shell_client:: OpenShellClient } ,
916930 } ;
917- use rcgen:: { CertificateParams , IsCa , KeyPair } ;
918- use std:: io:: { Error , ErrorKind , Write } ;
931+ use std:: io:: { Error , ErrorKind } ;
919932 use std:: net:: SocketAddr ;
920933 use std:: sync:: Arc ;
921934 use std:: time:: Duration ;
@@ -924,41 +937,13 @@ mod tests {
924937 use tokio:: net:: { TcpListener , TcpStream } ;
925938 use tokio:: sync:: watch;
926939
927- fn install_rustls_provider ( ) {
928- let _ = rustls:: crypto:: ring:: default_provider ( ) . install_default ( ) ;
929- }
940+ use crate :: tls_test_utils:: { generate_test_certs_with_ca, install_rustls_provider} ;
930941
931942 fn test_tls_acceptor ( ) -> ( TempDir , TlsAcceptor ) {
932943 install_rustls_provider ( ) ;
933944
934- let mut ca_params =
935- CertificateParams :: new ( Vec :: < String > :: new ( ) ) . expect ( "failed to create CA params" ) ;
936- ca_params. is_ca = IsCa :: Ca ( rcgen:: BasicConstraints :: Unconstrained ) ;
937- ca_params
938- . distinguished_name
939- . push ( rcgen:: DnType :: CommonName , "test-ca" ) ;
940- let ca_key = KeyPair :: generate ( ) . expect ( "failed to generate CA key" ) ;
941- let ca_cert = ca_params
942- . self_signed ( & ca_key)
943- . expect ( "failed to sign CA cert" ) ;
944-
945- let server_params = CertificateParams :: new ( vec ! [ "localhost" . to_string( ) ] )
946- . expect ( "failed to create server params" ) ;
947- let server_key = KeyPair :: generate ( ) . expect ( "failed to generate server key" ) ;
948- let server_cert = server_params
949- . signed_by ( & server_key, & ca_cert, & ca_key)
950- . expect ( "failed to sign server cert" ) ;
951-
952945 let dir = tempdir ( ) . expect ( "failed to create tempdir" ) ;
953- let write_file = |name : & str , data : & [ u8 ] | {
954- let path = dir. path ( ) . join ( name) ;
955- std:: fs:: File :: create ( & path)
956- . and_then ( |mut file| file. write_all ( data) )
957- . expect ( "failed to write tls test file" ) ;
958- } ;
959- write_file ( "ca.pem" , ca_cert. pem ( ) . as_bytes ( ) ) ;
960- write_file ( "server-cert.pem" , server_cert. pem ( ) . as_bytes ( ) ) ;
961- write_file ( "server-key.pem" , server_key. serialize_pem ( ) . as_bytes ( ) ) ;
946+ generate_test_certs_with_ca ( dir. path ( ) ) ;
962947
963948 let acceptor = TlsAcceptor :: from_files (
964949 & dir. path ( ) . join ( "server-cert.pem" ) ,
0 commit comments