用 Rust 写一个不依赖 OpenSSL 的命令行 SSH 工作台:r-shell 实战

发布时间:2026/7/1 9:55:28

用 Rust 写一个不依赖 OpenSSL 的命令行 SSH 工作台:r-shell 实战 运维和后端开发里有一类麻烦,不是「连不上服务器」这种大事,而是一堆很小但天天重复的操作:连上去敲一条命令看看状态,把一个安装包传上去,把一份日志拉下来,瞄一眼 CPU 和内存满没满,然后在生产、测试、数据库、日志机之间来回切。每一件单独看都不算事。可一旦机器多起来,系统自带的ssh/scp/sftp各管一摊、IP 和端口靠脑子记、参数每次重敲——这种「碎」就成了真正的时间黑洞。r-shell 想把这摊碎活收进一个命令行二进制里:connections管连接、exec跑命令、shell开交互终端、upload/download走 SFTP、stats打系统快照。它用 Rust 写内核,SSH 栈是纯 Rust 的russh,不依赖 OpenSSL / libssh,所以 macOS / Linux / Windows 都能出一个零依赖、拷过去就能跑的单文件。这篇不讲 AI,只讲一件事:怎么把多服务器的日常操作,收敛成一个顺手、可脚本化、带安全默认值的命令行工具。文中每条命令和输出都对应仓库里能跑的实现。一、它能干什么一张表先把全貌摆出来:子命令作用connections增删改查已保存的主机(存在本地workspace.json)exec在远程主机上执行单条命令并打印输出shell打开完整交互式 PTY(vim / htop / less 都能用)ls列出远程目录upload/download通过 SFTP 单文件上传 / 下载stats一次性的 CPU / 内存 / 磁盘 / 网络快照(Linux 与 Windows 通吃)mcp启动本地 MCP 服务器(可选,本文不展开)同一个二进制在三大平台都能跑。exec/shell/upload/download适用于任意 POSIX 主机;ls面向 Linux(依赖 GNUls);stats则做了 OS 适配,Linux 读/proc、Windows 走 PowerShell CIM,都能出数。二、和系统自带的 ssh / scp 有什么区别常有人问:ssh、scp不是够用了吗?够用,但 r-shell 解决的是「够用之上的碎」。它的定位看这张表最清楚:场景传统 ssh / scpr-shell连接信息管理手写~/.ssh/config或脑子记 IPconnections子命令统一增删改查执行远程命令ssh userhost cmdr-shell exec -c prod -- cmd文件传输scp单独拼路径upload/download复用同一份连接定义看负载手动top/df/free各看一遍stats一条命令聚合成一屏输出面向人眼表格之外都给--json,对脚本友好安全默认取决于系统与个人习惯内置主机密钥 TOFU、凭据文件0600分发依赖系统自带纯 Rust 单二进制,零系统依赖,拷了就跑一句话:r-shell 不是要取代ssh,而是把「连接管理 常用操作 安全默认 可脚本化」打包成一个顺手的工具。三、技术选型:为什么是 Rust russh,且不依赖 OpenSSL三个关键决策:语言用 Rust。SSH、SFTP、系统采样这些活儿,既要正确又要能扛并发,Rust 的所有权和类型系统能把「连接状态、凭据边界」这类不变量直接编译进类型里,少一类运行期惊吓。SSH 栈用纯 Rust 的russh,而不是去绑 OpenSSL / libssh。这一条对「分发」太关键了:一旦动态依赖 OpenSSL,你在 Windows 上就得操心运行时和版本地狱;改用russh后,整套握手、认证、PTY、known_hosts都在 Rust 里,编出来就是一个零系统依赖的单二进制,拷到一台干净机器上直接能跑。异步用 tokio。SSH / SFTP 是典型 I/O 密集场景,全程异步;命令行参数与子命令树交给clap的 derive 宏声明,--help自动生成;交互终端的 raw mode 和无回显密码输入交给crossterm。依赖很克制,核心就这几样(core的Cargo.toml):russh # 纯 Rust SSH 协议:握手 / 认证 / 通道 / PTY russh-keys # 密钥与 known_hosts 处理 russh-sftp # SFTP 子系统:文件传输 / 列目录 tokio # 异步运行时 sysinfo # 本机资源采样CLI 这一层再叠上clap(命令行)和crossterm(终端控制)。全程没有 OpenSSL。四、架构:CLI 与桌面端共享同一套内核r-shell 最初其实是个图形界面应用。正因为业务逻辑和界面一开始就解耦,后来重构出命令行版时,几乎零成本复用了全部后端——CLI 和桌面 GUI共用同一个core:同样的连接管理、同样的 SSH 实现、同样的系统采样。你在 GUI 里存的连接,CLI 也认;反过来也成立。┌───────────────────────────┐ 桌面 GUI ───┤ flutter_rust_bridge (FFI) ├──┐ └───────────────────────────┘ │ ▼ r-shell CLI ───────────────────────▶ core(共享内核) ├── native_backend SSH/SFTP、连接管理器 ├── monitor /proc 与 PowerShell CIM 采样 ├── model SavedConnection / 脱敏 sanitized() └── storage workspace.json(0600)CLI 入口本身很薄,clap解析完就把活儿丢给core:cli/ └── src/main.rs # 参数解析、子命令分发、输出格式化(表格 / JSON) core/ └── src/ # native_backend / monitor / model / storage / mcp —— CLI 与 GUI 共用五、命令实战(附真实输出)下面按子命令给出可直接复制的示例,输出格式都对应main.rs里真实的打印逻辑。1. connections —— 像通讯录一样管连接# 新增一个用公钥认证的连接 r-shell connections add \ --name prod-web --host 203.0.113.10 --username deploy \ --auth publickey --key-path ~/.ssh/id_ed25519 \ --folder Work --description 生产环境 Web 服务器 # 列表(表格) r-shell connections list表格输出:ID NAME HOST AUTH FOLDER ssh-1781247286839 prod-web deploy203.0.113.10:22 publickey Work ssh-1781247290114 test-db root10.0.0.5:22 password Staging加--json给脚本用,而且是脱敏的——只告诉你有没有密码 / 私钥,绝不回吐明文:[ { connection_id: ssh-1781247286839, name: prod-web, host: 203.0.113.10, username: deploy, port: 22, auth_method: publickey, folder: Work, tags: [], description: 生产环境 Web 服务器, status: Disconnected, has_password: false, has_private_key_path: true } ]改字段、删连接也都在一条命令里:r-shell connections update ssh-1781247290114 --port 2222 --folder Staging r-shell connections remove ssh-17812472901142. exec —— 跑一条命令就走--之后的内容原样发给远端。既能用已存连接(-c),也能临时给--host:r-shell exec -c prod-web -- uname -a r-shell exec -c prod-web -- ls -la /var/www df -h r-shell exec --host 203.0.113.10 --user deploy -- systemctl status nginx「连接 → 执行 → 自动断开」一气呵成,特别适合写进巡检 / 部署脚本里批量跑。3. shell —— 完整交互式终端raw 模式下的完整 PTY,vim、htop、less这类需要伪终端的程序都能正常用。会话卡住时按Ctrl-]强制退出本地循环:r-shell shell -c prod-web4. ls —— 列远程目录r-shell ls -c prod-web /var/log输出列依次是:类型(DIR/FILE/LNK)、权限、大小、修改时间、名称:DIR drwxr-xr-x 4096 2026-06-20 14:31 nginx FILE -rw-r--r-- 10240 2026-06-21 09:02 syslog FILE -rw-r--r-- 204800 2026-06-21 08:55 dpkg.log LNK lrwxrwxrwx 24 2026-06-18 02:00 alternatives.log加--json同样可以拿去喂脚本。5. upload / download —— SFTP 传文件走 SFTP,不依赖远端有没有scp,只要 SSH 通就能传:# 本地 - 远程 r-shell upload -c prod-web ./app.tar.gz /tmp/app.tar.gz # Uploaded ./app.tar.gz - /tmp/app.tar.gz (12.4 MB) # 远程 - 本地 r-shell download -c prod-web /tmp/remote.log ./local.log # Downloaded /tmp/remote.log - ./local.log (843.2 KB)6. stats —— 一屏看完系统资源连续采样两次,算出 CPU 占用和网络速率,一条命令出一份快照。Linux 上读/proc:r-shell stats -c prod-webOS: Linux 6.1.0 Uptime: 12d 4h 31m CPU: 7.4% (8 cores, load 0.42) Memory: 61.2% (4.9/7.8 GB) Disk: 40.0% (3.8/9.5 GB) Network: down 1.5 KB/s up 320 B/s同一条命令打到 Windows 主机也能出数——内核里换成一段 PowerShell CIM 采集,自动处理好编码:OS: Microsoft Windows 11 专业工作站版 10.0.29591 Uptime: 7d 6h 12m CPU: 9.0% (8 cores, load 0.00) Memory: 52.0% (8.3/15.9 GB) Disk: 52.4% (56.3/118.1 GB) Network: down 18.6 KB/s up 2.4 KB/s六、一次性命令的生命周期:连接 → 执行 → 断开exec/ls/upload/download/stats都遵循同一套「用完即走」的模型,这也是它适合脚本化的原因:解析目标(-c 已存连接 / --host 临时主机;缺密码则安全提示输入) ↓ 建立 SSH 连接(握手 → 主机密钥 TOFU 校验 → 密码或公钥认证) ↓ 执行子命令对应的操作 ↓ 自动关闭连接,干净退出每条命令都自包含、互不残留状态,写进 CI / 巡检脚本里行为可预测。(需要在一条会话里连发多条命令、保持工作目录的场景,则交给shell的交互式 PTY。)七、面向运维的安全默认值SSH 工具直连服务器凭据,安全不能是「可选项」。r-shell 把几条底线做成了默认行为:主机密钥 TOFU(首次信任)。按~/.ssh/known_hosts校验:首次连接记录主机密钥,之后必须一致;一旦对不上(典型的中间人,或主机被换),直接中止连接而不是默默连上去。只有显式传--insecure才跳过——那是留给一次性测试机的,不是日常姿势。凭据只进不出。密码、私钥路径、口令绝不会被打印;任何连接列表都走sanitized(),只暴露has_password/has_private_key_path这种布尔值。密码输入不回显。终端进 raw 模式逐字节读取,屏幕上不显形。配置文件仅属主可读。workspace.json在 Unix 上以0600创建、目录0700,不让同机器上别的用户cat到你的跳板信息。已保存连接的落盘位置:系统路径macOS~/Library/Application Support/r-shell/workspace.jsonLinux~/.local/share/r-shell/workspace.jsonWindows%LOCALAPPDATA%\r-shell\workspace.json八、安装源码编译只需要 Rust 和 Cargo(rustup.rs),不需要 OpenSSL 或 libssh:git clone https://github.com/MageGojo/r-shell-cli.git cd r-shell-cli cargo build --release --manifest-path cli/Cargo.toml sudo install -m 0755 cli/target/release/r-shell /usr/local/bin/r-shell r-shell --version不想装,直接跑也行:cargo run --manifest-path cli/Cargo.toml -- exec -c prod-web -- uptime预编译包懒得编译的话,Releases 里有现成的:平台文件macOS(Apple Silicon)r-shell-macos-apple-silicon.dmgmacOS(Intel)r-shell-macos-intel.dmgWindows x64r-shell-windows-x64-installer.exemacOS 上把二进制拷进PATH后,记得清一下 Gatekeeper 隔离标记:xattr -dr com.apple.quarantine /usr/local/bin/r-shell;Windows 安装器会自动把r-shell加进PATH。因为是没买付费证书的开源软件,首次打开时按系统提示放行即可。九、顺带一提:它还有个桌面端抛开命令行,同一套core还撑起了一个 Flutter 写界面的桌面应用:命令块终端(每条命令和它的输出各成一块,带退出码、耗时)、实时监控走势图、SFTP 拖拽、多标签管多台。CLI 适合脚本和远程会话,GUI 适合盯监控和翻历史,两者数据互通。另外,r-shell mcp还能起一个只绑127.0.0.1的本地 MCP 服务器,让支持 MCP 的工具借一条常驻 SSH 会话干活——那是另一个话题,本文不展开。十、适合谁运维 / DevOps:统一管理一堆服务器,批量exec巡检、upload/download发版拉日志,把远程操作写进流水线。后端开发:快速连测试机执行命令、传安装包,不用每次拼一长串ssh/scp。想要零依赖单文件的人:不想在目标机上装一堆东西,拷一个二进制就能用。Rust 学习者:clap子命令树、russh的 SSH 编程、tokio异步 I/O、known_hosts校验、业务与界面解耦——都是能直接照着读的活例子。如果你只管一两台机器,它是个顺手的 SSH 工具;如果你要管很多台并写自动化,它的连接管理和可脚本化会更值。十一、开源与下载r-shell 已开源(MIT),由极数本源(apizero.cn)· MageGojo出品:源码:github.com/MageGojo/r-shell-cli下载:Releases觉得有用就去仓库点个 star。日常要在多台服务器之间跑命令、传文件、看负载的,可以拿它当一个轻量、安全、可脚本化的 SSH 工作台;在学 Rust 命令行开发的,也很适合作为源码阅读和二次开发的参考。⚠️免责声明:本项目仅供学习与合法运维使用。SSH / 命令执行 / 文件读写均会对远程主机产生实际影响,使用者应自行确认操作对象与命令内容,并对重要数据做好备份。请仅在你拥有合法授权的主机上使用;使用本工具产生的一切后果由使用者自行承担。

相关新闻