4. SSH 正向隧道与动态转发:SSH -L 本地端口转发、SSH -D 动态 SOCKS5 代理、浏览器代理配置、穿透公司防火墙访问内网资源

好,咱们进入正题。前面几章我们把 SSH 的基础认证和密钥管理捋了一遍,现在该聊聊真正能干活的东西了——隧道转发。

说实话,我刚开始接触 SSH 隧道时,觉得这东西挺玄乎的。后来在项目里被逼着用了一次,才发现它其实就是个「端口搬运工」。你想想看,公司内网有台数据库服务器,你在家想连它,但防火墙只开了 22 端口。怎么办?SSH 隧道就是你的秘密通道。

4.1 本地端口转发:SSH -L 的底层逻辑

本地端口转发,说白了就是把本地某个端口上的流量,通过 SSH 加密通道,扔到远程的某个目标地址上。

命令格式很简单:

ssh -L [本地地址:]本地端口:目标地址:目标端口 用户@跳板机

举个例子。假设公司内网有台 MySQL 服务器 10.0.1.100:3306,你有一台公网跳板机 jump.example.com。你想在本地用 Navicat 连那个 MySQL:

ssh -L 3307:10.0.1.100:3306 user@jump.example.com

这条命令执行后,你本地 localhost:3307 收到的所有 TCP 连接,都会被 SSH 客户端加密,送到跳板机,再由跳板机解密后转发给 10.0.1.100:3306。然后你在 Navicat 里连 localhost:3307 就行了。

关键理解:SSH 客户端是转发起点,SSH 服务端是转发中继。流量全程加密,只有最后一跳(跳板机→目标)是明文的。

我在一个金融项目里遇到过这种情况:客户要求所有数据库操作必须经过审计,但开发人员又需要本地调试。我们就在跳板机上装了审计插件,然后用 -L 强制所有流量走跳板机,一举两得。

4.1.1 绑定地址与端口冲突

默认情况下,-L 只监听 127.0.0.1。如果你想让局域网其他机器也能用这个隧道,需要显式指定 0.0.0.0:

ssh -L 0.0.0.0:3307:10.0.1.100:3306 user@jump.example.com

但要注意,这样会暴露端口。我建议非必要不开启,或者配合防火墙白名单使用。

参数 作用 常用场景
-L 8080:web:80 本地 8080 → 远程 web:80 访问内网 Web 服务
-L 0.0.0.0:443:10.0.1.50:443 全端口监听,转发 HTTPS 团队共享内网服务
-L *:2222:db:5432 通配符绑定 PostgreSQL 远程调试

4.2 动态 SOCKS5 代理:SSH -D 的威力

本地转发有个局限:你得事先知道目标地址和端口。如果我想临时访问内网任意机器呢?这时候就该 -D 上场了。

动态转发会在本地启动一个 SOCKS5 代理服务器。所有发到这个代理的请求,都会通过 SSH 隧道转发到远端,再由远端帮你发起连接。

ssh -D 1080 user@jump.example.com

这条命令在本地 127.0.0.1:1080 开启了一个 SOCKS5 代理。你只要把浏览器或应用的代理设置指向它,所有流量就都走 SSH 隧道了。

我的习惯:端口号我喜欢用 1080,这是 SOCKS 协议的默认端口,好记。但如果你同时开多个隧道,记得换端口,比如 1081、1082。

4.2.1 SOCKS5 与 HTTP 代理的区别

很多人搞混这两个概念。我简单说一下:

  • HTTP 代理:只能代理 HTTP/HTTPS 流量,需要客户端主动发 CONNECT 请求。
  • SOCKS5 代理:工作在更底层,可以代理 TCP 和 UDP 流量,支持任意协议(SSH、FTP、SMTP 都行)。

说白了,SOCKS5 就是个万能管道。你甚至可以在 SOCKS5 隧道里再跑一个 SSH 连接——套娃玩法,后面章节会讲。

4.3 浏览器代理配置实战

隧道建好了,怎么让浏览器用起来?我推荐两种方式。

4.3.1 手动配置(适合临时使用)

以 Chrome 为例:

  1. 打开设置 → 高级 → 系统 → 打开代理设置。
  2. 选择「使用代理服务器」。
  3. 地址填 127.0.0.1,端口填 1080。
  4. 协议选 SOCKS5。

但这样有个问题:所有流量都走代理了,访问百度也走隧道,速度慢不说,还浪费带宽。

4.3.2 使用浏览器插件(推荐)

我习惯用 SwitchyOmega 这个插件。它可以按规则自动切换代理:

  • 内网域名(如 *.internal.company.com)走 SOCKS5 代理。
  • 其他地址直连。

配置方法:

  1. 安装 SwitchyOmega。
  2. 新建一个情景模式,选「代理服务器」。
  3. 协议选 SOCKS5,地址 127.0.0.1,端口 1080。
  4. 在「自动切换模式」里添加规则,比如 *.corp.com 走这个代理。

避坑指南:我曾经在配置时选了 SOCKS4 而不是 SOCKS5,结果 DNS 解析一直在本地执行,内网域名解析失败。折腾了半小时才发现。记住:SOCKS5 支持远程 DNS 解析,SOCKS4 不支持。内网场景必须用 SOCKS5。

4.4 穿透公司防火墙访问内网资源

现在我们把前面学的串起来,看看真实场景怎么用。

4.4.1 典型拓扑

假设你在家,想访问公司内网的三个服务:

  • GitLab(10.0.1.10:80)
  • Jenkins(10.0.1.20:8080)
  • 一台开发服务器(10.0.1.30:22)

公司防火墙只允许 SSH 出站到公网跳板机 jump.company.com。

方案一:开三个 -L 隧道。

ssh -L 8080:10.0.1.10:80 -L 8081:10.0.1.20:8080 -L 2222:10.0.1.30:22 user@jump.company.com

方案二:开一个 -D 隧道,配合浏览器插件。

ssh -D 1080 user@jump.company.com

我个人更推荐方案二。原因很简单:灵活。你临时想访问内网某个新服务,不用重新建隧道,浏览器里直接输地址就行。

4.4.2 保持隧道稳定

SSH 连接久了容易断。我一般会加几个参数:

ssh -D 1080 -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -N user@jump.company.com
  • -N:不执行远程命令,只建隧道。
  • ServerAliveInterval=30:每 30 秒发一个心跳包。
  • ServerAliveCountMax=3:连续 3 次心跳无响应就断开。

这样隧道可以挂几天都不掉。我有个隧道曾经跑了 47 天,直到机房维护才断。

4.5 本章核心逻辑图

下面这张图展示了本地转发和动态转发的核心区别:

SSH 隧道转发核心逻辑 本地端口转发 (-L) 本地:3307 跳板机 内网:3306 动态 SOCKS5 转发 (-D) 本地:1080 跳板机 任意内网目标 说明: -L 需要预先指定目标地址和端口,适合固定服务访问。 -D 只需指定本地 SOCKS5 端口,目标地址由浏览器/应用动态决定,更灵活。 适用场景: • 数据库客户端连接 • 固定端口的内网服务 适用场景: • 浏览器访问多个内网 Web 服务 • 临时需要访问未知目标

4.6 本章小结

本地转发和动态转发,一个适合固定目标,一个适合灵活访问。我平时开发时,-D 几乎常驻后台,配合浏览器插件,内网资源随便逛。但生产环境维护数据库时,我还是会用 -L,因为目标明确,不容易出错。