用 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(依赖 GNU ls);stats 则做了 OS 适配,Linux 读 /proc、Windows 走 PowerShell CIM,都能出数。

二、和系统自带的 ssh / scp 有什么区别

常有人问:sshscp 不是够用了吗?够用,但 r-shell 解决的是「够用之上的碎」。它的定位看这张表最清楚:

场景 传统 ssh / scp r-shell
连接信息管理 手写 ~/.ssh/config 或脑子记 IP connections 子命令统一增删改查
执行远程命令 ssh user@host "cmd" r-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

依赖很克制,核心就这几样(coreCargo.toml):

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 也认;反过来也成立。

text 复制代码
            ┌───────────────────────────┐
桌面 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:

text 复制代码
cli/
└── src/main.rs   # 参数解析、子命令分发、输出格式化(表格 / JSON)
core/
└── src/          # native_backend / monitor / model / storage / mcp ------ CLI 与 GUI 共用

五、命令实战(附真实输出)

下面按子命令给出可直接复制的示例,输出格式都对应 main.rs 里真实的打印逻辑。

1. connections ------ 像通讯录一样管连接

bash 复制代码
# 新增一个用公钥认证的连接
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

表格输出:

text 复制代码
ID                    NAME                    HOST                    AUTH        FOLDER
ssh-1781247286839     prod-web                deploy@203.0.113.10:22  publickey   Work
ssh-1781247290114     test-db                 root@10.0.0.5:22        password    Staging

--json 给脚本用,而且是脱敏的------只告诉你有没有密码 / 私钥,绝不回吐明文:

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
  }
]

改字段、删连接也都在一条命令里:

bash 复制代码
r-shell connections update ssh-1781247290114 --port 2222 --folder Staging
r-shell connections remove ssh-1781247290114

2. exec ------ 跑一条命令就走

-- 之后的内容原样发给远端。既能用已存连接(-c),也能临时给 --host:

bash 复制代码
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,vimhtopless 这类需要伪终端的程序都能正常用。会话卡住时按 Ctrl-] 强制退出本地循环:

bash 复制代码
r-shell shell -c prod-web

4. ls ------ 列远程目录

bash 复制代码
r-shell ls -c prod-web /var/log

输出列依次是:类型(DIR / FILE / LNK)、权限、大小、修改时间、名称:

text 复制代码
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 通就能传:

bash 复制代码
# 本地 -> 远程
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:

bash 复制代码
r-shell stats -c prod-web
text 复制代码
OS:      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 采集,自动处理好编码:

text 复制代码
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 都遵循同一套「用完即走」的模型,这也是它适合脚本化的原因:

text 复制代码
解析目标(-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.json
Linux ~/.local/share/r-shell/workspace.json
Windows %LOCALAPPDATA%\r-shell\workspace.json

八、安装

源码编译

只需要 Rust 和 Cargo(rustup.rs),不需要 OpenSSL 或 libssh:

bash 复制代码
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

不想装,直接跑也行:

bash 复制代码
cargo run --manifest-path cli/Cargo.toml -- exec -c prod-web -- uptime

预编译包

懒得编译的话,Releases 里有现成的:

平台 文件
macOS(Apple Silicon) r-shell-macos-apple-silicon.dmg
macOS(Intel) r-shell-macos-intel.dmg
Windows x64 r-shell-windows-x64-installer.exe

macOS 上把二进制拷进 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 出品:

觉得有用就去仓库点个 star。日常要在多台服务器之间跑命令、传文件、看负载的,可以拿它当一个轻量、安全、可脚本化的 SSH 工作台;在学 Rust 命令行开发的,也很适合作为源码阅读和二次开发的参考。

⚠️ 免责声明:本项目仅供学习与合法运维使用。SSH / 命令执行 / 文件读写均会对远程主机产生实际影响,使用者应自行确认操作对象与命令内容,并对重要数据做好备份。请仅在你拥有合法授权的主机上使用;使用本工具产生的一切后果由使用者自行承担。