Skip to content

Commit e21496e

Browse files
author
Nils Bars
committed
Persist SSH host key, fix channel cleanup, and limit welcome message to PTY sessions
- Change default host key path to /data/host_key and save generated keys to disk so they survive container restarts - Always send EOF and close on the client channel when the container channel ends - Only send the welcome message for interactive PTY sessions, not exec or SFTP channels
1 parent 91554a8 commit e21496e

2 files changed

Lines changed: 28 additions & 14 deletions

File tree

ssh-reverse-proxy/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl Config {
7979
.unwrap_or_else(|_| "0.0.0.0:2222".to_string()),
8080
host_key_path: std::env::var("SSH_HOST_KEY_PATH")
8181
.map(PathBuf::from)
82-
.unwrap_or_else(|_| PathBuf::from("/keys/host_key")),
82+
.unwrap_or_else(|_| PathBuf::from("/data/host_key")),
8383
},
8484
api: ApiConfig {
8585
base_url: std::env::var("API_BASE_URL")

ssh-reverse-proxy/src/server.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::api::ApiClient;
44
use crate::channel::{ChannelForwarder, ContainerEvent, DirectTcpIpForwarder, RemoteForwardManager, ShellForwarder, X11ForwardState, channel_msg_to_event};
55
use russh::ChannelReadHalf;
66
use crate::config::Config;
7-
use anyhow::Result;
7+
use anyhow::{Context, Result};
88
use russh::keys::PrivateKey;
99
use russh::server::{self, Auth, Handle, Msg, Server, Session};
1010
use 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

Comments
 (0)