把 SSH 工作台搬上全球边缘网络:深度拆解 ternssh —— 基于 Cloudflare Workers + Durable Objects 的免客户端 Web 终端与 SFTP 文件系统(纯 TypeScript 协议栈/零信任安全/全景实战)

把 SSH 工作台搬上全球边缘网络:深度拆解 ternssh —— 基于 Cloudflare Workers + Durable Objects 的免客户端 Web 终端与 SFTP 文件系统(纯 TypeScript 协议栈/零信任安全/全景实战)

如果你是一位经常需要在多台 VPS、跨国云主机之间来回穿梭的开发者或 SRE,你一定经历过这样的窘境:在没有随身携带主力工作本的紧急时刻,或是受制于严苛的公司内网环境,想要连上远程服务器排查故障,通常只有两个选择——要么临时下载配置 SSH 客户端和私钥,要么自建一套像 JumpServer、Apache Guacamole 或 Next Terminal 这样功能完备的 Web 堡垒机。

然而,传统的 Web SSH 方案存在着不可回避的痛点:它们通常是基于 Python、Go 或 Java 编写的常驻服务,动辄占用数百 MB 甚至上 GB 内存;必须暴露公网 HTTP/WebSocket 端口,极易成为全网扫描器挖掘 0day 漏洞的靶子;而在轻量化方面,大多数简易网页终端又缺乏趁手的 SFTP 文件管理与实时系统监控能力。

最近开源的 ternssh(GitHub: HaradaKashiwa/ternssh)提供了一种颠覆性的全新解法:直接将完整的 SSH 工作台、SFTP 文件管理器与监控仪表盘搬上了 Cloudflare Edge 边缘网络。它不仅做到了零服务器常驻(Serverless)、全球 Anycast 就近接入,更在底层硬核实现了一套无原生 C++ 依赖的纯 TypeScript SSH2/SFTP 协议栈。

本文将以第一视角带你深入拆解 ternssh 的系统架构、协议穿透黑科技、零信任安全防线以及从 Workers 到 Docker 的落地实战。


1. 为什么我们需要边缘原生的 Web SSH?

在深入代码之前,我们先梳理一下开发者在远程连接工具链上的核心矛盾:

维度 本地桌面客户端 (Termius / Warp) 传统 Web 堡垒机 (JumpServer / Guacamole) ternssh (Cloudflare Workers)
设备便携性 差(需逐台设备安装配置、同步私钥) 优(任意浏览器即开即用) 极致(任意现代浏览器,全球秒级加载)
服务器常驻资源 无(运行在客户端) 高(常驻 512MB~2GB 内存,容器长期占用) 零成本(Edge Serverless 纯按量计费,无常驻消耗)
网络延迟表现 取决于本地到目标机路由 取决于中转堡垒机节点 全球 Anycast(通过 Cloudflare 边缘骨干网就近建连)
公网暴露攻击面 仅开放目标机 SSH 端口 开放中转机 Web 端口与数据库,需打补丁 收敛至 Cloudflare Access,支持硬件 Key 零信任 2FA
运维功能完整性 高(终端 + SFTP + 端口转发) 高(偏向权限审计,稍显臃肿) 轻巧聚焦(终端 + 拖拽看板 + SFTP + 在线编辑器)

对于个人开发者、独立黑客以及小型 DevOps 团队而言,花费不菲的服务器预算去长期维护一台仅在紧急时使用的堡垒机,显然是不划算的。而基于 Cloudflare Workers 免费配额即可运转的 ternssh,恰好切中了这个“轻量、高可用、高安全”的黄金平衡点。


2. 核心架构拆解:V8 沙箱中的 SSH2 协议穿越

许多熟悉 Cloudflare Workers 的同学第一反应可能是:“Workers 不是运行在 Google V8 Isolate 隔离沙箱里吗?它怎么可能建立 TCP 连接去跑 SSH 协议?”

这正是 ternssh 最令人惊艳的技术突破点。

2.1 全链路拓扑图

flowchart TD
    subgraph Browser ["客户端浏览器 (React 19 + Tailwind v4)"]
        UI["可拖拽仪表盘 Dashboard"]
        XTerm["xterm.js 虚拟终端 (Canvas/WebGL)"]
        CM["CodeMirror 6 远程文件编辑器"]
        SFTPUI["SFTP 树状文件树 & 上传管理器"]
    end

    subgraph CF_Edge ["Cloudflare Global Network (Edge Workers)"]
        Access["Cloudflare Access (Zero Trust JWT 鉴权)"]
        Router["Worker Entry (server/src/index.ts)"]
        D1[("Cloudflare D1 SQLite 数据库\n存储主机配置/密钥/面板状态")]
        DO["Durable Objects (SshSession 单例持久连接)"]
        SocketAPI["cloudflare:sockets API (原生 TCP 套接字)"]
    end

    subgraph Target ["远端目标服务器 (Any VPS / Cloud)"]
        SSHPort["sshd (Port 22 / TCP)"]
        Proc["/proc 系统指标采样"]
        FileSystem["Linux 文件系统 (SFTP Subsystem)"]
    end

    UI -->|"HTTPS 静态请求"| Router
    XTerm -->|"WebSocket 双向终端流"| Access
    SFTPUI -->|"WebSocket SFTP 分片流"| Access
    CM -->|"文件热保存"| SFTPUI
    Access -->|"JWT 验签通过"| Router
    Router -->|"路由委派"| DO
    Router -->|"读写凭证与设置"| D1
    DO -->|"创建 TCP 套接字"| SocketAPI
    SocketAPI -->|"原始 TCP SSH2 流量"| SSHPort
    SSHPort -->|"探针定时读取"| Proc
    SSHPort -->|"SFTP 子系统读写"| FileSystem

2.2 核心硬核点:为什么手搓纯 TypeScript SSH 栈?

在传统 Node.js 环境下,做 SSH 相关的工具几乎 100% 依赖著名开源库 ssh2。但 ssh2 依赖了底层 C++ 绑定的原生加密库以及 Node.js 的标准 net.Socket。在 Cloudflare Workers 的纯 V8 沙箱里,这些原生编译模块完全无法载入。

ternssh 作者 Harada Kashiwa 在 server/src/ssh/ 下直接纯手写了一整套 SSH 协议的核心实现:

  1. 网络传输层 (transport.ts):基于 Cloudflare 官方提供的 import { connect } from 'cloudflare:sockets',在边缘节点与目标服务器建立原始双向 TCP 流。
  2. 密钥交换与加密 (kex.ts, packet.ts):完全基于浏览器的标准 WebCrypto API(crypto.subtle),实现了 Curve25519、ECDH-SHA2-NISTP256、Diffie-Hellman 等密钥交换算法,以及 AES-128-GCM、AES-256-GCM、ChaCha20-Poly1305 等对称加解密与 MAC 校验。
  3. 状态机与会话保持 (session.ts):处理 SSH 认证报文(Password / Public Key 签名)、Channel 打开、PTY 伪终端请求(pty-req)、窗口缩放尺寸同步(window-change)以及心跳保持。
  4. SFTP 子系统完整解析 (sftp.ts, sftp-handler.ts):在已经建立的 SSH Channel 上封装 SFTP 二进制协议头,实现 SSH_FXP_OPENSSH_FXP_READSSH_FXP_WRITESSH_FXP_STAT 等状态码,支持文件流式读取与写入。

2.3 Durable Objects 的中枢协同

SSH 会话是有状态的长连接,而标准的 Cloudflare Worker 是无状态、随用随起的短生命周期函数。为了保证终端输入输出不会因为 Worker 调度而中断,ternssh 采用了 Durable Objects (SshSession)

