1638 字
8 分钟
Hop:用 Rust 做一台轻量但不草率的 SSH 跳板机

跳板机经常出现在这样一个尴尬的位置:个人服务器和小团队确实需要统一入口,但完整堡垒机带来的数据库、缓存、消息队列和部署维护成本,又明显超过了实际需求。

Hop 想解决的就是这个区间。它是一个 Rust 编写的 SSH 跳板机,以单二进制、SQLite、公钥认证和原生 SSH 工作流为基础;需要管理时提供 Web 页面,不需要时,它看起来仍然只是一台可以直接 ssh 的服务器。

本文基于当前 0.1.5 版本,记录这个项目最重要的几条设计取舍。

轻量不是删掉安全能力#

Hop 的“轻”主要来自更少的运行时部件:

  • hop-server 一个二进制同时提供 SSH、TUI、Admin Web 和本机管理命令;
  • SQLite 随程序内嵌,不要求额外部署 Postgres;
  • 管理页面由服务端提供,日常使用无需安装专属客户端;
  • 发布构建启用 LTO、单 codegen unit 和 panic = "abort",目标是一个容易分发的产物。

但减少组件不代表省略关键边界。Hop 的入口只接受白名单中的 SSH 公钥,托管凭证使用 ChaCha20-Poly1305 加密,Admin 密码使用 Argon2 哈希,管理页面默认只监听 127.0.0.1:8080

远程打开 Admin Web 时,更推荐通过现有 SSH 通道转发:

ssh -N -L 8080:127.0.0.1:8080 root@hop-host

这让默认部署不会平白增加一个暴露在公网的管理入口。需要反向代理或管理网络时可以显式调整,但风险是部署者主动选择的,而不是项目替用户默默选择的。

让 SSH 保持 SSH#

Hop 没有发明新的连接协议。用户仍然使用 OpenSSH、SFTP、SCP 和 ProxyJump

# 打开 TUI,模糊搜索资产
ssh -p 2222 hop-host
# 直接进入指定资产
ssh -p 2222 web-prod-01@hop-host
# 把 Hop 当作 ProxyJump
ssh -J hop-host:2222 web-prod-01.hop

未指定资产时,终端中会出现由 ratatui 构建的选择器;把资产名放在 SSH 用户名位置,则可以跳过选择直接连接。对于已有大量 SSH 配置和脚本的人,这种兼容比一个功能齐全但封闭的 Web Terminal 更重要。

RDP、VNC、MySQL、PostgreSQL 和 Redis 也没有被分别实现一遍。它们统一落到 SSH 本地端口转发上,资产中的协议只负责提供端口和客户端提示:

ssh -p 2222 -N -T \
-L 127.0.0.1:13389:win-prod-rdp.hop:3389 \
hop-host

Hop 的核心只转发 TCP,不解析上层协议。这既限定了功能范围,也减少了协议实现和维护成本。

把“能访问什么”和“如何登录”拆开#

这是权限模型中很关键的一层。

进入 Hop 的 SSH Key 决定用户可以看见并连接哪些资产;资产绑定的托管凭据,则决定 Hop 用什么身份登录目标主机。二者不是同一个概念。

每把入口 Key 有两种资产访问模式:

  • all:可以访问当前以及未来新增的全部资产;
  • restricted:只能访问明确分配的资产,空集合表示可以进入 Hop,但不能发现或连接任何资产。

同一套授权检查会覆盖 TUI、直连 SSH、SFTP、ProxyJump 和本地 TCP 转发。否则只在界面中隐藏某个资产,却允许用户通过手写目标地址绕过限制,就只是“看起来有权限控制”。

托管凭据也有自己的约束:正在被资产引用时不能直接删除;导出功能只迁移名称、用户名和认证类型等元数据,不导出密码、私钥和 passphrase。便捷的迁移功能不应该顺便变成凭据泄露通道。

从一个人平滑长到一个小团队#

很多权限系统一开始就要求设计角色、策略和组织结构。对只有一个维护者的实例来说,这些步骤只是噪音。

Hop 采用渐进式管理:只有一位有效管理员时,登录页保持简单;添加第二位管理员后才显示账号名。权限级别也只保留三种任务化角色:

角色主要能力
Owner完整管理,包括管理员和访问级别
Operator管理资产、凭据、SSH 访问与 Known Hosts,并查看审计
Viewer只读查看 Dashboard、库存与审计

这里没有通用 RBAC 策略编辑器。它牺牲了一部分任意组合能力,换来的是更容易理解和审计的日常操作。

两个 crate,一条清晰主线#

代码按职责拆成两个主要 crate:

hop-core 配置、数据模型、SQLite、凭证加密
hop-server SSH 服务、TUI、Admin Web、本机 CLI

技术栈围绕 Tokio 展开:russh 处理 SSH,ratatui 提供终端界面,axummaud 组成管理页面,sqlx 管理 SQLite 与 schema 迁移。

这个结构没有为了“微服务化”而拆进程,但依然把可复用的领域和存储能力从入口层分离出来。对一个目标是单二进制部署的项目来说,模块边界通常比进程边界更实用。

备份是安全模型的一部分#

Hop 的运行数据中有三个文件不能遗漏:

hop.db 资产、入口密钥、会话、审计和加密后的凭证
hop.secret 凭证主密钥
hop_host_key SSH 服务端身份

尤其是 hop.secret:丢失后,数据库里的托管凭证无法恢复。只导出资产 CSV 也不能替代完整备份。升级前应停止服务,对整个数据目录做一致性快照,同时保留旧二进制或镜像和一条不经过 Hop 的备用管理路径。

轻量工具更容易部署,也更容易让人忘记它已经成为基础设施的一部分。把备份、回滚和主机密钥身份写进项目的正常使用路径,和把程序编译成一个文件同样重要。

最后#

Hop 并不打算覆盖大型堡垒机的全部场景。它没有复杂审批流,不解析 RDP 或数据库协议,当前审计页的筛选、分页、留存设置和导出也仍待完善。

它更像是在回答另一个问题:当需求只是可信入口、资产选择、托管凭据、透明转发和基础审计时,能不能让系统保持足够小,同时不牺牲默认安全?

我认为这正是“轻量”最有价值的含义。

https://github.com/oslo254804746/hop-rs

Hop:用 Rust 做一台轻量但不草率的 SSH 跳板机
https://oslo254804746.github.io/personal-blog/posts/hop-rs-lightweight-ssh-bastion/
作者
Oslo
发布于
2026-07-30
许可协议
CC BY-NC-SA 4.0