SSLTool V4.0:从一个 SSH 客户端到一个多协议终端工作台

• 6 分钟阅读 • code

本文记录 SSLTool 从 V3.1 到 V4.0 的安全加固与体验升级:多协议、代理、连接复用、录制、SSH 配置互操作、钩子与效率工具,以及一路上踩过的坑。

起点

V3.1 的 SSLTool 已经是一个具备 SSH 终端、SFTP、跳板机、TOFU 主机密钥验证、会话与工程管理的桌面客户端。V4.0 的目标是把它从「SSH 客户端」推近「终端工作台」:同一套界面能连更多东西,并在真实运维场景里少几次点击。

三个梯队一并落地:

多协议:抓住「会话线程」这层抽象

现有代码里,每种连接都是一个 QThread,向上提供相同的信号(connected / dataReceived / error / disconnected)。V4.0 没有重写这套约定,而是让新协议去实现它:

于是标签页、日志、录制、搜索、快捷键全都自动继承——加协议的成本被压到了「写一个 run 循环」。

代理与跳板机是两层正交的能力

代理回答「本机如何到达目标网络」,跳板机回答「哪台机器替我去连目标」。两者在代码里通过同一个缝对接:

sock_factory = None
if self._jump is not None:
    sock_factory = lambda: self._jump.open_channel(self.host, self.port)
elif self.proxy is not None:
    sock_factory = lambda: self._make_proxy_sock(self.host, self.port)

paramiko 的 SSHClient.connect(sock=...) 接受任何「已完成握手、可直接当作目标套接字」的 socket,所以 SOCKS5 / HTTP CONNECT 握手逻辑独立在 app/core/proxy.py 里,SSH、SSL、Telnet 与跳板机都能复用同一份实现。

代理最容易被忽略的细节是先认证、后建隧道:

sock.sendall(b"\x05" + bytes([len(methods)]) + methods)
ver, method = _recv_exact(sock, 2)
if method == 0x02:          # 用户名密码
    ...

测试里,我们写了一个迷你 SOCKS5 服务器(同时回放 seen 列表),让 SSHWorker 带 proxy dict 去连内嵌 sshd——如果代码忘了走代理,seen 会是空的,测试立刻失败。

连接复用池:省的是重复握手,防的是重复转发

同一目标、同一跳板机、同一认证方式的多个标签,其实只需要一条已认证连接。app/core/pool.py 做的是典型的引用计数池:

entry = pool.acquire(key, connect_fn)   # 命中 refcount+1,否则建连
...                                     # 每标签独立 channel / shell
pool.release(entry)                     # 归零后闲置,超时由后台线程回收

但有一条硬约束:端口转发只由建立该连接的标签启动。如果复用连接的新标签也去启动 -L 规则,同一个端口会被监听两次。所以每个标签记录「我是不是这条连接的首建者」,转发结果只打给首建标签。

闲置回收用后台线程(IDLE_TIMEOUT = 600 秒),进程退出时 pool.close_all()。

录制:最小可用的 asciinema cast v2

录制不追求完整回放,只做「事后能翻」:每个输出块追加一条 o 事件,输入可选记录 i 事件。

rec.write(b"hello cast")      # → [0.123, "o", "hello cast"]
rec.note_input("ls\n")        # → [0.456, "i", "ls\n"]

文件头是一行 JSON({"version": 2, "width": 80, "height": 24, ...}),之后的每行是一条事件。这个格式的价值在于:现有工具链都能读——asciinema play、各种 cast 查看器,甚至简单的 jq。

SSH 配置互操作:解析比导出难

导入 ~/.ssh/config 只需要处理五个字段:HostName / User / Port / IdentityFile / ProxyJump。真正麻烦的是边界:

导出走相反方向:每个会话生成一个 Host 块,跳板机写成 ProxyJump user@host:port。互操作的意义是「用户已有的资产不必重录」。

钩子:能在真实机器上跑命令的便利与风险

偏好设置里配置连接前 / 后命令,执行时注入 SSHTOOL_HOST / SSHTOOL_PORT / SSHTOOL_USER 环境变量:

run_hook("ssh-add ~/.ssh/id_ed25519", "pre", {"host": ..., "port": ...})

钩子输出会显示在终端里,而不是悄悄吞掉——出问题时用户能看到原因。同时文档明确写出:钩子是本机命令执行,只应配置你自己信任的命令。

i18n:不做框架,只做字典

V4.0 的界面语言切换是最小实现:一个 t() 函数 + 两张字典,运行时按语言取值,菜单标题在切换时原地重命名。

i18n.set_language("en")
i18n.t("menu.file")                       # "File"
i18n.t("status.reconnecting", n=2, max=5) # "Reconnecting (2/5)"

这不是完整的本地化方案,但对「中英两语」的需求,它比任何框架都轻。

诚实的限制:证书认证

paramiko 没有消费 OpenSSH 用户证书的能力。V4.0 的做法是把证书路径当私钥文件传给 key_filename,并在 README 里明确写:证书认证为实验性,OpenSSH 证书文件通常不会被解析。

同样诚实处理的还有 SOCKS4(仅无认证,用户名作 userid)与代理密码(不保存,每次询问)。把限制写进文档,比假装支持更省用户的时间。

测试:两套件各司其职

新增的 test_proxy_socks 与 test_telnet 是本版最有价值的两个测试:它们第一次证明了「代理隧道 + paramiko」与「Telnet 裸字节流」在真实连接下的正确性。

结尾

V4.0 的功能清单很长,但真正让这个版本成立的,是三处「不显眼」的设计:

  1. 会话线程同构——新协议几乎零成本接入界面层
  2. sock_factory 这个缝——代理与跳板机正交组合
  3. 诚实写限制——证书、SOCKS4、代理密码

工具的价值不在于支持多少协议,而在于每个协议都走得通、可解释、出问题能定位。
本项目更新到此为止,不再维护
https://github.com/Urtaome0211/SSLTool/releases/tag/v4.0

SSLTool v4.0 — Copyright (c) 2026 Urtaome0211

阅读进度 0%