sequenceDiagram
    autonumber
    participant Client as 浏览器 (xterm.js)
    participant Worker as Worker Gateway
    participant DO as Durable Object (SshSession)
    participant Target as 目标主机 (Port 22)

    Client->>Worker: 发起 WebSocket 升级握手 (/api/ws/ssh?session_id=xxx)
    Worker->>DO: 转发 WebSocket 至对应 Durable Object ID
    DO->>DO: 校验会话 Token 与本地 SQLite 锁
    DO->>Target: connect({ hostname, port }) 建立 TCP
    Target-->>DO: SSH-2.0 握手报文
    DO->>Target: 纯 TS 执行 KEX & WebCrypto 密钥协商
    DO->>Target: 发送 UserAuth (公钥/私钥签名)
    Target-->>DO: SSH_MSG_USERAUTH_SUCCESS
    DO->>Target: 请求 pty-req & shell
    DO-->>Client: WebSocket 建立完毕,流式输出欢迎横幅
    
    rect rgb(30, 40, 60)
        Note over Client,Target: 全双工双向低延迟流转
        Client->>DO: 键盘击键数据 (WebSocket Frame)
        DO->>Target: 加密 SSH 数据包 (TCP Socket)
        Target-->>DO: 解密回显终端字符
        DO-->>Client: xterm.js 绘制 ANSI 字符
    end

这种架构使得无论用户在世界的哪一个角落发起连接,Cloudflare Anycast 都会将前端就近路由,并在最靠近目标机器的 Edge 机房实例化 Durable Object,极大地缩短了 RTT(往返时延)。


3. 功能亮点:不仅是终端,更是现代化工作台

ternssh 并没有停留在“网页敲命令”的初级阶段,而是打造了一整套现代化的运维工作台:

3.1 拖拽式系统监控仪表盘

  • 在连接服务器后,ternssh 可以通过轻量后台探针定期采样远端服务器的负载(Load Average)、CPU 使用率、内存占比、磁盘使用空间与网络 I/O。
  • 前端使用基于 React 19 的网格布局系统,仪表盘卡片支持自由拖拽、缩放、组合,无论是竖屏监控还是多主机并排巡检都能轻松应对。

3.2 基于 CodeMirror 6 的远端热重载编辑器

  • 过去在 Web 终端里修改 Nginx 配置或 Docker Compose 文件,必须忍受在低延迟网络下用 Vim 盲打的痛苦。
  • ternssh 内置了强大的 CodeMirror 6 编辑器,支持 JSON、YAML、Nginx、Shell、Python、Rust 等数十种主流语言的语法高亮与行号显示。
  • 在左侧 SFTP 树状列表中双击文件,即可直接在右侧选项卡唤起编辑器;按下 Cmd + S / Ctrl + S,系统自动通过 SFTP 协议增量保存并回写远端。

3.3 零信任防护:Cloudflare Access 原生防线

  • 传统的开源 Web SSH 最怕密码被爆破或服务代码暴露漏洞。
  • ternssh 深度集成了 Cloudflare Zero Trust 的 Access 鉴权机制。在环境变量中配置 ACCESS_TEAM_DOMAINACCESS_AUD 后,Worker 会在入口强制校验 HTTP 请求头中的 Cf-Access-Jwt-Assertion
  • 只有通过了企业 SSO(如 Google Workspace、GitHub Org、Okta)并且满足设备证书/硬件 Key 策略的用户才能接触到 WebSocket 入口,实现了真正的“零暴露、零公网密码渗透”。

4. 生产环境实战部署方案

ternssh 提供了两种极具弹性的部署途径:Cloudflare 边缘云端部署本地/私服 Docker 部署

方案一:Cloudflare Workers 边缘零成本极速部署(推荐)

  1. 克隆项目源码并安装依赖

    git clone https://github.com/HaradaKashiwa/ternssh.git
    cd ternssh
    npm install
    
  2. 准备 D1 数据库与配置文件

    # 拷贝生产配置模版
    cp wrangler.production.jsonc.example wrangler.production.jsonc
    
    # 在 Cloudflare 创建 D1 数据库
    npx wrangler d1 create ternssh-db
    

    将终端输出的 database_id 回填到 wrangler.production.jsoncd1_databases 节点中。

  3. 构建前端并执行边缘发布

    # 构建 React 19 前端静态资源
    npm run build
    
    # 应用 D1 远程数据库迁移
    npm run db:migrate
    
    # 一键推送到 Cloudflare 全球网络
    npm run deploy
    
  4. 绑定 Cloudflare Access(安全必备)
    在 Cloudflare Zero Trust 控制台创建一个 Access Application,指向你的 Worker 自定义域名(如 ssh.yourdomain.com),并将生成的 Application AUD 与 Team Domain 写入环境变量:

    npx wrangler secret put ACCESS_TEAM_DOMAIN
    npx wrangler secret put ACCESS_AUD
    

