自建Web SSH:用Termix把SSH会话搬进浏览器,随时随地远程运维

发布时间:2026/10/23 19:17:36
自建Web SSH:用Termix把SSH会话搬进浏览器,随时随地远程运维

说个最近的遭遇:人在外面,手机上收到服务器告警,磁盘快满、日志刷屏、进程被挤出。我身边没电脑,手机上的终端客户端在那种情况下根本用不上。折腾了半天,我下决心给自己搭一套能放在浏览器里、随时随地打开就能连的 Web SSH。最后配合 Termix 把 SSH 会话桥接到了网页端,整个过程踩了不少坑。这篇是完整记录:想做这件事的原理、部署方式、安全加固和排错经验都在里面。适合跟我一样经常不在电脑前的人,也适合正在选型的小团队运维同学参考。

1. 为什么我最后选择自建 Web SSH,而不是硬凑合

1.1 传统 SSH 在"移动场景"下的四个尴尬

我不是说命令行 SSH 不好,它在正经办公环境里无可替代。但"随时随地管服务器"这六个字,直接把传统方式的脸打肿了:

第一,客户端依赖。公司电脑、客户现场的机器、朋友借的笔记本,不可能每台都装好 SSH 客户端。遇到 Windows 默认环境更强,命令端都没法直接用。手机端的 SSH 应用倒是不少,可屏幕小、键盘输入难受、复制粘贴还经常乱套。

第二,网络环境限制。很多访客网络只放行 80 和 443 端口,SSH 默认的 22 端口直接被掐死。有的公司出口代理还要审计流量,非标准端口很容易触发告警。

第三,多台机器管理碎片化。我得同时维护好几台服务器,IP、端口、密钥、用户名,光这些信息就够记一堆。没有一个统一的入口,每次都是临时去翻记录,效率极低。

第四,会话稳定性。网络一波动,SSH 会话断掉,正在跑的任务可能跟着遭殃。虽然进程层面有 tmux、screen 顶着,但你在移动端重新连回去的体验依然不痛快。

1.2 Web SSH 的适用人群与场景边界

这套方案不是要取代桌面 SSH,它解决的是"轻量、临时、跨设备"的问题。最适合三类人:

  • 个人开发者 / 独立站长:服务器不多,但经常外出,需要快速看一眼服务状态、重启容器、看日志,浏览器就能解决。
  • 小团队运维:成员需要共用一个管理入口,但不想给每个人都开服务器系统账号,又想留操作记录。
  • 混合环境使用者:公司内网有一批机器,家里有一批机器,想要一个统一的管理页面。

不适合谁?如果你的运维流程已经重度依赖堡垒机、审计系统,或者有严格的等保需求,那就没必要自建,直接走企业级方案。Web SSH 适合"够用就好"的人。

1.3 别人家的方案 vs 自己搭

市面上现成的方案并不少,云厂商控制台里基本都有网页终端,也有在线 SSH 工具网站。但我个人不愿意接受两个限制:一是账号系在特定平台上,换一家云厂商、换一台非公有云机器就用不了;二是中间链路自己不可控,在线工具相当于把服务器会话交给了第三方,安全性我说服不了自己。

Termix 这种自建方案最大的价值就是自主权:会话数据跑在自己控制的链路上,认证规则自己定,日志自己留。虽然搭建花费一些时间,但长期用下来很值。

2. Termix 的工作机制:SSH 是怎么被"塞进"浏览器的

2.1 整体结构:三个角色各司其职

在真正动手之前,我建议先花十分钟理解这套东西的运行逻辑。用一句话概括:浏览器负责显示终端界面,服务端负责代替你完成 SSH 协议通信,两者之间用实时通道连接起来。

拆开看分成三层:

  • 前端层:浏览器里跑的终端模拟组件,负责把字符流渲染成一个可交互的终端窗口,同时把键盘输入捕获并发送出去。
  • 桥接层:也就是 Termix 服务端本身,接收浏览器的实时连接请求,解析会话指令,然后在服务器本地创建一个真正可用的 SSH 会话。
  • 目标层:你要管理的目标服务器,Termix 服务端以普通 SSH 客户端的方式连接它。

整个过程里,浏览器并没有直接跟目标服务器建立 SSH 连接,它只跟 Termix 服务通信。

2.2 一次会话的完整数据流

