Skip to content

feat(cluster): 扩展节点直连通道(kin-slot-gate,TLS),槽位请求不再挤在 SSH 长连接里 - #614

Merged
dofastted merged 1 commit into
dofastted:mainfrom
Niutopia:feat/cluster-direct-link
Oct 19, 2026
Merged

dofastted merged 1 commit into
dofastted:mainfrom
Niutopia:feat/cluster-direct-link

Conversation

@Niutopia

Copy link
Copy Markdown
Contributor

摘要

扩展节点上的远程槽,所有 kernel / worker 请求都在控制面到节点的一条 SSH 长连接里各开一个通道。实测(美国控制面 → 日本节点,RTT 约 120ms)这条长连接平时只有零碎小包,偶尔一个几百 KB 的请求正文,拥塞控制把它的发送速率压在约 17KB/s(ss -ti:bbr bw 110–140kbps、cwnd 8),而同一线路新建连接下载能跑到 340–370KB/s。整台节点的请求都排在这条连接后面:同为 Opus、缓存命中 ≥90% 的请求,首字中位数本地槽 4.2s、远程槽 12.8s。集群页的“链路延迟”也因此在 120ms 和 1–2s 之间跳。

本 PR 加一条可选的直连通道:节点上跑一个很小的 kin-slot-gate,控制面对每个槽位请求新建一条 TLS 连接直达它,每条连接各自从慢启动开始,不再共用一个被压低的拥塞窗口。Docker、终端、健康检查仍走 SSH;直连不通时自动退回 SSH。

变更

  • 新 Go 命令 worker/cmd/kin-slot-gate(internal/slotgate,只用标准库)
    • TLS 1.3;首次启动自签 P-256 证书写入 tls_dir(私钥 0600),重启沿用。
    • 前言 KSG1 <token> <vm> <kernel|worker|ping>\n,5 秒内完成握手+前言;令牌按 SHA-256 常量时间比较。
    • 只连 <root>/vms/<vm>/run/{kernel,worker}.sock;vm id 白名单;最后一段以 O_PATH|O_NOFOLLOW 打开、确认是 socket 后经 /proc/self/fd 连接,不跟随槽内植入的符号链接,也没有二次路径查找。
    • 双向转发并传递半关闭;任一侧出错两边都关;最多 512 并发。
  • 控制面
    • src/lib/cluster/direct-link.mjs:在节点上建 / 刷新 kin-slot-gate 容器(宿主网络、只读根、CapDrop ALL,root 登录时只留 DAC_OVERRIDE;<root>/vms 只读挂载,仅 gate/tls 可写),借用节点上已有的槽位镜像运行静态二进制;容器 spec 摘要不变则不动。
    • 证书固定:经已固定主机密钥的 SSH 读取 gate 证书并保存 SHA-256;连接时指纹不符就不发令牌、退回 SSH。
    • 请求正文随前言一起发出、不等 gate 应答(gate 缓存在前言之后),回包开头的 OK 由控制面剥掉;建连只比 SSH 通道多一个 TLS 往返。
    • SocketRelay 增加可选 dialDirect:直连成功就用直连,否则走原来的 direct-streamlocal;槽 socket 不存在时与原来一样直接断开本地连接。
    • 连不上、证书不符、令牌被拒时该节点退回 SSH 30 秒;健康检查 ping 成功即恢复。
    • 新表 cluster_node_direct_links(迁移 039,ON DELETE CASCADE),令牌经 maybeEncrypt;令牌和指纹不出现在任何响应里。
    • PUT / DELETE /api/panel/cluster/nodes/:id/direct-link({ address, port? },只收 IP 字面量,端口默认 7790)。
  • 面板:节点详情新增「直连通道」(启用 / 修改 / 关闭,地址默认取节点 SSH 地址),节点行显示「直连」/「直连→SSH」。
  • 构建 / 发布:npm run build:gate;Dockerfile、arm64 Dockerfile(节点用 amd64:KIN_CLUSTER_GATE_BIN)、入口 ensure_bin、Release 附件 kin-slot-gate、.gitattributes、docs/BUILD.md、docs/PANEL_API.md、CHANGELOG「未发布」。

自检

  • go vet / gofmt / go test -race:internal/slotgate 6 项(TLS 转发与半关闭、坏前言 / 错令牌 / 路径越界 / 缺 socket、符号链接不跟随、证书重启不变且丢失后重建、明文客户端拿不到 OK、配置校验),另编成 linux/arm64 在 Linux 容器里跑过同一组(覆盖 O_PATH 分支)。
  • test/unit/cluster-direct-link.test.mjs 7 项:TLS 假 gate 下正文先于 OK 发出且 OK 被剥掉、证书不符时零字节发出、拒绝码与超时、容器 spec(权限、可写目录)、SocketRelay 直连 / 回退 SSH / 缺 socket、令牌与指纹在编辑后保留且不外泄、各种失败的退避与 ping 恢复、删除节点级联。cluster-node-edit.test.mjs 的测试库补跑 039。
  • 端到端:真实 kin-slot-gate 二进制 + 控制面 SocketRelay,5MB 正文经 TLS 半关闭完整送达;错令牌 gate_auth、路径越界 gate_bad_request、错指纹 gate_cert_mismatch。
  • test/unit 全量:本分支与 main 失败的 13 个文件名单完全相同(本地 macOS 环境),其余全过;web format:check / tsc -b / eslint / vitest(459 项)全过;biome 无改动。
  • 已合入我们生产 fork 并在本机完成验收(Linux 全量单测 3350 项 0 失败、启动审计确认只新增这一张表、回滚到旧版本可打开同一个库);线上实测数据上线后补在评论里。

未做

  • 不做 WireGuard / VPC 依赖:最初想走私网,但这条线路节点→控制面的 UDP 全端口不通,所以改成公网 TLS。私网同样可用(填私网 IP 即可)。
  • 节点防火墙不由控制面修改:文档写明应只对控制面出口 IP 放行该端口(令牌之外的第二道门)。
  • 没有连接池或 TLS 会话复用;Node 默认 agent 空闲 5 秒关闭连接,不会再形成常驻长连接。

@dofastted
dofastted merged commit 5c30b8d into dofastted:main Oct 19, 2026
5 checks passed
@dofastted dofastted mentioned this pull request Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants