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:我在本地开一个代理入口,后面去哪由请求自己决定。

参考资料