# ssh_tunnel_manager **Repository Path**: cvdnn/ssh_tunnel_manager ## Basic Information - **Project Name**: ssh_tunnel_manager - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-30 - **Last Updated**: 2026-10-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 隧道管家 (SSH Tunnel Manager) SSH 本地端口转发桌面工具,使用 PySide6、QFluentWidgets 和系统 OpenSSH。每条启用的隧道由一个独立的 SSH 进程维护。已加入 Windows、macOS、Linux 桌面适配;本机回归环境为 Windows,macOS/Linux 原生桌面验证尚待完成。 macOS/Linux 的安装、数据目录、自启方式与验证边界见 [跨平台运行说明](docs/cross-platform.md)。新安装默认将数据保存到用户目录;已有项目 `data/` 配置继续沿用。下文 `data/`、`logs/` 均相对于选定的运行数据根目录。 ## 版本号 版本从 `v1` 开始按整数递增(`v1`、`v2`、`v3`……),唯一来源是 `src/platform_support.py` 的 `APP_VERSION_MAJOR`,发布时只改这一行。界面(启动页、窗口标题、“系统配置 → 常规”)、启动日志和 `scripts/build_app.py` 的产物文件名使用该整数推导出的 `v` 标签;`Info.plist` 与 Inno Setup 的版本字段只接受数字,使用同一整数的数字形式。 ## 安装与启动 需要桌面环境、Python 和 OpenSSH 客户端。本次验证环境为 Python 3.12,GUI 依赖版本固定在 `requirements.txt`。以下为 Windows 操作。Fluent 与无边框窗口的包名分别是 [PySide6-Fluent-Widgets](https://pypi.org/project/PySide6-Fluent-Widgets/) 和 [PySideSix-Frameless-Window](https://pypi.org/project/PySideSix-Frameless-Window/)。 在项目目录中执行: ```powershell python -m venv .venv & .\.venv\Scripts\python.exe -m pip install -r requirements.txt & .\.venv\Scripts\pythonw.exe .\bin\ssh-tunnel-manager.pyw ``` 若已有本项目的 `.venv`,直接执行最后一条即可。也可从任意工作目录运行 `scripts/dev.ps1` 安装依赖。根目录旧 `ssh_tunnel_manager.pyw` 暂时保留为兼容入口,已有快捷方式仍可使用。双击 `.pyw` 使用 Windows 文件关联的 Python,不一定使用本项目虚拟环境。启动异常时,用 `.venv\Scripts\python.exe` 替代 `pythonw.exe` 查看终端错误。 升级旧目录时,先通过托盘菜单彻底退出程序,再运行 `scripts/migrate-layout.ps1` 预览迁移;确认没有目标冲突后加 `-Apply`。脚本将根目录的 `settings.json`、`tunnels.json`、`ssh_connections.json` 和 `ssh_tunnel.log` 原样移至 `data/`、`logs/`,发现目标已存在或 JSON 损坏时拒绝迁移。迁移后应用只在新位置读写;仓库中的 `config/examples/` 是脱敏示例,不会自动连接。 程序优先查找 Windows 系统目录中的 OpenSSH,再查找 PATH;可在“系统配置”中指定路径。默认启动时连接 `data/tunnels.json` 中已启用隧道,也可关闭“启动时连接已启用隧道”,改为手动启动。 启动页在独立轻量进程中显示连续滑动动画,避免界面库加载和主窗口初始化造成进度条停顿;主窗口显示后自动关闭启动页。滑动条表示正在加载,不代表完成百分比。 SSH 认证由 OpenSSH 和用户 `~/.ssh/config` 管理(Windows 为 `%USERPROFILE%\.ssh\config`)。程序使用 `BatchMode=yes`,不弹出密码输入框;请先配置可非交互登录的密钥或 SSH agent。 ## 打包为可双击应用 `scripts/build_app.py` 用 PyInstaller 把当前虚拟环境的解释器、`requirements.txt` 的 GUI 依赖和 `src/` 源码冻结成一个自包含应用:目标机器不需要安装 Python,也不需要再下载任何库。PyInstaller 不能交叉打包,请在 macOS 上生成 `.app`、在 Windows 上生成 `.exe`。 ```sh # macOS .venv/bin/python scripts/build_app.py --install --dmg --smoke-test ``` ```powershell # Windows(PowerShell) .\scripts\package.ps1 --install --installer --smoke-test ``` 产物写入 `release/`(Git 忽略): | 系统 | 双击入口 | 分发文件 | |---|---|---| | macOS | `SSH Tunnel Manager.app` | `SSH-Tunnel-Manager-<版本>-macos.zip`;`--dmg` 另出拖拽安装盘 | | Windows | `SSH Tunnel Manager\SSH Tunnel Manager.exe` | `SSH-Tunnel-Manager-<版本>-windows.zip`;`--installer` 且构建机装有 Inno Setup 时另出 `...-installer.exe` | 打包版仍按“数据目录”规则读写用户目录,用 `--data-dir "绝对目录"` 可改到便携位置。其他开关:`--output`、`--name`、`--version`、`--no-icon`、`--no-archive`、`--smoke-test`(用临时数据目录启动产物,写出启动日志才算通过)、`--install`(缺少 PyInstaller 时代为安装)。 打包版只含界面与隧道逻辑,OpenSSH 客户端仍使用系统自带的 `ssh`/`ssh.exe`,需在目标机器上可用;首次连接的主机密钥也要先用 OpenSSH 接受。macOS 产物仅 ad-hoc 签名,拷到另一台 Mac 首次打开需右键 →“打开”,免提示需 Developer ID 签名并公证。`scripts/make_macos_app.py` 生成的轻量启动器仍适合源码调试,它指向虚拟环境、不含依赖。 ## 功能 - 主窗口默认固定为 1100 × 660;打开右侧辅助工作区时固定扩展为 1520 × 660(Qt 逻辑像素,随系统 DPI 缩放)。屏幕放不下时按可用区域收窄:工作区最少保留 320 像素,仍不足时先压缩列表区域,窗口不会超出屏幕。保留拖动和最小化,移除最大化按钮并禁用边缘缩放和标题栏双击最大化。 - 顶部单行排列列表标题、在线计数、新建隧道、系统配置及窗口控制按钮。 - 点击“新建隧道”会在主界面右侧扩展辅助工作区,原界面保持原位;扩展区有与主界面同高的标题栏和独立关闭按钮,可在右侧填写并创建隧道。工作区默认收起,也可用取消按钮收起。 - 点击隧道列表中的一行,整行以浅青色高亮,并在右侧工作区显示连接详情;再次点击同一行会取消选中并收起详情,点击其他行则直接切换详情。状态区域悬停或获得键盘焦点时高亮,点击即可重新连接(已停用时会启用),操作中禁用重复点击。行末 `⋮` 提供远程桌面、复制地址与删除操作。保存已启用的隧道会重新连接。 - 点击“系统配置”会在右侧工作区显示开机自启、托盘行为、SSH 路径和心跳频率设置;取消或关闭会放弃未保存的修改。 - 本地绑定地址应用于 `ssh -L` 参数;复制地址、打开远程桌面和端口探测使用对应地址。 - 支持 IPv4/IPv6;监听 `0.0.0.0`、`*` 或 `::` 时,本机复制/连接地址使用相应的回环地址。IPv6 的 `host:port` 地址使用方括号。 - 从用户 `~/.ssh/config` 或指定配置文件发现显式 `Host`,支持 `Include`、多个别名、大小写无关关键字。与设置中添加的手动连接合并展示,标明来源。同名项通过独立连接 ID 区分。通配及否定 Host 不单独列为连接。 - 可折叠日志区采用单一卡片边界,显示连接状态和 SSH 错误输出;“清空日志”是无边框轻量操作,保留悬停和按下反馈,仅清空界面内容,历史文件保留。 - 系统配置提供 SSH 路径、检查周期、关闭到托盘和当前用户登录时自启设置。 - 系统托盘右键菜单贴在任务栏上方,并与托盘图标左边缘对齐;菜单项留有适当间距,支持恢复窗口、重连所有启用隧道和彻底退出。退出会停止管理的 SSH 进程。 旧 `settings.json` 的 `sshAliases` 和旧隧道继续兼容,编辑旧隧道不会因为同名连接而切换目标。新连接建议通过“系统配置 → SSH 连接”管理。 ## SSH 连接管理与回写 “系统配置”包含“常规”和“SSH 连接”页。常规页可设置 SSH 程序、配置入口、启动自动连接、健康检查周期、等待就绪时间和两种端口探测超时。 在 SSH 连接页点击“新建”,填写名称、主机、端口(默认 22),以及可选的用户名、私钥路径、跳板机、连接超时和保活参数。点击“保存配置”持久化到 `ssh_connections.json`;“应用到草稿”只更新当前表单草稿,“取消”会放弃未保存内容。私钥只保存路径,不保存内容或密码。 配置来源只读,实际 HostName、User、IdentityFile、ProxyJump、ProxyCommand、Host/Match 规则由 OpenSSH 在连接时解析。程序不会为刷新列表运行 `ssh -G` 或配置中的命令。打开连接编辑或设置页会重新读取,连接页也提供“刷新配置来源”。相对 Include 依照 OpenSSH 规则从用户 `~/.ssh` 解析,支持 `%d`、`%%` 和 `${环境变量}`;其他动态 token 无法安全静态解析时明确报错。Include 中发现的名称是候选项,不代表其条件一定成立。 手动连接可以勾选后点击“预览并导出勾选连接”: 1. 填写合法 SSH 导出别名;中文名称可保留,但需另填 ASCII 导出别名。 2. 预览目标文件路径及完整写入内容。 3. 点击独立的“确认写入”后才写文件;此操作立即生效,与设置页保存/取消独立。 导出会检查入口文件及 Include 中的同名 Host,冲突时拒绝覆盖。写入前重新检查文件变化,创建旁路 `config.backup-<唯一编号>` 备份,再原子替换。新 Host 块插入文件开头,原始字节保留,并通过 `Host *` 恢复原文件开头全局选项的作用范围。成功后显示备份位置并刷新列表。导出不删除手动记录,不自动修改已有隧道引用;重复导出同名 Host 会被拒绝。恢复时先退出其他配置编辑器,再用备份替换对应配置文件。 手动连接重命名保留内部 ID;修改后仅重连引用该连接的启用隧道。正在使用的连接不能删除,界面会列出引用隧道。SSH Host 删除后,已有进程不会因刷新被中断,但下一次实际重连将报告引用缺失,不回退为同名 DNS 目标。 应用保持非交互认证,继承 SSH 主机密钥策略,不再强制 `StrictHostKeyChecking=no`。首次连接未信任主机时,需要先用 OpenSSH 验证并接受主机密钥。 | 参数归属 | 保存/生效位置 | |---|---| | 应用行为、程序/配置路径、检查和探测时间 | 设置表单 → `settings.json` | | 手动主机、用户、端口、私钥路径、跳板机和可选连接参数 | 连接表单 → `ssh_connections.json` | | SSH 来源的认证、网络和主机密钥策略 | 原 SSH config,由 OpenSSH 读取 | | 本地监听、远端服务、启用及自动重连 | 隧道表单 → `tunnels.json` | | 工作线程数、动画间隔和界面尺寸 | 本次仍为实现常量,不提供设置控件 | ## 连接状态与重连 只有 **SSH 进程存活且指定本地端口能够建立 TCP 连接** 时,界面才显示“已连接”并记录“连接成功”。这表示本地转发入口就绪,不保证远端 RDP、数据库或 VNC 服务可用;远端服务故障需结合 SSH 日志与客户端连接结果排查。 默认每 4 秒检查一次,可设置为 2~60 秒。这是应用检查周期,与 SSH 保活不同。SSH 保活和连接超时默认继承 SSH 配置;手动连接可显式指定覆盖值。 “启用”列后的“连通性”按钮可手动测试本地转发端口,TCP 连接超时为 2 秒。按钮始终显示“检测”,使用与状态区域一致的轻量悬停/焦点高亮。测试在线程池执行,期间文字右侧显示旋转小图标,状态列跟随显示“排队中/检测端口”;结果返回后隐藏小图标,状态列显示连接结果,详细结果见悬浮说明及日志。按钮不显示结果文本,可再次点击测试。只有 SSH 进程存活且本地端口可连接才判定可用,不验证远端应用协议。手动测试不会重启 SSH;后续定时检查仍按原有自动重连设置运行。停用或连接操作进行中时按钮不可用,停用、编辑、删除后的过期结果不会覆盖新状态。 启动连接、端口探测、重连和进程清理均由后台线程池执行,最多并行处理 8 条隧道;主窗口显示无需等待连接或探测完成。每条隧道的操作串行执行,尚未完成的心跳不会重复排队。编辑、停用或删除后,旧任务的结果不会覆盖新状态,界面与日志只在主线程更新。彻底退出时先隐藏界面,在后台完成 SSH 进程清理后结束程序。 新启动 SSH 默认等待本地端口就绪 30 秒,可在设置中调整;自动/手动端口探测超时默认分别为 0.3/2 秒,也可调整。等待阈值与 SSH 的 ConnectTimeout 独立,应为握手、认证和监听留出足够时间;应用会校验手动连接明确填写的建连超时不超过等待阈值。进程退出或监听超时后,按 `autoReconnect` 决定是否重试。 托盘颜色: | 颜色 | 含义 | |---|---| | 绿色 | 全部启用隧道在线 | | 黄色 | 正在连接、重连,或仅部分隧道在线 | | 红色 | 所有启用隧道均断开,且没有连接中的隧道 | | 灰色 | 没有启用隧道,或服务处于暂停状态 | 列表计数为在线数/全部配置数;启动汇总日志和托盘计数为在线数/启用数。 ## 隧道 Item 状态图标 状态栏使用矢量线条图标、文字和悬浮说明,避免只靠颜色判断。 运行阶段的外圈带动画,隐藏窗口后暂停;后台进度约每 30 ms 收集一次, 很快完成的步骤可能直接切换到下一阶段,不人为延长连接或测试。 | 阶段 / 结果 | 图标 | 含义 | |---|---|---| | 排队中 | 灰色沙漏 | 等待工作线程或上一操作结束 | | 启动连接 | 琥珀色连接符 | 正在创建 SSH 进程 | | 等待监听 | 琥珀色时钟 | 进程已启动,等待后续健康检查 | | 检查进程 | 青色脉冲线 | 检查存活情况、收集诊断 | | 检测端口 | 青色放大镜 | 测试本地 TCP 入口 | | 重连中 / 重连等待 | 琥珀色回转箭头 | 自动恢复连接;悬浮显示重试次数 | | 已连接 | 绿色圆圈勾 | 进程存活且本地端口可连接 | | 已断开 | 红色圆圈叉 | 尚未确认可用;失败原因见悬浮说明和日志 | | 停止中 | 灰色方块 | 回收进程资源 | | 移除中 | 灰色叉 | 回收待移除隧道的进程 | | 已停用 | 灰色暂停符 | 未启用隧道 | 连接流程:排队 → 清理旧进程 → 启动连接 → 等待监听。 定时状态测试及其自动重连在后台执行,期间保留上一次连接结果,不切换图标、文字或启动检测动画。用户手动触发的连接、停止和端口测试则即时显示操作阶段。 结果返回后才更新连接状态;结果及说明相同时不重复更新控件,避免定时检查造成闪动。 进程存活但端口尚未就绪时,启动 30 秒内继续等待;失败后自动重连, 或在关闭自动重连时转为已断开。重连仍需再次检测成功才显示已连接。 SSH 内部 DNS、握手、认证未提供独立事件,因此不显示未经确认的子阶段。 操作阶段与连接结果独立保存于内存。主动操作的悬浮说明显示两者及最近异常,后台检查期间保留原说明; 旧任务的阶段和结果均按版本过滤,避免编辑、停用后出现过期提示。 删除操作会立即移除列表行,进程在后台清理;因此移除图标通常不在列表中停留。 “已连接”不代表远端 RDP、数据库等服务已经通过可用性测试。 ## 初始配置与错误处理 首次启动没有隧道文件时显示空列表,不创建或连接内置内网示例。隧道或手动连接配置损坏时明确记录错误,阻止覆盖损坏文件,修复后重启。保存失败会保留设置/新建表单,不提示成功。已有用户配置不会因本次代码更新被改写。 ## 日志与排错 SSH 的 stderr 由后台线程持续读取,主线程在检查连接或停止进程时写入界面和 `ssh_tunnel.log`。每条隧道保留最近 200 段待处理输出,每段至多 4096 字符,避免异常输出占满内存。SSH 进程退出还会记录退出码。 - `Permission denied`:检查 SSH 用户、密钥和 agent,以及命令行非交互登录是否成功。 - `Address already in use`:本地端口已被占用,关闭占用程序或更换端口。 - `Cannot assign requested address`:本地绑定地址不属于当前机器,修正“本地地址”。 - `channel ... open failed`、`Connection refused`:检查跳板机到目标地址/端口的可达性与服务状态。 - 启动 `ssh.exe` 失败:检查系统配置中的可执行文件路径。 ## 文件与测试 ```text bin/ssh-tunnel-manager.pyw 日常启动入口 src/app.py 主程序 src/ssh_connections.py SSH 发现、连接校验、原子保存与导出 src/connection_settings.py 手动连接编辑及导出预览 src/paths.py 数据、日志与资源路径 src/assets/app_logo.png 图像资源(当前窗口图标由代码绘制) data/tunnels.json 隧道规则(本机文件,Git 忽略) data/settings.json 全局设置和 SSH 别名(本机文件,Git 忽略) data/ssh_connections.json 用户手动 SSH 连接(本机文件,Git 忽略) logs/ssh_tunnel.log 运行时日志(Git 忽略) config/examples/ 脱敏配置示例 scripts/ 环境、测试、迁移与预览工具 requirements.txt 已验证的 GUI 依赖版本 tests/ unittest 回归测试 ``` 执行测试和语法检查: ```powershell & .\scripts\test.ps1 ``` Windows/Linux 测试使用 Qt offscreen 和临时配置;macOS 因原生无边框窗口依赖 Cocoa,需图形会话,可能短暂显示测试窗口。覆盖绑定/客户端地址、IPv6、端口探测、连接状态、重连等待、真实本地子进程的 stderr 与清理、托盘颜色及窗口构造。测试不连接真实 SSH 主机,也不改写用户的隧道配置或注册表。三个系统均可使用当前环境的 `python scripts/test.py` 运行测试、语法和依赖检查。 `.gitignore` 排除虚拟环境、缓存、`data/` 和 `logs/`。新增测试在临时目录验证 Include、配置保存、旧引用兼容、同名来源、缺失连接、导出冲突、备份与并发修改检查;不改写用户 SSH 配置。离屏预览使用 `tests/fixtures/` 中的脱敏样例,脚本位于 `scripts/preview/`。