增加ssh

增加deploy下细分的tcp、tls、ssh
This commit is contained in:
xiuting.xu
2026-04-22 16:02:42 +08:00
parent 99250f8aa9
commit b60d579a38
37 changed files with 2951 additions and 636 deletions

97
src/rtr/server/ssh.rs Normal file
View File

@@ -0,0 +1,97 @@
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use anyhow::{Context, Result, anyhow, bail};
use russh::keys;
use russh::keys::PrivateKey;
use russh::keys::ssh_key::{self, AuthorizedKeys};
use russh::server::Config as RusshServerConfig;
use russh::{MethodKind, MethodSet};
#[derive(Debug, Clone)]
pub struct RtrSshRuntimeConfig {
pub server_config: Arc<RusshServerConfig>,
pub authorized_keys: Arc<Vec<ssh_key::PublicKey>>,
pub username: Arc<str>,
pub subsystem_name: Arc<str>,
pub password: Option<Arc<str>>,
}
pub fn load_rtr_ssh_runtime_config(
host_key_path: impl AsRef<Path>,
authorized_keys_path: impl AsRef<Path>,
username: &str,
subsystem_name: &str,
password: Option<&str>,
inactivity_timeout: Option<Duration>,
keepalive_interval: Option<Duration>,
) -> Result<RtrSshRuntimeConfig> {
if username.trim().is_empty() {
bail!("SSH username must not be empty");
}
if subsystem_name.trim().is_empty() {
bail!("SSH subsystem name must not be empty");
}
let host_key = load_host_key(host_key_path.as_ref())?;
let authorized_keys = load_authorized_keys(authorized_keys_path.as_ref())?;
let password = password.map(str::trim).filter(|value| !value.is_empty());
let mut methods = MethodSet::empty();
methods.push(MethodKind::PublicKey);
if password.is_some() {
methods.push(MethodKind::Password);
}
let server_config = RusshServerConfig {
methods,
keys: vec![host_key],
inactivity_timeout,
keepalive_interval,
keepalive_max: 3,
auth_rejection_time: Duration::from_secs(1),
auth_rejection_time_initial: Some(Duration::from_secs(0)),
..Default::default()
};
Ok(RtrSshRuntimeConfig {
server_config: Arc::new(server_config),
authorized_keys: Arc::new(authorized_keys),
username: Arc::from(username.trim()),
subsystem_name: Arc::from(subsystem_name.trim()),
password: password.map(Arc::from),
})
}
fn load_host_key(path: &Path) -> Result<PrivateKey> {
keys::load_secret_key(path, None).with_context(|| {
format!(
"failed to load SSH host private key from {} (OpenSSH private key expected)",
path.display()
)
})
}
fn load_authorized_keys(path: &Path) -> Result<Vec<ssh_key::PublicKey>> {
let entries = AuthorizedKeys::read_file(path).with_context(|| {
format!(
"failed to read SSH authorized_keys file from {}",
path.display()
)
})?;
let mut keys = Vec::with_capacity(entries.len());
for entry in entries {
keys.push(entry.public_key().clone());
}
if keys.is_empty() {
return Err(anyhow!(
"SSH authorized_keys file {} does not contain any usable keys",
path.display()
));
}
Ok(keys)
}