=============================================================
::: info 以下是与爆爆龙(自己养的虾[openclaw])的讨论
:::
=============================================================
介绍下SSH 本地端口转发功能吧
openclaw-control-ui3:46 PM
SSH 本地端口转发(Local Port Forwarding)是 SSH 最实用的功能之一,简单说就是:把你本地机器的某个端口,通过 SSH 隧道,转发到远程服务器能访问的任意地址和端口。
基本语法
ssh -L [本地端口]:[目标主机]:[目标端口] [SSH 服务器]工作原理
你的本地机器 --(localhost:本地端口)--> SSH 服务器 --(目标主机:目标端口)--> 最终服务经典场景
1. 访问内网服务
比如公司数据库只在内部网络开放:
ssh -L 3306:localhost:3306 user@jump-server然后本地用数据库客户端连接 localhost:3306 就能访问远程数据库。
2. 绕过防火墙访问网站
ssh -L 8080:目标网站.com:80 user@有外网权限的服务器本地浏览器访问 localhost:8080 就能通过服务器访问目标网站。
3. 加密不安全的协议
比如 VNC、Redis 这些明文传输的服务:
ssh -L 5900:localhost:5900 user@remoteVNC 客户端连接 localhost:5900,流量全程加密。
实用技巧
-
后台运行:加
-N -f参数Terminal window ssh -N -f -L 3306:localhost:3306 user@server -
多端口转发:可以指定多个
-LTerminal window ssh -L 3306:localhost:3306 -L 6379:localhost:6379 user@server -
绑定到所有接口:默认只绑定 localhost,加
0.0.0.0:可让同局域网其他机器也能用Terminal window ssh -L 0.0.0.0:3306:localhost:3306 user@server
和远程转发的区别
- 本地转发 (
-L):本地端口 → 远程能访问的服务 - 远程转发 (
-R):远程端口 → 本地能访问的服务(用于把本地服务暴露出去)
有具体想用的场景吗?可以帮你看看怎么配置。
**爆爆龙 (Bàobàolóng)**3:46 PMcoder-model
看你的提供的例子上看,这个格式 ssh -L [本地端口]:[目标主机]:[目标端口] [SSH 服务器] 中的【目标主机】不是太理解
You3:55 PM
好问题!这个「目标主机」确实容易让人困惑。
核心概念
目标主机 = 从 SSH 服务器的视角看,你要访问的服务在哪里
关键点:这个地址是 SSH 服务器能看到的,不是你本地能看到的。
三种常见情况
1. 服务就在 SSH 服务器上
ssh -L 3306:localhost:3306 user@server- 目标主机:
localhost(对 server 来说就是它自己) - 含义:本地 3306 → SSH 服务器 → SSH 服务器自己的 3306
2. 服务在 SSH 服务器能访问的内网另一台机器
ssh -L 3306:192.168.1.50:3306 user@jump-server- 目标主机:
192.168.1.50(内网数据库服务器,jump-server 能访问) - 含义:本地 3306 → jump-server → 内网 192.168.1.50 的 3306
3. 服务在 SSH 服务器能访问的外网
ssh -L 8080:api.example.com:80 user@vps- 目标主机:
api.example.com(VPS 能访问的外网服务) - 含义:本地 8080 → VPS → 外网 api.example.com
形象理解
你本地电脑 │ │ SSH 连接 ▼SSH 服务器(跳板) │ │ 目标主机在这里的某个位置 ├───→ 可能是服务器自己 (localhost) ├───→ 可能是内网其他机器 (192.168.x.x) └───→ 可能是外网服务 (example.com)为什么这么设计?
因为 SSH 隧道是从服务器端发起连接的。流程是:
- 你本地连接
localhost:本地端口 - 请求通过 SSH 隧道传到服务器
- 服务器去连接「目标主机:目标端口」
- 数据原路返回
所以「目标主机」必须是服务器能解析和访问的地址,你本地能不能访问无所谓。
有具体想连接的服务吗?可以帮你看看目标主机应该填什么。
**爆爆龙 (Bàobàolóng)**3:55 PM
=============================================================
::: info 以下是与Claude的讨论
:::
=============================================================
有没有办法用ssh创建一个加密隧道到远端机器,然后我再本地访问那台机器,而那台机器感觉不出来是远程访问,比如访问http://localhost/
3:26 PM
是的,这就是 SSH 本地端口转发(Local Port Forwarding),非常实用的技巧。
基本命令
bash
ssh -L [本地端口]:[目标主机]:[目标端口] [用户名]@[SSH服务器]你的场景:访问远端机器的 localhost
bash
ssh -L 8080:localhost:80 user@remote-server然后在本地浏览器访问 http://localhost:8080,流量会通过加密隧道转发到远端机器的 80 端口。远端服务器看到的请求来自 127.0.0.1,感觉不出是远程访问。
常用参数
| 参数 | 说明 |
|---|---|
-N | 不执行远程命令,只建立隧道(推荐) |
-f | 后台运行 |
-C | 压缩传输数据 |
-T | 禁用伪终端分配 |
推荐的后台运行方式:
bash
ssh -L 8080:localhost:80 -N -f user@remote-server更复杂的场景
转发到远端网络内的第三台机器:
bash
ssh -L 8080:192.168.1.100:80 user@remote-server这样流量会从 SSH 服务器转发到其内网的 192.168.1.100:80。
同时转发多个端口:
bash
ssh -L 8080:localhost:80 -L 5432:localhost:5432 -N user@remote-server关闭隧道
bash
# 找到进程ps aux | grep ssh
# 结束进程kill [PID]```
或者用 `~.` 在 SSH 会话中直接断开。
## 写入配置文件(长期使用)
编辑 `~/.ssh/config`:```Host my-tunnel HostName remote-server User user LocalForward 8080 localhost:80 ServerAliveInterval 60然后直接运行 ssh -N my-tunnel 即可。
ssh -L 18789:localhost:18789 -N -f micmote@192.168.3.11 之后,本地可以访问http://localhost了,但是明显远端机器这个openclaw程序还是知道差别,因为界面都不一样,反正实际上不能使用了
3:33 PM
这个问题很常见。OpenClaw(或类似Web应用)界面不同、功能异常,通常是因为:
可能的原因
1. 端口不一致(最常见) 你转发的是 18789,但本地访问的是 http://localhost(默认80端口)。需要访问:
http://localhost:187892. 应用硬编码了域名/端口 很多Web应用会在HTML/JS里写死服务器地址,比如:
javascript
const api = "http://192.168.3.11:18789/api" // 直连,不走隧道这类请求会绕过你的SSH隧道,直接去连远端IP,本地当然访问不到。
3. WebSocket 连接失败 如果应用用了 WebSocket(ws:// 或 wss://),SSH 端口转发默认支持,但应用可能因为 Host header 不匹配而拒绝。
4. Cookie / Session 绑定了原始 IP 或域名
排查方法
打开浏览器开发者工具(F12)→ Network 标签,刷新页面,看有没有:
- 请求失败(红色)
- 请求目标是
192.168.3.11而不是localhost - WebSocket 连接失败
把你看到的告诉我,可以更精准地解决。
常见解法
如果是硬编码IP问题,可以用本地 hosts 文件”欺骗”应用:
bash
# /etc/hosts 加一行(macOS/Linux)127.0.0.1 192.168.3.11这样应用发给 192.168.3.11 的请求也会走本地,再通过隧道转发。
但要注意:这样改完之后,SSH 连接本身也会受影响,需要用 IP 直连或改用另一个别名。
你现在访问的URL是什么?http://localhost 还是 http://localhost:18789?
没有失败的、全是localhost、WebSocket连接相应101 Switching Protocols
3:37 PM
网络请求都正常,那问题基本可以锁定在 WebSocket 的 Host header 或应用的反向代理配置上。
最可能的原因
应用服务端在 WebSocket 握手时检查了 Origin 或 Host header,发现不是它期望的值(比如期望 192.168.3.11:18789,但收到的是 localhost:18789),然后走了不同的逻辑分支。
排查:看 WebSocket 具体内容
在 F12 → Network → 找到那个 101 的 WebSocket 请求,点进去看:
- Headers 标签 → 请求头里的
Host和Origin是什么? - Messages 标签 → WebSocket 有没有在传数据?还是连上就断了?
快速验证方法
直接在远端机器本地访问一次(如果你能 SSH 进去的话):
bash
ssh user@192.168.3.11# 进去之后用 curl 测试curl http://localhost:18789对比本地隧道访问的响应,看 HTML 内容是否一样。如果一样,问题在前端JS;如果不一样,问题在服务端根据 Host 返回了不同内容。
最有效的解法:用 SSH + 本地反向代理伪造 Host
用 socat 或 nginx 在本地做一层转发,把 Host header 改成应用期望的值:
方案A:本地起一个 nginx
nginx
server { listen 80; server_name localhost;
location / { proxy_pass http://localhost:18789; proxy_set_header Host 192.168.3.11:18789; proxy_set_header Origin http://192.168.3.11:18789;
# WebSocket 支持 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }}这样浏览器访问 http://localhost,nginx 转给 localhost:18789,同时把 Host 改成远端期望的值。
你能看一下那个 WebSocket 请求的 Origin 和 Host header 具体是什么值吗?
host localhost:18789 origin http://localhost:18789
3:37 PM
那 Host 和 Origin 都是 localhost:18789,服务端收到的也应该是这个,不存在 header 不匹配的问题。
重新定位问题
既然网络层完全正常,问题很可能在应用逻辑层。
WebSocket 连上了(101),但应用可能通过 WebSocket 消息本身传递了地址信息,或者应用行为依赖于某个环境变量、配置文件、或者运行参数里写死的地址。
下一步:看 WebSocket 消息内容
F12 → Network → 找到 101 那个请求 → Messages 标签
看看 WebSocket 在传什么数据,特别是:
- 有没有包含
192.168.3.11的消息 - 连接建立后有没有马上收到错误消息
- 消息是正常业务数据还是很快就停了
同时:界面”不一样”具体是怎么不一样?
- 是样式/布局乱了?
- 还是某些功能按钮不见了?
- 还是直接显示错误页?
这个细节能帮助判断是前端资源加载问题还是业务逻辑问题。
unauthorized: gateway token missing (open the dashboard URL and paste the token in Control UI settings)
3:39 PM
找到了!这是应用级别的认证问题,和 SSH 隧道无关。
原因
这个应用(OpenClaw 或类似的)有一个网关 Token 机制:
- 直接在远端机器本地访问时,可能自动带了 token(通过 cookie 或 localStorage)
- 你通过隧道在本地访问,是一个全新的浏览器环境,没有这个 token
解决步骤
1. 先 SSH 进远端机器,用命令行拿到 token
看看应用的配置文件或启动日志:
bash
# 看启动日志(根据实际服务名替换)journalctl -u openclaw -n 50# 或者cat ~/.openclaw/config.yml# 或者找配置目录find / -name "*.yml" -path "*/openclaw/*" 2>/dev/null```
**2. 或者直接看报错提示**
它说 *"open the dashboard URL and paste the token in Control UI settings"*,说明:- 有一个 dashboard URL 可以获取 token- 需要把这个 token 填到 Control UI 的设置里
你能在远端机器上找到这个 dashboard URL 吗?通常在启动日志里会打印出来,类似:```Dashboard: http://localhost:xxxxx?token=xxxxxxxx把那个 token 找到,填进去就好了。你用的具体是什么程序?
天啦,我把这个Dashboard: http://localhost
3:41 PM
哈哈!对,就是这么简单!
浏览器访问那个带 token 的 URL 之后,token 就被存进了本地的 cookie 或 localStorage,之后应用就认为你是合法用户了。
SSH 隧道本身一直工作得好好的,只是缺这一步认证。以后重新建隧道后如果又提示 unauthorized,再访问一次那个带 token 的链接就行了。
刚才我在powershell里运行那个ssh -L -N -f,现在我把这个powershell窗口都关了,怎么隧道还在工作啊?我怎么关掉它?
3:48 PM
因为 -f 参数让 SSH 后台运行了,关掉 PowerShell 窗口不会杀掉它,它作为独立进程继续在跑。
关掉它
powershell
# 找到 ssh 进程Get-Process ssh
# 直接杀掉所有 ssh 进程Stop-Process -Name ssh
# 或者指定 PID 精确关掉Stop-Process -Id <PID>如果你同时有多个 SSH 连接不想全杀,用 Get-Process ssh 先看一下 PID,再用 -Id 指定。
从你的提供的例子上看,这个格式 ssh -L [本地端口]:[目标主机]:[目标端口] [SSH 服务器] 中的【目标主机】不是太理解
3:57 PM
好问题!这里的目标主机是从 SSH 服务器的视角来看的,不是你本地机器的视角。
三台机器的场景
你的电脑 ----SSH隧道----> SSH服务器 ----> 目标主机目标主机就是最终你想访问的机器,由 SSH 服务器负责去连它。
对应你的命令
ssh -L 18789:localhost:18789 micmote@192.168.3.11- SSH 服务器 =
192.168.3.11 - 目标主机 =
localhost← 这里的 localhost 是指 192.168.3.11 自己!
所以你的场景其实只有两台机器,目标主机就是 SSH 服务器本身。
三台机器的例子
假设你想通过 192.168.3.11 访问内网里另一台机器 192.168.3.20 的网页:
ssh -L 8080:192.168.3.20:80 micmote@192.168.3.11你本地访问 localhost:8080,流量经过隧道到达 192.168.3.11,再由它转发给 192.168.3.20:80。你的电脑甚至不需要知道 192.168.3.20 的存在。