跳至正文
老丹的足迹 —— 代码写给机器,游记写给自己,感悟写给时间
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面

SSH隧道详解:从原理到实战,一篇就够了

作者注:本文不讲晦涩的理论,用“点外卖”的类比和可直接运行的命令,帮你彻底理解SSH隧道是什么、怎么用、服务器要不要配。

一、引言:SSH隧道到底是什么?

SSH隧道(SSH Tunneling),也叫端口转发(Port Forwarding),是利用SSH协议建立的一条加密数据传输通道。它的核心作用就一句话:

把本地一个端口,通过SSH加密连接,映射到远程某个服务的端口上。

通俗地说:你坐在家里,通过一台公网服务器做“跳板”,安全地访问公司内网的服务,或者把家里的服务安全地暴露给外网。

它有三个最常用的场景:

  • 本地转发(-L):访问被防火墙限制的内网服务(如数据库、内部网站)
  • 远程转发(-R):把内网服务暴露到公网(内网穿透)
  • 动态转发(-D):创建SOCKS5代理,实现浏览器级别的全局代理

二、理解原理:一个“点外卖”的类比

为了让你不看代码也能懂,我们用“点外卖”来还原SSH隧道的完整过程。

角色对应关系

现实角色技术角色
你(坐在家里)客户端(你的Ubuntu电脑)
公司保安(有工号,能进出公司)SSH服务器(有公网IP,能访问内网)
老王面馆(在公司内部,只送内网)目标服务(如内网数据库,只允许内网IP访问)
你家门口的取餐箱本地监听的端口
你和保安之间的加密电话线SSH加密连接

完整流程

  1. 你给公司保安打电话,报上工号和密码,保安确认你的身份——建立SSH连接(身份认证)
  2. 你在电话里告诉保安:“我在家门口放了个取餐箱(本地端口),你帮我盯着。如果我往里面放纸条,你就按纸条上的内容,去老王面馆帮我买面,然后加密送回来。”——设置端口转发规则
  3. 你写了一张纸条:“要一碗牛肉面”(相当于发送数据库查询请求),放进家门口的取餐箱(访问本地端口)
  4. 保安定时来取餐箱查看,看到纸条后,装进一个只有你俩能解的密码箱(SSH加密),通过电话线传到公司
  5. 保安收到后解密,以他自己的身份去老王面馆买面(SSH服务器代替你访问内网目标)
  6. 保安把面加密装进密码箱,通过电话线传回给你,你解密后取出来吃(返回查询结果)

三个关键认知

第一,数据没有“穿透”防火墙。 你的请求根本没直接去找面馆,而是保安替你跑了一趟。防火墙只看到保安在公司内部活动,这是合法的。

第二,隧道就是那条加密电话线。 外界听不到内容(加密),也不知道你在让保安干什么(协议混淆),只看到你和保安在通话(SSH连接)。

第三,隧道只负责搬运,不管内容。 无论是MySQL查询、HTTP请求还是远程桌面,只要是TCP协议的数据,都能走SSH隧道。

三、实战配置:三条命令走天下

以下所有命令在Ubuntu客户端上执行,服务器端默认不需要任何配置(后面会讲例外情况)。

场景一:本地转发(-L)——访问内网服务

需求:你在家(Ubuntu),想连接公司内网的MySQL数据库(IP 192.168.1.100,端口 3306),但公司防火墙只允许内网访问。

命令:

ssh -L 3307:192.168.1.100:3306 root@123.45.67.89

效果:输入服务器密码后,隧道建立。你打开另一个终端,执行:

mysql -h 127.0.0.1 -P 3307 -u 用户名 -p

你连的是自己电脑的 3307 端口,但实际连上了内网数据库的 3306 端口。

参数解析:

  • -L = Local,表示在本地监听
  • 3307 = 你本机随便选一个空闲端口
  • 192.168.1.100:3306 = 目标服务的真实IP和端口
  • root@123.45.67.89 = 跳板机(SSH服务器)的登录信息

场景二:远程转发(-R)——暴露内网服务

需求:你在Ubuntu上跑了一个Web服务(端口 3000),想让外地的同事访问,但你家没有公网IP。

命令:

