3089 字
15 分钟
SSH本地端口转发
2026-10-09

=============================================================

::: info 以下是与爆爆龙(自己养的虾[openclaw])的讨论

:::

=============================================================

介绍下SSH 本地端口转发功能吧

openclaw-control-ui3:46 PM

SSH 本地端口转发(Local Port Forwarding)是 SSH 最实用的功能之一,简单说就是:把你本地机器的某个端口,通过 SSH 隧道,转发到远程服务器能访问的任意地址和端口。

基本语法#

Terminal window
ssh -L [本地端口]:[目标主机]:[目标端口] [SSH 服务器]

工作原理#

你的本地机器 --(localhost:本地端口)--> SSH 服务器 --(目标主机:目标端口)--> 最终服务

经典场景#

1. 访问内网服务#

比如公司数据库只在内部网络开放:

Terminal window
ssh -L 3306:localhost:3306 user@jump-server

然后本地用数据库客户端连接 localhost:3306 就能访问远程数据库。

2. 绕过防火墙访问网站#

Terminal window
ssh -L 8080:目标网站.com:80 user@有外网权限的服务器

本地浏览器访问 localhost:8080 就能通过服务器访问目标网站。

3. 加密不安全的协议#

比如 VNC、Redis 这些明文传输的服务:

Terminal window
ssh -L 5900:localhost:5900 user@remote

VNC 客户端连接 localhost:5900,流量全程加密。

实用技巧#

  • 后台运行:加 -N -f 参数

    Terminal window
    ssh -N -f -L 3306:localhost:3306 user@server
  • 多端口转发:可以指定多个 -L

    Terminal 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 服务器上#

Terminal window
ssh -L 3306:localhost:3306 user@server
  • 目标主机:localhost(对 server 来说就是它自己)
  • 含义:本地 3306 → SSH 服务器 → SSH 服务器自己的 3306

2. 服务在 SSH 服务器能访问的内网另一台机器#

Terminal window
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 服务器能访问的外网#

Terminal window
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 隧道是从服务器端发起连接的。流程是:

  1. 你本地连接 localhost:本地端口
  2. 请求通过 SSH 隧道传到服务器
  3. 服务器去连接「目标主机:目标端口」
  4. 数据原路返回

所以「目标主机」必须是服务器能解析和访问的地址,你本地能不能访问无所谓。

有具体想连接的服务吗?可以帮你看看目标主机应该填什么。

**爆爆龙 (Bàobàolóng)**3:55 PM

=============================================================

::: info 以下是与Claude的讨论

:::

=============================================================

有没有办法用ssh创建一个加密隧道到远端机器,然后我再本地访问那台机器,而那台机器感觉不出来是远程访问,比如访问http://localhost/

3:26 PM

是的,这就是 SSH 本地端口转发(Local Port Forwarding),非常实用的技巧。

基本命令#

bash

Terminal window
ssh -L [本地端口]:[目标主机]:[目标端口] [用户名]@[SSH服务器]

你的场景:访问远端机器的 localhost#

bash

Terminal window
ssh -L 8080:localhost:80 user@remote-server

然后在本地浏览器访问 http://localhost:8080,流量会通过加密隧道转发到远端机器的 80 端口。远端服务器看到的请求来自 127.0.0.1,感觉不出是远程访问。

常用参数#

参数说明
-N不执行远程命令,只建立隧道(推荐)
-f后台运行
-C压缩传输数据
-T禁用伪终端分配

推荐的后台运行方式:

bash

Terminal window
ssh -L 8080:localhost:80 -N -f user@remote-server

更复杂的场景#

转发到远端网络内的第三台机器:

bash

Terminal window
ssh -L 8080:192.168.1.100:80 user@remote-server

这样流量会从 SSH 服务器转发到其内网的 192.168.1.100:80。

同时转发多个端口:

bash

Terminal window
ssh -L 8080:localhost:80 -L 5432:localhost:5432 -N user@remote-server

关闭隧道#

bash

Terminal window
# 找到进程
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:18789

2. 应用硬编码了域名/端口 很多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

Terminal window
# /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 请求,点进去看:

  1. Headers 标签 → 请求头里的 Host 和 Origin 是什么?
  2. Messages 标签 → WebSocket 有没有在传数据?还是连上就断了?

快速验证方法#

直接在远端机器本地访问一次(如果你能 SSH 进去的话):

bash

Terminal window
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

Terminal window
# 看启动日志(根据实际服务名替换)
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?token=xxxxxxxx填进浏览器地址栏就好了。。。

3:41 PM

哈哈!对,就是这么简单!

浏览器访问那个带 token 的 URL 之后,token 就被存进了本地的 cookie 或 localStorage,之后应用就认为你是合法用户了。

SSH 隧道本身一直工作得好好的,只是缺这一步认证。以后重新建隧道后如果又提示 unauthorized,再访问一次那个带 token 的链接就行了。

刚才我在powershell里运行那个ssh -L -N -f,现在我把这个powershell窗口都关了,怎么隧道还在工作啊?我怎么关掉它?

3:48 PM

因为 -f 参数让 SSH 后台运行了,关掉 PowerShell 窗口不会杀掉它,它作为独立进程继续在跑。

关掉它#

powershell

Terminal window
# 找到 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 的存在。

SSH本地端口转发
http://cypherx.top/posts/cs/ssh本地端口转发/
作者
Cypherx
发布于
2026-10-09
许可协议
CC BY-NC-SA 4.0