92 lines
2.5 KiB
Rust
92 lines
2.5 KiB
Rust
![]() |
use log::{info, warn};
|
||
|
use std::fs;
|
||
|
use std::io::Write;
|
||
|
use std::os::unix::fs::PermissionsExt;
|
||
|
use std::process::Stdio;
|
||
|
use tokio::net;
|
||
|
use tokio::process::Command;
|
||
|
|
||
|
use super::{retry_or_ignore, Config};
|
||
|
|
||
|
pub async fn start(cfg: &Config) {
|
||
|
retry_or_ignore(async || {
|
||
|
info!("ssh: writing authorized keys");
|
||
|
|
||
|
let ssh_dir = "/root/.ssh";
|
||
|
let authorized_keys = format!("{ssh_dir}/authorized_keys");
|
||
|
|
||
|
fs::create_dir_all(ssh_dir)?;
|
||
|
fs::set_permissions(ssh_dir, fs::Permissions::from_mode(0o700))?;
|
||
|
|
||
|
let mut ak = Vec::new();
|
||
|
|
||
|
for auth in &cfg.auths {
|
||
|
writeln!(ak, "{} {}", auth.ssh_key, auth.name)?;
|
||
|
}
|
||
|
|
||
|
fs::write(authorized_keys, ak)?;
|
||
|
Ok(())
|
||
|
})
|
||
|
.await;
|
||
|
|
||
|
retry_or_ignore(async || {
|
||
|
let mut sshd_args = Vec::new();
|
||
|
|
||
|
sshd_args.extend(["-i", "-E", "/var/log/sshd.log"]);
|
||
|
|
||
|
for key_path in cfg.ssh.keys.iter() {
|
||
|
if !fs::exists(key_path).is_ok_and(|b| b) {
|
||
|
info!("ssh: host key not found (ignored): {key_path}");
|
||
|
continue;
|
||
|
}
|
||
|
sshd_args.extend(["-h", key_path]);
|
||
|
}
|
||
|
|
||
|
let sshd_args = sshd_args.into_iter().map(String::from).collect();
|
||
|
|
||
|
// don't pre-start sshd as it should rarely be useful at this stage, use inetd-style.
|
||
|
let listen_addr = cfg.ssh.listen.clone();
|
||
|
info!("ssh: starting listener on {listen_addr}");
|
||
|
|
||
|
let listener = net::TcpListener::bind(listen_addr).await?;
|
||
|
|
||
|
tokio::spawn(handle_ssh_connections(listener, sshd_args));
|
||
|
|
||
|
Ok(())
|
||
|
})
|
||
|
.await;
|
||
|
}
|
||
|
|
||
|
async fn handle_ssh_connections(listener: net::TcpListener, sshd_args: Vec<String>) {
|
||
|
loop {
|
||
|
let (stream, remote) = match listener.accept().await {
|
||
|
Ok(v) => v,
|
||
|
Err(e) => {
|
||
|
warn!("ssh: listener stopped: {e}");
|
||
|
return;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
info!("ssh: new connection from {remote}");
|
||
|
|
||
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||
|
let fd = stream.as_raw_fd();
|
||
|
|
||
|
let mut cmd = Command::new("/usr/sbin/sshd");
|
||
|
cmd.args(&sshd_args);
|
||
|
|
||
|
cmd.stdin(unsafe { Stdio::from_raw_fd(fd) });
|
||
|
cmd.stdout(unsafe { Stdio::from_raw_fd(fd) });
|
||
|
cmd.stderr(Stdio::null());
|
||
|
|
||
|
match cmd.spawn() {
|
||
|
Ok(mut child) => {
|
||
|
tokio::spawn(async move { child.wait().await });
|
||
|
}
|
||
|
Err(e) => {
|
||
|
warn!("ssh: failed to start server: {e}");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|