ssh -R 8080:127.0.0.1:3000 root@123.45.67.89

效果:同事访问 http://123.45.67.89:8080,实际看到的是你家Ubuntu上 3000 端口的网页。

参数解析:

  • -R = Remote,表示在远程服务器上监听
  • 8080 = 公网服务器上对外开放的端口
  • 127.0.0.1:3000 = 你本地服务的地址和端口

注意:默认情况下,服务器上的 8080 端口只监听 127.0.0.1,即只有服务器本机能访问。如果想让外网所有人访问,需要修改服务器配置(见下文“服务器端配置”)。

场景三:动态转发(-D)——SOCKS5代理

需求:你想让Ubuntu上的浏览器通过远程服务器访问所有网站(如访问受限资源)。

命令:

ssh -D 1080 root@123.45.67.89

效果:本机开启一个SOCKS5代理,地址为 127.0.0.1:1080。然后在Ubuntu的系统设置 → 网络 → 代理中设置:

  • 代理类型:SOCKS5
  • 主机:127.0.0.1
  • 端口:1080

之后浏览器的所有流量都会经过远程服务器转发。

参数解析:

  • -D = Dynamic,动态代理
  • 1080 = 本机SOCKS5代理端口

命令速查表

功能命令参数监听位置流量方向适用场景
本地转发-L 本地端口:目标IP:目标端口你的电脑你 → 跳板机 → 目标访问内网服务
远程转发-R 远程端口:本地IP:本地端口远程服务器别人 → 跳板机 → 你暴露内网服务
动态转发-D 本地端口你的电脑你(SOCKS5)→ 跳板机 → 任意目标全局代理

四、让隧道更稳定:进阶技巧

1. 后台运行(-f)

加了 -f,SSH验证通过后进入后台运行,终端可以关闭:

ssh -f -L 3307:192.168.1.100:3306 root@123.45.67.89

2. 保持连接(心跳包)

防止网络波动导致隧道断开:

ssh -o ServerAliveInterval=60 -L 3307:192.168.1.100:3306 root@123.45.67.89

每60秒发一个“我还活着”的信号。

3. 生产环境推荐:autossh

autossh 会监控SSH连接,断开后自动重连,比 -f 更可靠。

安装:

sudo apt-get install autossh

使用(和ssh命令基本一样):

autossh -M 0 -f -L 3307:192.168.1.100:3306 root@123.45.67.89

4. 配置文件固化(省得每次敲长命令)

编辑 ~/.ssh/config:

Host mysql_tunnel
    HostName 123.45.67.89
    User root
    LocalForward 3307 192.168.1.100:3306
    ServerAliveInterval 60

之后只需执行:

ssh mysql_tunnel

五、服务器端到底要不要配置?

绝大多数情况下,服务器端不需要任何配置。 OpenSSH服务端默认开启了端口转发功能。

验证方法:查看服务器上的 /etc/ssh/sshd_config,默认包含:

AllowTcpForwarding yes    # 允许TCP转发,默认就是yes

唯一需要改的地方:GatewayPorts

如果你使用远程转发(-R),并且想让外网所有人都能访问,需要修改服务器配置。

问题现象:执行 -R 后,在服务器上查看端口:

netstat -tunlp | grep 8080

显示 127.0.0.1:8080 ——只有服务器本机能访问,外网连不上。

解决办法:

  1. 编辑服务器SSH配置文件:
sudo vi /etc/ssh/sshd_config
  1. 找到或添加:
GatewayPorts yes
  1. 重启SSH服务:
sudo systemctl restart sshd
  1. 重新执行客户端的 -R 命令,再查看端口,变成 0.0.0.0:8080,所有人都能访问了。

安全提醒:GatewayPorts yes 相当于你主动声明“我确认要把内网服务暴露给公网”,安全责任由你自己承担。默认绑在 127.0.0.1 是为了防止意外泄露内网资源。

服务器防火墙

如果服务器开了防火墙(如 ufw),需要确保:

  • SSH端口(22)是开放的
  • 如果用 -R 暴露了新端口(如8080),也需要放行
sudo ufw allow 8080/tcp

六、常见问题排查

