feat: SSH 隧道 — 主机密钥校验 + 跳板机(ProxyJump) + 逐跳连通性检测 + 健壮性#14
Merged
Conversation
- SshAuthMethod 增 'agent',读取 SSH_AUTH_SOCK(win32 回退 OpenSSH 命名管道) - 抽出 tunnelCore 纯函数(buildTunnelOptions/resolveSshAgentSock), 收敛 connect()/test() 两处重复的隧道选项装配 - ConnectionForm 认证方式新增 SSH Agent(无需私钥/密码)+ 三套 i18n - 新增 11 个 tunnelCore 单测
- expandHome 展开私钥路径的前导 ~ / ~/(Node fs 不展开,照表单 占位符 ~/.ssh/id_ed25519 填会 ENOENT) - openTunnel 改为返回隧道句柄、connect() 用局部变量贯穿,删除共享 实例字段 pendingTunnel —— 并发连接不同 id 不再互相覆盖致隧道泄漏 - 新增 expandHome 单测
修复审计标记的安全 blocker:此前 ssh2 无 hostVerifier,盲信任意 host key,可被中间人。 - evaluateHostKey 纯函数:首连无 pin 则信任并学习指纹(TOFU), 之后仅接受完全匹配,变更即拒连 - tunnel.ts 设 hostHash:'sha256' + hostVerifier,捕获不匹配并给出 清晰错误(区分服务器重建 vs MITM);暴露 learnedHostKey - SshConfig.pinnedHostKey(非密钥,明文存);connectionStore .recordSshHostKey 在首连成功后回写;test() 不回写 - ConnectionForm 保留 pin 不被编辑覆盖 + 提供重置入口(合法换机) + 三套 i18n - 新增 evaluateHostKey / pin 透传单测
- ConnectionForm 启用 SSH 时前置校验 host/user 必填、端口 1–65535、 私钥模式必填路径,未过则禁用保存并在底栏给出原因(不再把空值 推迟到连接时才报错) - 启用 SSH 且填了副本集名时给出告警:隧道下按单节点 directConnection 连接、replicaSet 被忽略(对齐"缺失应报错绝不静默") - tunnelCore.readPrivateKey:私钥读取失败给出指明路径的可读错误 - 三套 i18n + readPrivateKey 单测
复用现有 ssh2 实现支持"经跳板机连目标"(等价 ProxyJump /
ProxyCommand -W):连堡垒机 → forwardOut 到目标 SSH 端口 → 在该
通道上再起一条 SSH 到目标 → 从目标 forwardOut 出 mongo。每一跳
独立做 host-key TOFU 校验,复用 agent 认证。
- tunnelCore:TunnelOptions 重构为 { target, jump?, dest },buildHop
统一装配;jump 仅 agent/私钥文件认证、不存任何密钥(带口令私钥
走 ssh-agent)
- tunnel.ts:SshTunnel 支持可选 jump,connectHop 复用于两跳,
learnedHostKey / learnedJumpHostKey 分别上报
- SshConfig.jump(SshHopConfig);connectionStore.recordSshJumpHostKey
回写跳板机指纹;sessionManager 首连后两跳指纹都持久化
- ConnectionForm 新增跳板机区块(开关/host/port/user/认证 agent|key/
host-key 重置)+ 校验 + 三套 i18n
- tunnelCore 单测覆盖 jump 装配(target+jump、jump 不带存储密钥)
PR #14 之后尚未做的:运行期掉线上报(需新 IPC 推送通道,价值最高)、 多跳跳板链、跳板机密码认证、私钥粘贴/文件选择器、连接超时+取消、 自动重连。
- 连接前对入口跳板/SSH 主机做 TCP 可达性预检(tcpProbe,8s 快失败), 把"网络不通"与"认证/host-key 失败"清楚区分开,且不必等 ssh2 的 20s readyTimeout - classifyConnError 纯函数:按 socket code / ssh2 level 把错误分为 network / timeout / dns / auth / hostkey / other 并给可读提示 - 每跳报错标注是 jump host 还是 target(host:port),多跳失败一眼 看出是哪一跳;跳板→目标 forwardOut 失败也单独标注 - 新增 diagnose.test(tcpProbe)+ classifyConnError 单测
面向终端用户:不再需要开终端 ssh-add,私钥+口令在界面内即可完成。 - 新增通用 dialog:openFile IPC(Electron 原生文件选择器,main 侧展开 ~,接 ipc.ts/preload/registerIpc/useAppStore);私钥路径旁加“浏览…” - 跳板机那一跳支持带口令私钥:新增第 4 个加密密钥 encJumpSshPassphrase (safeStorage),沿 StoredSecrets/sanitize/getDecrypted/saveConnection/ ConnectionInput/DecryptedConnection/inputToDecrypted 对称扩展;表单加 “跳板机私钥密码短语”框 - tunnelCore 给 jump 传 dec.jumpSshPassphrase(此前硬编码空对象) - 软化 auth 报错:从“去终端 ssh-add”改为“在设置里改用私钥文件” - 三套 i18n(browse/pickKey/jumpPassphrase,改写 jumpHint)+ 单测 - agent 降为可选快路(保留),私钥文件成为不依赖终端的稳路
把之前隐形的网络预检做成 GUI 可见功能:SSH 标签页加「检测连通性」 按钮,点击逐段显示 TCP→跳板机 / 登录跳板机 / 经跳板机到目标SSH端口 / 登录目标 / 到达MongoDB端口 的 ✓/✗ + 耗时 + 失败原因,哪一段断了一眼 可见(非技术用户也能自查)。 - tunnel.ts:diagnoseConnection 逐段执行、首个失败即停(其余 skip)、 收尾销毁所有 client;connectHop 抽成模块级可复用(SshTunnel 与 诊断共用) - tunnelCore.planDiagnoseStages:纯函数规划阶段(可测,有无跳板两种) - IPC connections:diagnose 全链路(ipc/preload/registerIpc/useAppStore) - ConnectionForm:SSH 区「检测连通性」按钮 + 结果面板;三套 i18n - 新增 planDiagnoseStages 单测
- 移除 ssh-agent 认证方式(主连接下拉 + 跳板机),跳板机仅私钥 - 私钥密码短语提示改为输入框 placeholder(已存密钥仍优先显示掩码) - 移除主机密钥指纹展示与重置勾选;TOFU 校验机制保留(静默校验, 首连学习、变更即拒),变更拒连报错改为提示删除并重建连接
- check connectivity 拆成「SSH 主机」「跳板机」两个按钮,各测自己那一跳 (去掉到 MongoDB 端口那步,避免与 Test connection 重合);SSH 主机检测 在配了跳板机时经跳板机测目标,否则直连 - 结果默认只显示总结 ✓/✗ + chevron,click 展开逐步日志(失败自动展开) - 移除跳板机区块冗余说明文字(与上方 SSH 段不对称) - diagnose IPC 增加 scope 参数(ssh|jump),贯穿 ipc/preload/registerIpc/store
- SshHopConfig.authMethod 收紧为 'privateKey',类型层杜绝跳板机密码登录 - TODO: 文件选择器已实现(误列为待办);补上逐跳连通性检测到已交付清单
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
MongoDB 连接的 SSH 隧道之前停在「能连单节点」的雏形,存在一个安全 blocker(盲信任 host key)与若干缺口。本 PR 补齐到「安全可用」,并加上跳板机(ProxyJump)与可视化的逐跳连通性检测。
改了什么
主机密钥校验 / TOFU(修复安全 blocker)
hostVerifier,盲信任意 host key,可被中间人。evaluateHostKey:首连学习并固定指纹(TOFU),之后仅接受完全匹配,变更即拒。跳板机 / ProxyJump
ProxyJump/ssh -W):连堡垒机 →forwardOut到目标 → 在该通道上再起一条 SSH 到目标 → 从目标 forwardOut 出 mongo。逐跳连通性检测(Check connectivity)
tcpProbe,8s 快失败),把「网络不通」与「认证 / host-key 失败」清楚区分。健壮性
~路径展开(expandHome,否则 ENOENT)、并发隧道句柄竞态(删共享pendingTunnel、改局部变量贯穿)。dialog:openFile+「浏览…」)。main/ssh/tunnelCore.ts(纯函数,有单测)+tunnel.ts(ssh2 副作用)。SSH 认证:主机支持密码 + 私钥;跳板机仅私钥。(ssh-agent 在迭代中评估后移除。)
测试
pnpm typecheck✅(node + web)pnpm test:unit✅ 369 passedpnpm test:integration✅ 116 passedSSH 握手 / host-key / 跳板机嵌套无法用
mongodb-memory-server模拟,建议真机冒烟:未包含(后续,见
TODO.md§3)运行期 SSH 掉线上报、多跳跳板链、跳板机密码认证、私钥粘贴内容(textarea)、连接总超时 + 取消、隧道/驱动自动重连。