我画一下我在脑子里理的流程,对这个部分新手容易晕:

  1. 你在浏览器里打开 Termix 页面,先做登录认证,服务端发给你一个有效的会话令牌。
  2. 你在页面里输入目标服务器的地址、端口、用户名,或者选择一个预配置的主机。
  3. Termix 服务端收到请求后,校验权限,然后替你执行一次 SSH 连接。
  4. 连接建立后,目标服务器的 stdout 输出被编码成数据帧,通过实时通道推给浏览器。
  5. 浏览器里的终端模拟组件把数据帧还原成文字和颜色,显示在页面上。
  6. 你在页面敲下的每个按键,也被封装成数据帧回传到服务端,服务端把它写入 SSH 会话的标准输入。

实际用下来,感觉就和本地终端几乎一样。

2.3 为什么要用 WebSocket 而不是普通 HTTP 请求

这里解释一个关键选型点。SSH 会话是双向、持续、实时的通信,每个按键都要很快响应,服务端的输出也要连续推给浏览器。如果用普通 HTTP 轮询,你每敲一个字符就要发一次完整请求,服务端有输出还要另外拉一次,延迟和资源消耗都受不了。

Termix 的桥接通道用的是 WebSocket。它的核心优势是:建立一次连接,之后双向推送数据,不需要反复握手,也不存在短轮询的延迟问题。体验上接近原生 SSH,能做到输入即时回显、输出持续滚动。在反向代理层处理的关键就是把这个 WebSocket 升级请求正确放行,后面第五节我会详细讲踩坑过程。

2.4 会话管理与终端类型协商

还有一个容易被忽略的细节是终端类型协商。传统 SSH 会自动协商一个终端类型,比如xterm-256color,影响颜色显示和快捷键行为。Web SSH 同样要做这个事情,Termix 在建立会话时会带上预设的终端类型,保持颜色主题、光标定位、快捷键组合能正常工作。如果协商不对,最常见的症状是top、htop的界面刷不出来,或者 vim 里的方向键变成字符A、B、C、D。

3. 从零搭起:Termix 部署全流程记录

3.1 部署前的准备工作

先说硬件和环境需求。我用的是一台 1 核 1G 的小机器,CentOS 7 系统,内存小了点,但只跑 Termix 完全够。如果你要同时承载多人使用,建议 2 核 2G 起步。需要一个域名,用来配置 HTTPS,不然浏览器里的部分高级能力(比如剪贴板读取)会被限制。域名解析到这台机器上,并确保 80、443 端口在防火墙里放行。

注意:放行 443 端口之后,如果这台服务器本身就是你的目标机器之一,请确认你没有把 22 端口暴露到公网。Termix 服务本身应当通过系统防火墙只允许本地回环访问,真正对外暴露的只有 443。

3.2 安装与初始化配置

Termix 的安装包跟一般软件不大一样,不同版本的目录结构和命令会存在差异。下面这套步骤是我基于常见实践整理的通用流程,你拿到具体版本后要对照官方说明调整目录和配置文件路径。

# 1. 创建独立运行用户,避免以 root 身份常驻服务 useradd -m -s /bin/bash termix # 2. 切换到该用户并下载安装包,解压到指定目录 su - termix mkdir -p ~/termix tar -xzf termix-release.tar.gz -C ~/termix cd ~/termix # 3. 生成初始配置模板 ./termix init

初始化完成后,目录下会有一个termix.yaml配置模板。我个人的习惯是把配置项按三块分:监听地址、认证策略、会话上限。最开始什么都不用想复杂,只改监听地址:

server: listen: "127.0.0.1:8080" # 只监听本地回环 session_timeout: 1800 # 会话空闲超时,单位秒 auth: mode: "password" # 先用密码登录,后边再加 TOTP session_ttl: 7200 # 登录态过期时间

用./termix start启动服务,telnet 127.0.0.1 8080能通,就说明基础服务起来了。这时浏览器访问需要先想办法打通外网访问路径,推荐的做法是交给反向代理来处理。

3.3 初始化配置的"可操作"细节

我第一次搭的时候,在配置上犯过一个低级错误:把监听地址配成了服务器公网 IP。这样 Termix 服务本身直接暴露在公网,省掉反向代理的部署,看起来更简单,但后续想加 HTTPS、做访问控制就非常痛苦,而且 8080 端口很容易被扫描工具盯上。正确做法是只监听 127.0.0.1,让外部流量经过反向代理转发进来。这样 Termix 永远只面对本机的 Web 服务器,公网攻击者看不到它。