报错信息原因解决办法
connect to host 123.45.67.89 port 22: Connection refused服务器SSH没开或IP错了检查服务器状态:sudo systemctl status ssh
Permission denied (publickey,password)密码或密钥不对确认密码,或检查 ~/.ssh/authorized_keys
bind: Address already in use本地端口被占用换个端口,如 -L 3308:...
channel_setup_fwd: ... Connection refused目标服务没开用 telnet 目标IP 端口 测试可达性
远程转发(-R)外网无法访问默认只监听127.0.0.1服务器改 GatewayPorts yes

查看隧道是否还活着

ps aux | grep ssh

看到你刚才的命令还在,就说明隧道正常。

关闭隧道

  • 没加 -f:按 Ctrl + C
  • 加了 -f:ps aux | grep ssh 找进程ID,然后 kill 进程ID

七、极简验证实验

不用改任何服务器配置,5分钟验证隧道原理:

在服务器上(开启一个测试服务):

nc -l 9999

(这个命令让服务器监听9999端口,输入什么就显示什么)

在本地Ubuntu:

ssh -L 8888:127.0.0.1:9999 root@服务器IP

在本地另一个终端:

nc 127.0.0.1 8888

输入任意文字,服务器那边的 nc -l 9999 终端里会同步显示。

原理:你连本地8888端口 → SSH加密传给服务器 → 服务器连自己的9999端口 → 数据返回。整个过程服务器配置没动过。

八、总结

SSH隧道的本质

通过SSH加密连接,把两台电脑的端口“接”在一起,让数据像走专线一样安全传输。

三种模式一句话记忆

  • -L = 本地开入口,通到目标(你去访问别人)
  • -R = 远程开入口,通到你(别人来访问你)
  • -D = 本地开万能入口(你去访问任何人)

服务器配置结论

  • 99%的情况:服务器零配置,开箱即用
  • 唯一例外:用 -R 暴露服务给外网所有人,需要改 GatewayPorts yes

适用场景

  • 安全访问内网数据库、内部网站
  • 内网穿透,让外网访问家里的服务
  • 在公共Wi-Fi下加密所有流量
  • 绕过网络限制访问受限资源

最后送你一句实践心法:先敲命令跑通,再回头理解原理。SSH隧道是“用中学”的技术,动手试一次,比看十篇文章都管用。


本文基于Ubuntu 20.04/22.04 + OpenSSH环境编写,其他Linux发行版通用。

作者

老丹

关注我
其他文章
上一个

公钥密码学标准(PKCS):一份深度技术档案

下一个

OATH TOTP 开放标准完全解读

关于博主

    老丹是一名C/C++后台开发工程师,信奉“无抽象不设计,无性能不生产”。

  • 技术栈:Modern C++、Linux环境编程、多线程/并发、网络编程等。
  • 信条:能用constexpr解决的问题绝不拖到运行时,能靠RAII避免的泄漏绝不写析构。
  • 正在填坑:从解封装到渲染的C++全链路实现,正在驯服FFmpeg与H.264/H.265。
  • 输出原则:这里的每一段代码都经过-Wall -Wextra -Werror -O2的洗礼。

近期文章

  • Linux系统的安全基石:深入理解可插拔认证模块(PAM) 2026年7月27日
  • vsftpd 完全指南:从核心原理到Docker容器化部署 2026年7月27日
  • 互联网的”导航”安全卫士:深入解读DNSSEC 2026年7月27日
  • Ubuntu DNS 配置完全指南 2026年7月27日
  • 在 Ubuntu 中使用 Certbot 的操作指南 2026年7月27日

文章分类

  • C/C++开发 (13)
  • Docker容器 (3)
  • Linux工具包 (10)
  • Linux服务配置 (33)
  • Linux系统 (10)
  • OpenWrt路由 (2)
  • Shell脚本 (3)
  • 安防技术 (4)
  • 数据安全 (30)
  • 网络协议 (17)
  • 计算机理论 (22)
联系我们:📍 地址:中国·广东省深圳市   |   ✉️ 邮箱:support@tanglinux.com   |   💬 QQ:870866607
版权所有:老丹的足迹粤ICP备2026061170号-1       公安备案图标 粤公网安备44030002013274号