ByteNoteByteNote

字节笔记本

2026年8月29日

没有公网 IP:用 MQTT-Web-Terminal 远程进树莓派

API中转
¥120

家用树莓派、公司里的边缘盒子,往往都没有公网 IP。内网穿透能解决,但要额外服务、证书和端口映射。MQTT-Web-Terminal 换了条路:设备端和浏览器都连同一台 MQTT Broker,终端输入输出走主题转发,设备本身不用暴露任何端口。

仓库在 lewangdev/MQTT-Web-Terminal,设备侧是 Python + PTY,浏览器侧是 xterm.js + MQTT over WebSocket。

它怎么通的

整体就三块:

  1. 设备端(树莓派)main.pypty.fork() 拉起一个 shell,再用 paho-mqtt 连 Broker。收到 /device/<id>/terminal/input 就往 PTY 写键入,读到 shell 输出就发到 /device/<id>/terminal/output。窗口大小变化走 /device/<id>/terminal/resize
  2. 浏览器端app.js 用 xterm.js 画终端,通过 WebSocket(常见是 wss://...:8084/mqtt)连同一个 Broker,订阅 output、发布 input/resize。
  3. MQTT Broker:两边都主动连出去。设备在 NAT 后面也没关系,只要能访问 Broker。

默认示例用的是带 TLS 的 EMQX 类云 Broker(仓库里带了 emqxsl-ca.crt,端口 8883),你也可以换成自建 Mosquitto / EMQX,只要同时支持 TCP MQTT(设备)和 WebSocket MQTT(浏览器)。

先准备 Broker

选一个你能拿到账号密码的 MQTT 服务,例如 EMQX Cloud 免费实例,或自己的 EMQX/Mosquitto。记下:

  • 主机名(设备用,例如 xxx.emqxsl.com
  • MQTT TLS 端口(常见 8883
  • WebSocket TLS 地址(常见 wss://xxx.emqxsl.com:8084/mqtt
  • 用户名 / 密码
  • CA 证书(若用云厂商 TLS,把仓库里的证书换成你自己的,或关掉 TLS 仅在内网测)

主题按设备 ID 隔离。仓库默认 DEVICE_ID = "raspberrypi",多台机器就改成不同 ID。

设备端:树莓派上跑终端代理

克隆仓库并装依赖:

bash
git clone https://github.com/lewangdev/MQTT-Web-Terminal.git
cd MQTT-Web-Terminal
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

requirements.txt 目前只有 paho-mqtt==1.6.1

main.py 顶部配置:

python
DEVICE_ID = "raspberrypi"   # 多机时改成唯一名
DEVICE_SHELL = "bash"       # 或 "sh"
MQTT_HOST = "你的-broker-主机"
MQTT_PORT = 8883
MQTT_USER = "mqtt"
MQTT_PASSWD = "你的密码"
MQTT_USE_TLS = True

用 TLS 时,把 client.tls_set("emqxsl-ca.crt") 指到你的 CA;自签或测试环境可按需调整 tls_insecure_set。确认后启动:

bash
python3 main.py

进程会一直挂着:连上 Broker 后订阅 input/resize,并开线程把 PTY 输出推到 output 主题。建议用 systemd 或 tmux 保活,开机自启更省心。

浏览器端

在同一仓库里(有 Node 的电脑上)按 README 装依赖并启动静态页,浏览器打开本机 8000 端口。

改 app.js:用户名、口令、deviceId、mqttUrl(WebSocket 地址)要和设备端一致。 deviceId 必须和 DEVICE_ID 相同。连上后状态显示 connected,按 Enter 激活终端。复制粘贴用 Ctrl+Shift+X / Ctrl+Shift+V。

安全上别踩坑

这条链路等于把 shell 挂到公网 Broker 上,务必:

  • 强口令 + ACL:Broker 上限制该用户只能读写对应 device 主题,别开匿名。
  • TLS:生产环境别用明文 MQTT;浏览器侧优先 wss。
  • 别用 root 跑设备端:用普通用户,需要时再 sudo。
  • 仓库示例里的空口令是占位符,部署前换成自己的,也别把改过的配置提交回去。

需要多人访问时,前面再加一层登录(反向代理 Basic Auth / SSO)更稳妥。

和内网穿透差在哪

MQTT-Web-Terminalfrp / ngrok 一类
设备是否开端口否,只出站连 Broker通常要开客户端连中继
终端 UI自带 xterm 网页仍要自己 SSH / WebTTY
依赖一个 MQTT 服务中继服务 + 隧道配置
多设备靠不同 DEVICE_ID 主题多条隧道或不同端口

如果你已经有 MQTT(家居、IoT 网关),这套几乎零额外基础设施;如果只是临时 SSH,传统隧道可能更轻。两者不互斥,可以按场景选。

小结

没有公网 IP 时,不必硬上端口映射。让树莓派和浏览器都连 MQTT,用主题把 PTY 的输入输出桥过去,网页里就能进 shell。上手步骤:配好 Broker,改 main.py 在设备上跑,改 app.js 在浏览器连同一套凭据和设备 ID。源码见 https://github.com/lewangdev/MQTT-Web-Terminal ,终端前端思路可参考 https://github.com/cs01/pyxtermjs

分享: