Files
klink/README.md
T
gary 166ffdecbf feat: expand SSH management and cross-platform support
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.
2026-07-20 17:46:02 +08:00

398 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)。