如何在Rust中实现SSH动态端口转发,自动化跨服务器访问?
实现Rust版SSH动态端口转发(SOCKS5)
用ssh2-rs实现(基于libssh2)
ssh2-rs其实支持动态端口转发(对应ssh -D的SOCKS5代理功能),只是需要手动处理本地监听和连接转发逻辑。
步骤:
- 添加依赖到
Cargo.toml:
[dependencies] ssh2 = "0.10" tokio = { version = "1.0", features = ["full"] }
(用tokio做异步IO,方便处理多客户端连接)
- 核心实现代码:
use std::net::{TcpListener, TcpStream}; use ssh2::Session; use tokio::task; async fn handle_client(mut local_stream: TcpStream, session: Session) { // 读取SOCKS5握手请求 let mut handshake_buf = [0u8; 1024]; let n = local_stream.read(&mut handshake_buf).unwrap(); // 回复SOCKS5确认(无认证) local_stream.write_all(&[0x05, 0x00]).unwrap(); // 读取目标地址请求 let n = local_stream.read(&mut handshake_buf).unwrap(); let addr_type = handshake_buf[3]; let (target_host, target_port) = match addr_type { 0x01 => { // IPv4 let host = format!( "{}.{}.{}.{}", handshake_buf[4], handshake_buf[5], handshake_buf[6], handshake_buf[7] ); let port = ((handshake_buf[8] as u16) << 8) | handshake_buf[9] as u16; (host, port) } 0x03 => { // 域名 let len = handshake_buf[4] as usize; let host = String::from_utf8_lossy(&handshake_buf[5..5+len]).to_string(); let port = ((handshake_buf[5+len] as u16) << 8) | handshake_buf[5+len+1] as u16; (host, port) } _ => panic!("Unsupported address type"), }; // 通过SSH会话建立到目标服务器的连接 let mut remote_stream = session.channel_direct_tcpip(&target_host, target_port).unwrap(); // 回复SOCKS5连接成功 local_stream.write_all(&[0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]).unwrap(); // 双向转发数据 task::spawn(async move { let _ = std::io::copy(&mut local_stream, &mut remote_stream); remote_stream.close().unwrap(); }); let _ = std::io::copy(&mut remote_stream, &mut local_stream); local_stream.shutdown(std::net::Shutdown::Both).unwrap(); } #[tokio::main] async fn main() { // 建立SSH连接到中转服务器B let tcp_stream = TcpStream::connect("computer_b_ip:22").unwrap(); let mut sess = Session::new().unwrap(); sess.set_tcp_stream(tcp_stream); sess.handshake().unwrap(); sess.userauth_password("myUser", "myPassword").unwrap(); // 也可以替换为密钥认证逻辑 assert!(sess.authenticated()); // 监听本地端口1080 let listener = TcpListener::bind("127.0.0.1:1080").unwrap(); println!("SOCKS5 proxy listening on 127.0.0.1:1080"); // 处理每个客户端连接 for stream in listener.incoming() { match stream { Ok(stream) => { let sess_clone = sess.clone(); task::spawn(async move { handle_client(stream, sess_clone).await; }); } Err(e) => println!("Failed to accept connection: {}", e), } } }
说明:
- 这段代码实现了完整的SOCKS5代理逻辑,完全对应
ssh -D 1080 myUser@ComputerB的功能 - 支持IPv4和域名类型的目标地址,处理双向数据转发
- 用tokio实现异步处理多客户端连接,避免单连接阻塞整个代理
用thrussh实现(纯Rust)
thrussh是纯Rust编写的SSH库,没有直接封装SOCKS5逻辑,但可以通过底层API手动实现:
- 添加依赖:
[dependencies] thrussh = "0.43" thrussh-keys = "0.22" tokio = { version = "1.0", features = ["full"] }
- 核心思路:
- 建立thrussh的SSH客户端连接(支持密码或密钥认证)
- 监听本地1080端口,复用上面的SOCKS5握手逻辑
- 针对每个目标地址,调用thrussh的
channel_open_direct_tcpip方法建立远程连接 - 启动异步任务双向转发本地流与远程SSH通道的数据
构建单二进制文件
要生成可直接分享的单二进制文件,在Cargo.toml中添加以下发布配置:
[profile.release] strip = true opt-level = 3 lto = true codegen-units = 1
执行cargo build --release后,可执行文件会生成在target/release目录下。Windows平台需确保编译时静态链接依赖库,建议使用GCC工具链编译。
内容的提问来源于stack exchange,提问作者lolozen
相关产品推荐
相关产品推荐

