Files
markbase/markbase-core/src/ssh2_server/server.rs
Warren f90e4f496c
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
VFS/DataProvider/Config refactoring + SSH public key authentication
Phase 1-6 of refactoring plan:
- VFS abstraction (VfsBackend trait + LocalFs + OpenFlags builder)
- DataProvider trait (SqliteProvider + PgProvider, SFTPGo-compatible)
- Config refactoring (AppConfig unified sections, env overrides)
- SSH handlers (sftp/scp/rsync) migrated to VFS + DataProvider
- SSH public key authentication (Ed25519 signature verification)
- SSH stderr → CHANNEL_EXTENDED_DATA support
- Web auth uses DataProvider instead of direct SQL
- User home directory from provider (per-user isolation)
- PostgreSQL auth provider for SFTPGo compatibility
2026-06-18 23:35:18 +08:00

198 lines
6.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ssh2 Server核心实现
// 替代russh提供完整的SSH/SFTP/SCP/rsync支持
use crate::provider::sqlite::SqliteProvider;
use crate::sftp::config::SftpConfig;
use anyhow::{Result, anyhow, Context};
use log::{info, warn, error};
use ssh2::Session;
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;
use std::thread;
/// ssh2 Server主结构
pub struct Ssh2Server {
config: Arc<SftpConfig>,
}
impl Ssh2Server {
/// 创建新的ssh2服务器
pub fn new(config: Arc<SftpConfig>) -> Self {
Self { config }
}
/// 启动SSH服务器阻塞式
pub fn run(&self, port: u16) -> Result<()> {
let bind_addr = format!("127.0.0.1:{}", port);
let listener = TcpListener::bind(&bind_addr)?;
info!("ssh2 Server listening on {}", bind_addr);
// 接受客户端连接(多线程处理)
for stream in listener.incoming() {
match stream {
Ok(stream) => {
info!("New SSH connection from {}", stream.peer_addr()?);
// 每个客户端独立线程处理
let config = self.config.clone();
thread::spawn(move || {
if let Err(e) = handle_client(stream, config) {
error!("Client connection error: {}", e);
}
});
}
Err(e) => {
warn!("Failed to accept connection: {}", e);
}
}
}
Ok(())
}
}
/// 处理单个客户端连接
fn handle_client(stream: TcpStream, config: Arc<SftpConfig>) -> Result<()> {
info!("Handling client connection");
// 1. 创建ssh2 session
let mut session = Session::new()?;
session.set_tcp_stream(stream.try_clone()?);
session.handshake()?;
info!("SSH handshake completed");
// 2. 认证
let user = authenticate_client(&session, &config)?;
info!("Client authenticated: {}", user);
// 3. 处理channel请求
handle_channels(&session, &user, &config)?;
// 4. 关闭session
session.disconnect(None, "Server shutdown", None)?;
info!("Client disconnected: {}", user);
Ok(())
}
/// 认证客户端
fn authenticate_client(session: &Session, config: &Arc<SftpConfig>) -> Result<String> {
// 等待认证请求
loop {
// 检查认证状态
if session.authenticated() {
info!("Client already authenticated");
break;
}
// 获取认证方法
let auth_methods = session.auth_methods("username");
if auth_methods.is_none() {
warn!("No auth methods available");
return Err(anyhow!("No auth methods"));
}
// 简化处理尝试password认证
// 实际实现需要从客户端读取username和password
// ⚠️ 这里是placeholder实际需要
// 1. 从SSH协议读取username
// 2. 从SSH协议读取password
// 3. 使用SftpAuth验证
// 暂时返回默认用户(测试用)
let user = "warren";
let password = "demo123";
// 使用SqliteProvider验证复用现有认证系统
let provider = SqliteProvider::new(&config.auth_db_path)
.context("Failed to init SqliteProvider for ssh2_server")?;
if provider.check_password(user, password)? {
info!("Password auth successful for user: {}", user);
session.userauth_password(user, password)?;
return Ok(user.to_string());
} else {
warn!("Password auth failed for user: {}", user);
return Err(anyhow!("Auth failed"));
}
}
Err(anyhow!("Auth timeout"))
}
/// 处理channel请求
fn handle_channels(session: &Session, user: &str, config: &Arc<SftpConfig>) -> Result<()> {
info!("Handling channels for user: {}", user);
loop {
// 等待channel请求
// ⚠️ ssh2库的channel API需要进一步研究
// 简化实现创建session channel
let channel = session.channel_session()?;
info!("Session channel created");
// 等待exec请求
channel.wait()?;
// 读取exec命令
let command = read_exec_command(&channel)?;
info!("Exec command: {}", command);
// 根据命令类型路由
if command.starts_with("sftp") {
info!("SFTP subsystem requested");
handle_sftp_subsystem(&channel, user, config)?;
} else if command.starts_with("scp") {
info!("SCP command requested");
handle_scp_command(&channel, user, &command, config)?;
} else if command.starts_with("rsync") {
info!("rsync command requested");
handle_rsync_command(&channel, user, &command, config)?;
} else {
warn!("Unknown command: {}", command);
}
channel.close()?;
channel.wait_close()?;
// 简化处理:一次连接只处理一个命令
break;
}
Ok(())
}
/// 读取exec命令placeholder
fn read_exec_command(channel: &ssh2::Channel) -> Result<String> {
// ⚠️ ssh2::Channel API需要进一步研究
// 如何读取客户端发送的exec命令
// 暂时返回测试命令
Ok("sftp")
}
/// 处理SFTP subsystemplaceholder
fn handle_sftp_subsystem(channel: &ssh2::Channel, user: &str, config: &Arc<SftpConfig>) -> Result<()> {
info!("SFTP subsystem handlerplaceholder");
// Phase 2将实现14个SFTP操作
Ok(())
}
/// 处理SCP命令placeholder
fn handle_scp_command(channel: &ssh2::Channel, user: &str, command: &str, config: &Arc<SftpConfig>) -> Result<()> {
info!("SCP handlerplaceholder");
// Phase 3将实现完整SCP
Ok(())
}
/// 处理rsync命令placeholder
fn handle_rsync_command(channel: &ssh2::Channel, user: &str, command: &str, config: &Arc<SftpConfig>) -> Result<()> {
info!("rsync handlerplaceholder");
// Phase 4将实现完整rsync
Ok(())
}