@@ -4,7 +4,7 @@ use crate::api::ApiClient;
44use crate :: channel:: { ChannelForwarder , ContainerEvent , DirectTcpIpForwarder , RemoteForwardManager , ShellForwarder , X11ForwardState , channel_msg_to_event} ;
55use russh:: ChannelReadHalf ;
66use crate :: config:: Config ;
7- use anyhow:: Result ;
7+ use anyhow:: { Context , Result } ;
88use russh:: keys:: PrivateKey ;
99use russh:: server:: { self , Auth , Handle , Msg , Server , Session } ;
1010use russh:: { Channel , ChannelId , CryptoVec } ;
@@ -258,6 +258,9 @@ impl SshConnection {
258258 break ;
259259 }
260260 }
261+ // Ensure the channel is always closed when the container channel ends
262+ let _ = session_handle. eof ( client_channel_id) . await ;
263+ let _ = session_handle. close ( client_channel_id) . await ;
261264 debug ! ( "Event forwarder task ended for channel {:?}" , client_channel_id) ;
262265 } ) ;
263266 }
@@ -564,11 +567,15 @@ impl server::Handler for SshConnection {
564567 ctx. forwarder = Some ( Box :: new ( forwarder) ) ;
565568 }
566569
567- // Send welcome message if we have one
568- if let Some ( ref welcome) = self . state . welcome_message {
569- // Note: The welcome message will appear after the shell prompt
570- // because the container is now connected
571- debug ! ( "Welcome message available: {}" , welcome. len( ) ) ;
570+ // Send welcome message for interactive sessions (PTY requested)
571+ let has_pty = self . state . channels . get ( & channel_id)
572+ . map ( |ctx| ctx. pty_params . is_some ( ) )
573+ . unwrap_or ( false ) ;
574+ if has_pty {
575+ if let Some ( ref welcome) = self . state . welcome_message {
576+ let msg = format ! ( "{}\r \n " , welcome. replace( '\n' , "\r \n " ) ) ;
577+ session. data ( channel_id, CryptoVec :: from_slice ( msg. as_bytes ( ) ) ) ?;
578+ }
572579 }
573580
574581 info ! (
@@ -1092,22 +1099,29 @@ pub async fn run_server(config: Config) -> Result<()> {
10921099 std:: io:: stderr ( ) . flush ( ) . ok ( ) ;
10931100 spawn_key_refresh_task ( api_client, Arc :: clone ( & server. valid_keys ) , 60 ) ;
10941101
1095- // Load host key
1102+ // Load or generate host key (persisted across restarts)
10961103 let key_path = & config. server . host_key_path ;
10971104 let key = if key_path. exists ( ) {
10981105 eprintln ! ( "[SSH-PROXY] run_server: Loading host key from {:?}" , key_path) ;
10991106 std:: io:: stderr ( ) . flush ( ) . ok ( ) ;
1100- info ! ( "Loading host key from {:?}" , key_path) ;
1101- russh :: keys :: PrivateKey :: read_openssh_file ( key_path) ?
1107+ russh :: keys :: PrivateKey :: read_openssh_file ( key_path)
1108+ . context ( format ! ( "Failed to load host key from {:?}" , key_path) ) ?
11021109 } else {
1103- eprintln ! ( "[SSH-PROXY] run_server: Generating new host key" ) ;
1110+ eprintln ! ( "[SSH-PROXY] run_server: Generating new host key (path {:?} does not exist)" , key_path ) ;
11041111 std:: io:: stderr ( ) . flush ( ) . ok ( ) ;
1105- info ! ( "Generating new host key" ) ;
11061112 let key = russh:: keys:: PrivateKey :: random (
11071113 & mut rand:: thread_rng ( ) ,
11081114 russh:: keys:: Algorithm :: Ed25519 ,
1109- ) ?;
1110- // TODO: Save for persistence
1115+ )
1116+ . context ( "Failed to generate host key" ) ?;
1117+ if let Some ( parent) = key_path. parent ( ) {
1118+ std:: fs:: create_dir_all ( parent)
1119+ . context ( format ! ( "Failed to create host key directory {:?}" , parent) ) ?;
1120+ }
1121+ key. write_openssh_file ( key_path, russh:: keys:: ssh_key:: LineEnding :: LF )
1122+ . context ( format ! ( "Failed to save host key to {:?}" , key_path) ) ?;
1123+ eprintln ! ( "[SSH-PROXY] run_server: Saved host key to {:?}" , key_path) ;
1124+ std:: io:: stderr ( ) . flush ( ) . ok ( ) ;
11111125 key
11121126 } ;
11131127
0 commit comments