方案二:Docker / 本地私有化快速启动

如果你的服务器处于没有外网或严格合规的内网局域网,ternssh 官方镜像也打包了完整的单机本地运行环境(底层基于 Wrangler 本地沙箱模拟 D1 与 DO):

# docker-compose.yml
services:
  ternssh:
    image: ghcr.io/haradakashiwa/ternssh:latest
    container_name: ternssh
    restart: unless-stopped
    ports:
      - "8787:8787"
    volumes:
      - ternssh-data:/app/.wrangler
    environment:
      # 可选:如果前端挂了反向代理,可配置 Access JWT 校验
      - ACCESS_TEAM_DOMAIN=
      - ACCESS_AUD=

volumes:
  ternssh-data:

一键启动:

docker compose up -d

启动后直接访问 http://localhost:8787 即可初始化管理面板。


5. 深度实测心得与踩坑避险指南(Gotchas)

在深入试用与审查代码后,有以下几个核心细节值得在生产落地时重点关注:

[!WARNING]
1. Cloudflare Workers TCP Sockets 连接限制

  • Cloudflare 的 cloudflare:sockets 现阶段仅支持对标准非保留公网 IP 和域名的 TCP 连接,无法直接连接 127.0.0.1 或内网私有网段(如 192.168.x.x)。若需穿透内网,目标主机前置需要暴露公网入口或通过 Cloudflare Tunnel / 端口转发。
  • 在 Cloudflare Free 计划下,TCP 连接受到并发连接数限制;同时如果长达 100 秒没有任何数据包往返,边缘节点会主动切断 Socket。避坑方案:建议在目标主机的 /etc/ssh/sshd_config 中配置 ClientAliveInterval 30ClientAliveCountMax 3,由服务端定时发送心跳空包防超时。

[!TIP]
2. 首次连接信任(TOFU)与公钥指纹
ternssh 在首次连接目标主机时,会将目标服务器的 Host Key 指纹记录在 Cloudflare D1 数据库中。如果远端服务器重装或被更换,前端会弹出安全告警阻断连接,有效防止了中间人攻击(MITM)。在迁移服务器时如果遇到认证失败,只需进入主机管理面板刷新指纹缓存即可。

[!NOTE]
3. 大文件传输的内存天花板
Cloudflare Worker 单个请求的内存上限通常为 128MB。ternssh 在 SFTP 上传与下载时采用了分片流式管道设计(Chunk Streaming),避免一次性将上百兆文件载入内存。但在网络抖动环境下传输超大文件(>500MB),仍建议优先使用命令行 rsync,将 Web SFTP 留给代码配置文件与日志的高频调阅。


6. 总结与展望

HaradaKashiwa/ternssh 是一次非常大胆且优雅的技术实践。它证明了:

  1. Serverless 与边缘计算的边界正在急剧扩大:从最初简单的 HTTP API、SSR 网页渲染,到现在完全可以用纯 TypeScript 实现复杂的双工长连接网络协议栈(如 SSH2/SFTP)。
  2. 零信任基础设施的普及化:借助 Cloudflare 强大的免费生态矩阵(Workers + D1 + Durable Objects + Access),个人开发者和初创团队不需要购买额外服务器,就能在 5 分钟内搭建一套比肩工业级安全的全球可访问运维中枢。

如果你正好在寻找一款无需常驻开销、随时随地在浏览器中开箱即用的轻量 SSH 工作台,ternssh 绝对值得你立刻部署一试。