ssh的类型与原理
SSH 是什么
SSH 的全称是 Secure Shell,中文一般翻译成“安全外壳协议”。
它最常见的用途是:让我们在本地电脑上,安全地登录到另一台远程服务器,并在远程服务器上执行命令。
比如:
1 | ssh user@example.com |
这条命令的意思是:用 user 这个用户名,通过 SSH 登录到 example.com 这台服务器。
SSH 解决的核心问题是:在不可信的网络里,安全地连接另一台机器。
它通常会做几件事:
- 身份认证:确认你是不是有权限登录这台服务器,比如密码登录、密钥登录。
- 加密通信:登录后输入的命令、返回的结果都会被加密传输。
- 远程命令执行:可以像操作本地终端一样操作远程服务器。
- 端口转发:也就是常说的 SSH 隧道,可以把某个网络连接“套”进 SSH 加密连接里传输。
这篇笔记主要记录 SSH 的 端口转发 / 隧道 用法。
SSH 隧道是什么
SSH 隧道的本质是:借用一条已经建立好的 SSH 连接,把其他 TCP 流量转发过去。
可以先把 SSH 想成一条安全管道:
1 | 本地电脑 <====== 加密 SSH 连接 ======> SSH 服务器 |
正常情况下,这条管道只用来传输终端命令。
但端口转发会让这条管道承担更多事情:把某个端口收到的流量,经由 SSH 连接送到另一端,再转发给真正的目标服务。
常见的 SSH 隧道有三种:
| 类型 | 参数 | 核心方向 | 记忆方式 |
|---|---|---|---|
| 本地隧道 | -L |
本地端口 -> SSH 服务器侧的目标服务 | 把远程服务拉到本地 |
| 远程隧道 | -R |
远程端口 -> 本地侧的目标服务 | 把本地服务暴露到远程 |
| 动态隧道 | -D |
本地 SOCKS 代理 -> SSH 服务器 -> 任意目标 | 让 SSH 临时变成代理 |
本地隧道:把远程服务拉到本地
一句话理解:在本地开一个端口,访问这个本地端口时,流量会通过 SSH 转发到远程网络里的某个服务。
命令模板:
1 | ssh -L 本地监听端口:目标主机:目标端口 用户名@SSH服务器 |
例子:
1 | ssh -L 3307:127.0.0.1:3306 user@example.com |
这条命令的含义是:
- 在本地电脑监听
3307端口。 - 通过 SSH 登录到
example.com。 - 当本地访问
127.0.0.1:3307时,流量会被送到 SSH 服务器那一侧的127.0.0.1:3306。
如果远程服务器上有一个 MySQL 只监听自己的 127.0.0.1:3306,外部不能直接访问,那么本地隧道就可以让你在本地这样连:
1 | mysql -h 127.0.0.1 -P 3307 -u root -p |
注意:这里连的是本地的 3307,但真正到达的是远程服务器那边的 3306。

适用场景
- 远程数据库不允许公网访问,只允许服务器本机访问。
- 公司内网服务只能从跳板机访问。
- 想安全地临时访问远程机器上的管理后台。
容易混淆的点
目标主机 是站在 SSH 服务器那一侧 来看的。
比如:
1 | ssh -L 8080:127.0.0.1:80 user@example.com |
这里的 127.0.0.1:80 不是你本地电脑的 80 端口,而是 example.com 那台服务器自己看到的 127.0.0.1:80。
远程隧道:把本地服务暴露到远程
一句话理解:在远程服务器上开一个端口,别人访问远程服务器这个端口时,流量会通过 SSH 反向回到本地服务。
命令模板:
1 | ssh -R 远程监听端口:目标主机:目标端口 用户名@SSH服务器 |
例子:
1 | ssh -R 9000:127.0.0.1:3000 user@example.com |
这条命令的含义是:
- 通过 SSH 登录到
example.com。 - 在远程服务器上监听
9000端口。 - 当远程服务器访问自己的
127.0.0.1:9000时,流量会通过 SSH 隧道回到本地电脑的127.0.0.1:3000。
如果你本地正在跑一个 Web 服务:
1 | npm run dev |
服务地址是:
1 | http://127.0.0.1:3000 |
那么远程隧道可以让 SSH 服务器通过下面这个地址访问到它:
1 | http://127.0.0.1:9000 |

适用场景
- 本地电脑没有公网 IP,但想让远程服务器访问本地服务。
- 在服务器上借用本地电脑的代理端口。
- 临时把本地开发服务暴露给远程环境调试。
容易混淆的点
远程隧道默认通常只监听远程服务器自己的 127.0.0.1。
也就是说,下面这条命令:
1 | ssh -R 9000:127.0.0.1:3000 user@example.com |
一般只代表 SSH 服务器本机 可以访问 127.0.0.1:9000,不一定代表互联网上所有人都能访问 example.com:9000。
如果希望远程端口监听公网地址,通常还涉及 SSH 服务端配置,比如 GatewayPorts,这个属于更高风险的公网暴露场景,需要谨慎处理。
动态隧道:让 SSH 变成 SOCKS 代理
一句话理解:在本地开一个 SOCKS 代理端口,应用把请求交给这个代理后,SSH 会把请求转发到不同的目标地址。
命令模板:
1 | ssh -D 本地SOCKS端口 用户名@SSH服务器 |
例子:
1 | ssh -D 1080 user@example.com |
这条命令的含义是:
- 在本地电脑监听
1080端口。 - 这个端口不是固定转发到某一个服务,而是作为 SOCKS 代理使用。
- 浏览器或其他程序把请求交给
127.0.0.1:1080后,SSH 会根据请求里的目标地址,帮你从 SSH 服务器那一侧访问目标网站或服务。
浏览器代理可以这样理解:
1 | 浏览器 -> 127.0.0.1:1080(SOCKS5) -> SSH 服务器 -> 目标网站 |

适用场景
- 想让浏览器临时通过 SSH 服务器访问网络。
- 目标地址很多,不想为每个服务单独写一个
-L。 - 需要一个临时 SOCKS5 代理。
容易混淆的点
动态隧道和本地隧道都在本地开端口,但区别很大:
-L是固定转发:一个本地端口对应一个固定目标。-D是动态代理:目标地址由应用的 SOCKS 请求决定。
所以 -D 后面不需要写 目标主机:目标端口。
三种隧道对比
| 对比项 | 本地隧道 -L |
远程隧道 -R |
动态隧道 -D |
|---|---|---|---|
| 监听端口在哪里 | 本地电脑 | SSH 服务器 | 本地电脑 |
| 流量最终去哪里 | SSH 服务器那一侧的固定目标 | 本地电脑那一侧的固定目标 | 请求指定的动态目标 |
| 是否固定目标 | 固定 | 固定 | 不固定 |
| 常见用途 | 访问远程内网服务 | 让远程机器访问本地服务 | 临时 SOCKS 代理 |
| 记忆方式 | 拉回来 | 推过去 | 代理出去 |
更口语化地记:
- 本地隧道
-L:我在本地开门,把远程服务拉到我面前。 - 远程隧道
-R:我在远程开门,把访问反向带回本地。 - 动态隧道
-D:我在本地开一个代理入口,后面去哪由请求自己决定。