3.4 反向代理与 HTTPS 配置

我用的主机的 Web 服务器是常见开源方案,给 Termix 加一段反向代理配置。这里有个关键要求:必须放行 WebSocket 升级请求。以我的配置片段为例:

server { listen 443 ssl; server_name term.example.com; ssl_certificate /etc/ssl/term/term.example.com.pem; ssl_certificate_key /etc/ssl/term/term.example.com.key; location /termix/ { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }

如果你的 Web 服务器可以自动申请和续期 HTTPS 证书,那更容易,直接把域名指过来,配一个反向代理就好。注意http与https的自动跳转、请求体大小限制要放开,SSH 会话传输的流式数据并不大,不会被 Nginx 限制困扰,但 WebSocket 帧的读取超时要给足。默认超时经常是 60 秒,终端长时间没有输出时会被主动断开,建议把读取超时调大,例如 300 秒以上。

配好后浏览器访问https://term.example.com/termix/,看到登录页,说明整套链路已经通了。

4. 公网暴露的 Web SSH,这些安全加固必须做

4.1 我只跑了两天就被扫描了

这套服务上线后,我特意观察了几天日志。第一天就有来自不同来源的扫描请求,试图猜登录口令。这给我提了一个醒:Web SSH 是价值非常高的攻击目标,因为它一旦被攻破,等于把服务器权限交出去了。所以安全这块不是可选项,是必选项。

攻击面主要有三块:

  • 登录前:暴力破解、弱口令尝试、未知路径探测。
  • 会话中:令牌泄露、被中间人截获、多用户会话隔离不足。
  • 系统层:Termix 服务本身上线后,如果某个未知接口被调用了,安全隐患会放大。

4.2 登录认证升级:从单层密码到两层保护

密码登录在公网上撑不了多久。我的建议是至少做到两层:第一层密码验证,第二层动态验证码。Termix 支持在登录认证基础上挂载动态口令验证,我用的是常见的开源动态口令库,生成方式是基于时间的一次性密码。手机端装一个标准认证类应用,扫描 Termix 生成的二维码,以后每次登录都要输入 6 位动态码。

这样做的好处是:即使密码因为某种原因泄露,没有动态码也进不来。我还顺手做了失败次数限制:同一 IP 连续失败 5 次就临时封禁 15 分钟。这个可以在 Web 服务器层实现,也可以在 Termix 内启用,看各自版本支持情况。

4.3 IP 白名单、访问路径与来源控制

如果你的使用场景相对固定,可以在 Web 服务器层加 IP 限制。只允许公司出口 IP、家里宽带 IP 访问/termix/路径,其它来源一律返回 403。这个策略简单有效,能过滤掉 80% 的扫描流量。

如果活动范围确实不固定,至少可以设置账号级登录地点提醒:登录成功之后,把本次来源 IP 显示在页面上,发现不对立刻手动失效全部会话。

4.4 会话隔离与超时策略

多人共用这套系统时,每个用户的 SSH 会话必须相互隔离。Termix 的每个连接在服务端有独立的会话映射,互不干扰。我配置了空闲超时自动断开,默认为 30 分钟;如果 30 分钟内没有键盘输入,会话自动终止,避免人走开以后会话被接管。

注意:这里的会话超时和系统层的 idle session 不是一回事。系统层的会话可能还会因为服务端线路问题悬挂着,Web SSH 的会话超时只是指浏览器与 Termix 之间的连接。真正要杜绝悬挂连接,建议同时配合使用ClientAliveInterval这类 SSH 服务端参数。

4.5 审计日志是最容易被忽视的一环

我建议从第一天就开启操作日志。这个日志至少需要记录四个要素:谁、在什么时间、连了哪台机器、执行了什么操作。如果只记录登录成功但没记录具体命令,出问题的时候你连攻击者做了什么都不知道。

Termix 大部分版本可以配置将日志输出成文本落盘。我的做法是直接写到日志目录下,然后定期归档。终端输出和键盘输入如果都记录,日志文件会增长得很快,但如果连续记录两个月,对磁盘占用完全可接受,换来的排查能力非常值得。

风险点常见攻击方式我采用的加固措施
登录口令暴力破解、撞库密码 + 动态验证码,失败次数限制
访问来源全网扫描Web 服务器层 IP 白名单,非白名单 403 拒绝
会话劫持令牌泄露、中间人强制 HTTPS,会话空闲自动过期
越权操作账号共用、权限过大独立系统账号映射,按需分配主机权限
事后溯源证据缺失记录登录时间、来源、命令输出、目标主机

5. 实测踩坑记录:从能用到好用的关键修复

5.1 复制粘贴失灵和方向键乱码

第一次跑通之后,我用 vim 编辑文件,发现方向键每次都会插入字符A、B、C、D,复制粘贴也完全不受控。一开始以为是 Termix 的问题,排查之后确认是终端类型协商的问题。因为某些终端组件默认以极小型的终端类型连接,老旧的程序不支持功能键转义序列。

解决方式有两种:

  • 把终端类型固定为xterm-256color,在 Termix 的会话配置里指定终端类型参数。
  • 检查浏览器页面是否启用了正确的终端模拟组件的键位映射,有些组件默认不开启应用光标模式。

真正把终端类型改成xterm-256color之后,vim 的体验立刻正常了。复制粘贴要注意页面上提供的安全上下文权限,首次使用会要授权剪贴板读取,允许一次后就不再弹。

5.2 WebSocket 被反向代理掐断的完整排查链路

这个问题很典型,值得写出来。上线前一天我测试一切正常,第二天再打开页面,输入密码后能建立会话,但终端没有任何输出,几秒后提示连接已断开。看 Termix 服务端日志,发现浏览器发起 WebSocket 升级请求,服务端已经接受了,但反向代理层没有正确转发。

排查过程是这样的:

  1. 先用浏览器的开发者工具抓请求,确认 WebSocket 握手请求返回了 200 而不是 101。
  2. 200 说明反向代理没有把它当一个升级请求,而是当成普通 GET 请求处理了。
  3. 检查反向代理配置,发现我第一版配置里没有设置Upgrade和Connection请求头。
  4. 最初补上了Connection "upgrade"的配置,接上发现还是不稳,因为代理的读取超时太短,长连接闲置一会儿就被切断。
  5. 最后调整代理读取超时参数,把超时加大,问题彻底消失。

这里最容易被忽略的是:很多反向代理默认不保留长连接,升级请求一旦走默认处理,WebSocket 就废了。这种问题不是一眼能看出来的,要认真翻一下层层的日志。

5.3 多人同时登录同一主机的会话串扰

有同事在同一时间登录同一台目标服务器,出现了一种奇怪现象:两边敲的命令偶尔会跑到对方会话里。排查后发现,问题不在 Termix 本身,而是我们两个人用的系统账号是同一个。当两个 Web SSH 会话映射到同一个系统用户时,服务端交互式会话会冲突。

解决办法是给每个人分配独立的系统账号,并且 Termix 配置里把账号与主机访问权分开控制。如果只是临时用一下,至少要让每个人手动指定--identity参数连接不同会话,避免复用同一个默认账号。

5.4 手机端体验优化的一些细节

浏览器能开网页就能连服务器,但手机端有几个地方值得打磨:

  • 把页面添加到主屏幕,全屏显示,体验接近原生应用。
  • 开启页面自带的字号调整选项,手机端字号适中才不费眼。
  • 把常用主机预存在 Termix 的配置里,连接时只需要点一下选择,不用敲 IP。
  • 开小键盘出来会很占屏,尽量用终端操作,少开输入法。

我实际在手机上用它修过一次配置错误:人在地铁上,服务告警,我打开手机浏览器,点两下进了页面,改掉了配置,服务恢复。以前这种场景基本是等回电脑再说。

一些我最后想分享的使用习惯

Termix 这套体系用了几个月后,我总结了几条自己的习惯。现在每周都会检查一次登录日志,看看有没有陌生 IP 的尝试记录。动态验证码让密码泄露的风险变低,但并不能完全代替定期更换密码。所有目标服务器的密钥尽量都采用密钥登录,并且 Termix 服务端保存密钥文件的位置要设置严格权限,其它用户不可读。如果你准备给团队使用,建议最开始就约定账号命名规范和目标主机分组规则,不然人一多就会乱。

如果你已经有一台可以访问公网的服务器,搭这套东西一两个小时就能搞定,真正花时间的其实是被攻击之后怎么应对。能把这套东西认真跑起来的人,运维效率会提升很大。希望你也能从这套方案里受益。