@@ -371,6 +371,15 @@ fn is_benign_tls_handshake_failure(error: &std::io::Error) -> bool {
371371 )
372372}
373373
374+ fn log_metrics_tls_handshake_rejection ( error : & std:: io:: Error , client : SocketAddr ) {
375+ warn ! (
376+ event = "metrics_tls_handshake_rejected" ,
377+ error = %error,
378+ client = %client,
379+ "Rejected metrics TLS handshake; verify that the client uses HTTPS and, when client authentication is enabled, a certificate trusted by the metrics client CA"
380+ ) ;
381+ }
382+
374383fn is_benign_connection_close ( error : & ( dyn std:: error:: Error + ' static ) ) -> bool {
375384 openshell_core:: transport_errors:: is_expected_transport_close_error ( error)
376385}
@@ -1104,12 +1113,7 @@ async fn serve_tls_metrics_listener(
11041113 debug ! ( error = %error, client = %addr, "Metrics TLS handshake closed early" ) ;
11051114 }
11061115 Ok ( Err ( error) ) => {
1107- warn ! (
1108- event = "metrics_tls_handshake_rejected" ,
1109- error = %error,
1110- client = %addr,
1111- "Rejected metrics TLS handshake; verify that the client uses HTTPS and, when client authentication is enabled, a certificate trusted by the metrics client CA"
1112- ) ;
1116+ log_metrics_tls_handshake_rejection ( & error, addr) ;
11131117 }
11141118 Err ( _) => {
11151119 warn ! ( client = %addr, "Metrics TLS handshake timed out" ) ;
@@ -1970,8 +1974,9 @@ mod tests {
19701974 BoundGatewayListener , ConfiguredComputeDriver , ConnectionProtocol , ExtensionKind ,
19711975 MultiplexService , ServerState , TlsAcceptor , allow_plaintext_service_http,
19721976 bind_gateway_listener, classify_initial_bytes, configured_compute_driver,
1973- extension_token_ttl, is_benign_tls_handshake_failure, mint_gateway_extension_credential,
1974- serve_gateway_listener, serve_tls_metrics_listener, validate_peer_endpoint_scheme,
1977+ extension_token_ttl, is_benign_tls_handshake_failure, log_metrics_tls_handshake_rejection,
1978+ mint_gateway_extension_credential, serve_gateway_listener, serve_tls_metrics_listener,
1979+ validate_peer_endpoint_scheme,
19751980 } ;
19761981 use axum:: { Router , routing:: get} ;
19771982 use openshell_core:: {
@@ -2418,6 +2423,59 @@ mod tests {
24182423 let _ = tokio:: time:: timeout ( Duration :: from_secs ( 2 ) , handle) . await ;
24192424 }
24202425
2426+ #[ derive( Clone ) ]
2427+ struct TraceBuffer ( Arc < Mutex < Vec < u8 > > > ) ;
2428+
2429+ impl std:: io:: Write for TraceBuffer {
2430+ fn write ( & mut self , bytes : & [ u8 ] ) -> std:: io:: Result < usize > {
2431+ self . 0
2432+ . lock ( )
2433+ . expect ( "trace buffer lock" )
2434+ . extend_from_slice ( bytes) ;
2435+ Ok ( bytes. len ( ) )
2436+ }
2437+
2438+ fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
2439+ Ok ( ( ) )
2440+ }
2441+ }
2442+
2443+ #[ test]
2444+ fn metrics_tls_handshake_rejection_logs_actionable_context ( ) {
2445+ use tracing_subscriber:: layer:: SubscriberExt as _;
2446+
2447+ let buffer = Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ;
2448+ let writer = TraceBuffer ( buffer. clone ( ) ) ;
2449+ let subscriber = tracing_subscriber:: registry ( ) . with (
2450+ tracing_subscriber:: fmt:: layer ( )
2451+ . with_writer ( move || writer. clone ( ) )
2452+ . with_ansi ( false ) ,
2453+ ) ;
2454+ let _traced = crate :: otel_tracing:: test_exporter:: install_scoped ( subscriber) ;
2455+
2456+ log_metrics_tls_handshake_rejection (
2457+ & Error :: new ( ErrorKind :: InvalidData , "untrusted client certificate" ) ,
2458+ "192.0.2.10:443" . parse ( ) . expect ( "valid test client address" ) ,
2459+ ) ;
2460+
2461+ let output = String :: from_utf8 ( buffer. lock ( ) . expect ( "trace buffer lock" ) . clone ( ) )
2462+ . expect ( "tracing output is UTF-8" ) ;
2463+ assert ! ( output. contains( "WARN" ) , "log output: {output}" ) ;
2464+ assert ! (
2465+ output. contains( "metrics_tls_handshake_rejected" ) ,
2466+ "log output: {output}"
2467+ ) ;
2468+ assert ! (
2469+ output. contains( "untrusted client certificate" ) ,
2470+ "log output: {output}"
2471+ ) ;
2472+ assert ! ( output. contains( "192.0.2.10:443" ) , "log output: {output}" ) ;
2473+ assert ! (
2474+ output. contains( "Rejected metrics TLS handshake" ) ,
2475+ "log output: {output}"
2476+ ) ;
2477+ }
2478+
24212479 #[ tokio:: test]
24222480 async fn tls_metrics_listener_serves_https_and_rejects_plaintext ( ) {
24232481 let listener = TcpListener :: bind ( "127.0.0.1:0" )
0 commit comments