Add idempotent ktui sync, managed host keys, recoverable SSH sessions, resumable SFTP transfers, and mouse interactions. Enable background tunnels and staged self-updates on Windows with broader lifecycle tests.
398 lines
15 KiB
Markdown
398 lines
15 KiB
Markdown
# klink
|
||
|
||
`klink` 是一个使用 Go 编写的终端 TUI SSH 节点和端口转发管理器。它保存结构化节点配置,
|
||
使用系统 OpenSSH 建立连接,并提供本地、远程和动态 SOCKS5 转发。
|
||
|
||
|
||
## 当前能力
|
||
|
||
- 节点新增、编辑、复制、删除、搜索和标签
|
||
- IPv6 优先,网络不可达时回退 IPv4
|
||
- SSH Agent、私钥文件、系统凭据存储中的密码、ProxyJump 和 HostKey 策略
|
||
- Host key 指纹扫描、确认后写入或移除统一的 known_hosts
|
||
- 可恢复的 tmux SSH 会话与 Tunnel 启动状态自愈
|
||
- 本地转发 `-L`、远程转发 `-R`、动态转发 `-D`
|
||
- 隧道专用会话 `-N`、后台 worker、状态恢复和持续自动重连
|
||
- ktui 节点幂等同步、klink JSON 和 OpenSSH `ssh_config` 导入
|
||
- 版本化 JSON 导出
|
||
- TUI 宽屏双栏和窄屏单栏布局
|
||
- Nodes、Tunnels、SFTP、Settings 和 About 使用一致的单宽语义图标,无需 Nerd Font
|
||
- 可持久化的主题、SSH、地址优先级和隧道默认设置
|
||
- TUI 配置导入、导出、重载和本机 SSH 环境诊断
|
||
- SFTP 本地/远程双栏文件浏览、文件与目录递归上传下载
|
||
- About 应用信息、Gitea Release 更新检查和应用内更新
|
||
|
||
## 环境要求
|
||
|
||
- PATH 中可用的 OpenSSH `ssh`
|
||
- macOS、Linux 和 Windows:支持前台和后台隧道
|
||
|
||
保存密码依赖操作系统凭据存储:macOS 使用 Keychain,Windows 使用 Credential Manager,
|
||
Linux/BSD 使用 Secret Service(例如 GNOME Keyring 或 KWallet)。klink 不会把密码写入 `config.json`。
|
||
|
||
## 安装
|
||
|
||
macOS 和 Linux:
|
||
|
||
```sh
|
||
curl -fsSL https://gitea.bytevibe.dev/gary/klink/raw/branch/main/install.sh | sh
|
||
```
|
||
|
||
Windows PowerShell 5.1 或更高版本:
|
||
|
||
```powershell
|
||
irm https://gitea.bytevibe.dev/gary/klink/raw/branch/main/install.ps1 | iex
|
||
```
|
||
|
||
安装器会自动识别操作系统和 CPU 架构,从公开的 Gitea Release 下载最新版,并在安装前校验
|
||
`checksums.txt` 中的 SHA256。macOS/Linux 默认安装到可写的 `/usr/local/bin`,否则使用
|
||
`~/.local/bin`;Windows 默认安装到 `%LOCALAPPDATA%\Programs\klink\bin` 并加入用户 `PATH`。
|
||
|
||
安装指定版本或覆盖安装目录:
|
||
|
||
```sh
|
||
curl -fsSL https://gitea.bytevibe.dev/gary/klink/raw/branch/main/install.sh | \
|
||
KLINK_VERSION=v0.1.0 KLINK_INSTALL_DIR="$HOME/.local/bin" sh
|
||
```
|
||
|
||
```powershell
|
||
$env:KLINK_VERSION="v0.1.0"
|
||
$env:KLINK_INSTALL_DIR="$HOME\bin"
|
||
irm https://gitea.bytevibe.dev/gary/klink/raw/branch/main/install.ps1 | iex
|
||
```
|
||
|
||
## 构建
|
||
|
||
从源码构建需要 Go 1.25 或更高版本。
|
||
|
||
```sh
|
||
make build
|
||
./bin/klink version
|
||
```
|
||
|
||
也可以直接运行:
|
||
|
||
```sh
|
||
go run ./cmd/klink
|
||
```
|
||
|
||
## 快速开始
|
||
|
||
添加节点:
|
||
|
||
```sh
|
||
klink add Tokyo \
|
||
--ipv6 2001:db8::10 \
|
||
--ipv4 203.0.113.10 \
|
||
--user root \
|
||
--identity ~/.ssh/id_ed25519 \
|
||
--tags production,asia
|
||
```
|
||
|
||
在 Nodes 页面新增或编辑节点时,`Authentication` 可以选择 `SSH Agent`、`Key file` 或
|
||
`Password`。选择密码后输入的内容会保存到系统凭据存储,表单和配置文件中都不会显示明文;
|
||
再次编辑时密码留空会保留现有凭据。命令行可用 `--auth agent|key|password` 选择认证方式,
|
||
密码本身需要在 TUI 节点编辑器中保存。
|
||
|
||
打开 TUI:
|
||
|
||
```sh
|
||
klink
|
||
```
|
||
|
||
连接或预览实际执行的 OpenSSH 命令:
|
||
|
||
```sh
|
||
klink connect Tokyo
|
||
klink connect Tokyo --dry-run
|
||
klink connect Tokyo --ipv4
|
||
klink connect Tokyo --session
|
||
```
|
||
|
||
TUI 连接默认在系统安装了 `tmux` 时使用节点专属会话。SSH 中按 `Ctrl-b d` 可暂时分离并返回
|
||
klink,再次选择同一节点会恢复原会话;未安装 `tmux`、已经位于 tmux 内,或设置
|
||
`KLINK_NO_SESSION=1` 时自动使用普通 SSH 连接。
|
||
|
||
```sh
|
||
klink session list
|
||
klink session attach Tokyo
|
||
klink session kill Tokyo
|
||
```
|
||
|
||
检查服务器当前提供的 host key 指纹,再接受或移除 known_hosts 记录:
|
||
|
||
```sh
|
||
klink host-key inspect Tokyo --ipv6
|
||
klink host-key accept Tokyo --ipv6 --algorithm ssh-ed25519
|
||
klink host-key remove Tokyo --ipv6
|
||
```
|
||
|
||
## TUI 操作
|
||
|
||
| 按键 | 操作 |
|
||
| --- | --- |
|
||
| `Tab` / `Shift+Tab` | 切换下一个/上一个主页标签 |
|
||
| `←` / `→` 或 `h` / `l` | 在当前页面切换焦点或调整选项 |
|
||
| `↑` / `↓` 或 `j` / `k` | 移动选择 |
|
||
| `Enter` | 连接当前节点 |
|
||
| `/` | 搜索节点 |
|
||
| `a` / `e` / `c` / `d` | 新增、编辑、复制、删除 |
|
||
| `s` | 启动或停止当前隧道 |
|
||
| `r` | 重启当前隧道 |
|
||
| `Ctrl+S` | 保存编辑表单 |
|
||
| 空格 | 在设置页和编辑器中调整当前选项 |
|
||
| `Esc` | 取消或返回 |
|
||
| `q` | 退出 |
|
||
|
||
支持鼠标单击标签、列表项和 SFTP 面板,使用滚轮移动指针所在的列表。双击节点、隧道、
|
||
设置项、SFTP 文件或弹窗列表项会执行对应操作;表单字段和弹窗按钮可以直接点击。
|
||
|
||
设置 `NO_COLOR=1` 可以禁用颜色。
|
||
|
||
### About 页面
|
||
|
||
About 页面展示 klink Logo、版本、构建信息、运行平台、配置路径和项目地址。
|
||
使用 `↑/↓` 或 `j/k` 选择操作,按 `Enter` 检查更新或安装已经发现的新版本。
|
||
更新来自 [gitea.bytevibe.dev/gary/klink](https://gitea.bytevibe.dev/gary/klink) 的最新 Release;
|
||
klink 只会安装与当前系统、架构匹配且通过 `checksums.txt` SHA256 校验的附件。安装完成后需要重新启动 klink。
|
||
Windows 无法覆盖正在运行的可执行文件,因此会先把新版本暂存到当前程序旁边;退出 klink 后,
|
||
后台 PowerShell 助手会完成替换,再次启动即使用新版本。若替换失败,错误会写入
|
||
`klink.exe.update-error.log`。
|
||
|
||
当前仓库尚未发布 Release 时,检查结果会显示“尚未发布”,不会被当作网络故障。
|
||
|
||
### Settings 页面
|
||
|
||
在 Settings 页面使用 `↑/↓` 或 `j/k` 选择项目,使用 `←/→`、`h/l` 或空格修改,
|
||
操作项目按 `Enter` 执行。修改会立即保存。
|
||
|
||
可配置项目:
|
||
|
||
- `system`、`dark`、`light` 主题和颜色开关
|
||
- 新节点默认使用 SSH Agent、HostKey 策略和连接超时
|
||
- IPv6 优先或 IPv4 优先的连接顺序
|
||
- 新隧道默认模式和自动重连
|
||
- 从 ktui 同步节点,或导入 klink JSON 和 OpenSSH `ssh_config`
|
||
- 导出包含全局设置、节点和隧道的完整 JSON 备份
|
||
- 从磁盘重新加载当前配置
|
||
- 检查本机 `ssh` 路径、版本、`SSH_AUTH_SOCK` 和 Agent 身份状态
|
||
- 扫描所选节点的 host key 指纹,展示算法和 SHA256 指纹,并在用户确认后接受或移除记录
|
||
|
||
默认设置只影响之后新建的节点和隧道,不会静默改写已有项目。
|
||
严格校验和 `accept-new` 都显式使用同一个 known_hosts 文件,默认是 `~/.ssh/known_hosts`;
|
||
可通过 `KLINK_KNOWN_HOSTS` 指定其他路径。SSH、SFTP、隧道和 Settings 中的 Host Key 操作
|
||
都会使用这一路径。
|
||
|
||
### SFTP 页面
|
||
|
||
SFTP 页面左侧浏览本地文件,右侧浏览所选节点的远程文件。宽度不足时只显示当前焦点面板,
|
||
使用 `←/→` 或 `h/l` 切换本地和远程面板。
|
||
按 `n` 打开节点选择框,使用 `↑/↓` 或 `j/k` 选择节点并按 `Enter` 连接;当前节点名称显示在远程面板标题中。
|
||
按 `g` 可输入路径并跳转当前面板,相对路径以当前目录为起点,本地和远端都支持 `~` 家目录写法。
|
||
上传或下载开始后会显示进度、实时速度和已传输大小。使用 `←/→` 或 `h/l` 选择“取消”或“隐藏”,
|
||
按 `Enter` 执行;`Esc` 可直接隐藏。隐藏不会中断传输,按 `t` 打开任务列表后可重新查看排队中、运行中、已完成、失败或已取消的任务。
|
||
传输按加入顺序串行执行;任务列表中选择失败或取消的任务后按 `r` 会使用 klink 管理的 partial 文件断点续传。
|
||
|
||
| 按键 | 操作 |
|
||
| --- | --- |
|
||
| `↑/↓` 或 `j/k` | 移动文件选择 |
|
||
| `Tab` / `Shift+Tab` | 切换下一个/上一个主页标签 |
|
||
| `←/→` 或 `h/l` | 聚焦本地/远程面板 |
|
||
| `Enter` | 进入目录;选择文件时上传或下载 |
|
||
| `Backspace` / `Esc` | 返回当前面板的上级目录 |
|
||
| `u` | 上传左侧选中的文件或目录 |
|
||
| `d` | 下载右侧选中的文件或目录 |
|
||
| `f` | 在当前面板新建空文件 |
|
||
| `m` | 在当前面板新建目录 |
|
||
| `e` | 重命名当前文件或目录 |
|
||
| `x` | 确认后递归删除当前文件或目录 |
|
||
| `g` | 输入绝对或相对路径并跳转当前面板 |
|
||
| `r` | 刷新两侧目录 |
|
||
| `n` | 打开包含全部节点的选择框 |
|
||
| `t` | 打开 SFTP 传输任务列表 |
|
||
| 任务列表 `r` | 重试失败或已取消任务并断点续传 |
|
||
|
||
新建和重命名不会覆盖同名目标,删除目录前必须确认。文件夹会递归传输,目标已存在时必须确认后才会覆盖;文件先写入临时路径,完成后再重命名,
|
||
避免中断后留下看似完整的文件。启用断点续传的任务会保留带传输身份后缀的 `.klink-part-*` 文件;
|
||
下载任务同时保存只含传输身份、大小和 SHA256 的 sidecar 元数据。重试时会先校验本地 partial,
|
||
再让 SFTP 从远端对应偏移继续读取,不会重新传输已完成的前缀;成功后原子重命名为目标文件并清理元数据。
|
||
为避免密码提示破坏全屏界面,SFTP 使用非交互认证:
|
||
可以使用 SSH Agent(包括 Bitwarden SSH Agent)、无需交互输入口令的私钥,或节点中已保存的密码。
|
||
密码节点没有已保存凭据时,SFTP 会给出错误并要求先编辑节点。符号链接不会被传输。
|
||
文件操作完成后仅刷新受影响的面板;刷新中的列表会暂时显示加载状态,另一侧面板仍可继续浏览。
|
||
远端目录列表会在当前 klink 进程内按节点、连接地址和路径缓存。再次进入已访问目录或离开后返回
|
||
SFTP 页面时会直接使用缓存;远端标题会显示 `cached`。上传和远端增删改会自动失效相关缓存,
|
||
按 `r` 可忽略缓存并立即从服务器重新同步。缓存不会写入磁盘,退出 klink 后自动清除。
|
||
|
||
从 Nodes 页面或 `klink connect` 启动 SSH 时,klink 会在 OpenSSH 接管终端前显示 Logo 和感谢语。
|
||
|
||
## 发布与应用内更新
|
||
|
||
推送 `v*` tag 会触发 `.gitea/workflows/release.yml`,使用 GoReleaser 构建 Linux、macOS、Windows
|
||
的 amd64/arm64 压缩包以及 `checksums.txt`。仓库 Actions Secret 中需要配置具有 Release 写权限的 `GITEA_TOKEN`。
|
||
|
||
```sh
|
||
git tag -a v0.1.0 -m "klink v0.1.0"
|
||
git push origin v0.1.0
|
||
```
|
||
|
||
本地构建也可以注入版本信息:
|
||
|
||
```sh
|
||
make build VERSION=0.1.0 COMMIT=local BUILD_DATE=2026-07-19
|
||
```
|
||
|
||
发布前建议执行 `make check`,并确认 Gitea Actions 中的 Release 工作流成功上传所有平台附件和
|
||
`checksums.txt`。首次发布完成后,上面的安装命令即可直接使用。
|
||
|
||
## 端口转发
|
||
|
||
隧道专用模式开启自动重连后,会在网络故障或服务器重启后持续尝试恢复,退避时间从 1 秒
|
||
逐步增加并封顶为 32 秒;手动停止会立即取消等待。认证失败、Host Key 失败等不可恢复错误
|
||
仍会直接进入失败状态,避免无意义地重复认证。
|
||
|
||
本地数据库转发:
|
||
|
||
```sh
|
||
klink tunnel add database \
|
||
--node Tokyo \
|
||
--type local \
|
||
--bind 127.0.0.1:5432 \
|
||
--target db.internal:5432 \
|
||
--reconnect
|
||
```
|
||
|
||
远程转发:
|
||
|
||
```sh
|
||
klink tunnel add webhook \
|
||
--node Tokyo \
|
||
--type remote \
|
||
--bind 127.0.0.1:8080 \
|
||
--target localhost:3000
|
||
```
|
||
|
||
动态 SOCKS5 代理:
|
||
|
||
```sh
|
||
klink tunnel add socks-dev \
|
||
--node Tokyo \
|
||
--type dynamic \
|
||
--bind 127.0.0.1:1080
|
||
```
|
||
|
||
管理运行状态:
|
||
|
||
```sh
|
||
klink tunnel list
|
||
klink tunnel start database --dry-run
|
||
klink tunnel start database --background
|
||
klink tunnel stop database
|
||
klink tunnel restart database
|
||
```
|
||
|
||
一个配置可以包含多条转发规则:
|
||
|
||
```sh
|
||
klink tunnel forward add database \
|
||
--type local \
|
||
--bind 127.0.0.1:6379 \
|
||
--target redis.internal:6379
|
||
|
||
klink tunnel forward remove database 2
|
||
```
|
||
|
||
本地和动态转发默认绑定 `127.0.0.1`。绑定 `0.0.0.0`、`::` 或其他非回环地址时,
|
||
启动命令会拒绝继续;确认风险后显式传入 `--allow-public`。远程公开监听还取决于 SSH
|
||
服务器的 `GatewayPorts` 配置。
|
||
|
||
后台隧道和 TUI SFTP 依赖非交互式认证,可以使用 SSH Agent、无需手工输入口令的私钥,
|
||
或节点中已保存到系统凭据存储的密码。运行日志和 PID 状态保存在配置目录的 `runtime/` 子目录中,
|
||
权限为当前用户私有。Windows 后台 worker 使用脱离控制台的进程、命名停止事件和 Job Object;
|
||
关闭 klink 不会中断 Tunnel,停止时会优雅取消 SSH,强制终止也会清理整个 SSH 子进程树。
|
||
|
||
## 导入和导出
|
||
|
||
### 从 ktui 同步
|
||
|
||
在 TUI 的 Settings 页面选择 `Sync from ktui` 并按 `Enter`,或者直接运行:
|
||
|
||
```sh
|
||
klink sync ktui
|
||
```
|
||
|
||
klink 会执行:
|
||
|
||
```sh
|
||
ktui export json
|
||
```
|
||
|
||
然后根据 ktui UUID 同步节点名称、IPv4、IPv6、区域、分组和标签。已有节点会保留 klink 中的
|
||
SSH 用户、认证方式、密码凭据关联、密钥、端口、Host Key 策略、ProxyJump、连接记录和隧道引用;
|
||
只对首次出现的新节点应用当前默认 SSH 设置。重复执行同步不会创建副本。
|
||
|
||
如果旧版本导入的节点没有稳定 UUID,klink 只会在带有 `ktui-` ID 或 `Imported from ktui`
|
||
标记的节点中按名称或已有 IP 迁移匹配,不会覆盖同名的手工节点。ktui 中已经消失的节点不会
|
||
被自动删除,避免连带删除本地隧道或遗留凭据,需要时可在 Nodes 页面手工确认后删除。
|
||
|
||
也可以先保存 ktui 导出文件,再通过 `Import configuration` 或命令行同步:
|
||
|
||
```sh
|
||
ktui export json -o ktui-nodes.json
|
||
klink import ktui-nodes.json
|
||
```
|
||
|
||
首次同步后在 Nodes 页面选择新节点并按 `e` 填写 SSH `User`,再选择 Agent、密钥或密码认证。
|
||
保存的密码只进入系统凭据存储,不会出现在 klink JSON、导出文件或进程参数中。
|
||
|
||
同步 ktui JSON 或导入 klink JSON:
|
||
|
||
```sh
|
||
klink import nodes.json
|
||
```
|
||
|
||
导入 OpenSSH 配置:
|
||
|
||
```sh
|
||
klink import ~/.ssh/config --format ssh-config
|
||
```
|
||
|
||
只有具体的 `Host` 别名会被转换为节点。`Host *`、通配规则和 Match 块不会展开成节点;
|
||
导入结果会逐项报告跳过内容和名称/ID 冲突。
|
||
|
||
导出包含全局设置、节点和隧道的版本化 JSON:
|
||
|
||
```sh
|
||
klink export klink-backup.json
|
||
klink export -
|
||
```
|
||
|
||
导出只包含节点的认证方式和私钥路径,不包含系统凭据存储中的密码。将备份导入另一台设备后,
|
||
密码节点需要重新输入并保存密码。
|
||
|
||
## 配置与安全
|
||
|
||
查看配置路径:
|
||
|
||
```sh
|
||
klink config path
|
||
```
|
||
|
||
可以用全局参数或环境变量指定其他配置:
|
||
|
||
```sh
|
||
klink --config ./demo.json list
|
||
KLINK_CONFIG=./demo.json klink list
|
||
```
|
||
|
||
- 配置文件以 `0600` 权限原子写入
|
||
- 密码和私钥内容不会写入配置;只保存私钥路径
|
||
- 默认使用严格 HostKey 校验
|
||
- `accept-new` 允许首次写入主机密钥
|
||
- `insecure` 会禁用 HostKey 校验,仅应在明确理解风险时使用
|
||
- OpenSSH 参数通过结构化参数数组执行,不经过 shell 插值
|
||
|
||
## 许可证
|
||
|
||
klink 使用 [MIT License](LICENSE)。
|