SSH 转发实际承载的是哪一类应用流量?
Shell、本地转发、SOCKS 与隧道设备模式暴露的是不同的接口。在把它和路由式 VPN 相比之前,先确认你的应用能用哪一种。
终端连上了,提示符出现了,所有人都同意 SSH 访问是通的。然后笔记本上的数据库客户端依然连不到内网主机,于是这次会话被当成罪魁祸首。实际搭起来的只是一个 shell,而它只是 SSH 能提供的四种相当不同的接口之一。先给接口命名,再验证应用真的在用这个接口。顺序很重要,因为每种接口都有各自的失败方式。
先给接口命名,再作任何比较
OpenSSH 客户端手册区分了四种容易混淆的机制。shell 或远程命令在 SSH 主机上执行。本地转发在客户端打开一个监听套接字,由远端发起后续连接。远程转发相反,在服务器一侧监听。动态转发则为懂得使用它的程序提供一个 SOCKS 端点。
- Shell:程序在远端机器上运行,本地没有任何监听。
- 本地转发:你的笔记本上有一个端口,由远端主机发起外连。
- 远程转发:远端一侧有一个端口,流量回到你这边。
- 动态:客户端上的 SOCKS 代理,只有懂得代理的应用才会用到。
这些机制都不安装路由。无视代理设置、使用 UDP、自行解析域名的程序,不会受到活跃 SSH 会话的影响。这不是 SSH 的缺陷,而是你选的那个接口的边界。
本地、远程与动态转发的区别在于谁在监听
决定大多数情形的差别在于监听套接字归谁所有。本地转发由客户端监听,由远端去拨号目标,因此目标地址是按服务器的视角解析的。远程转发由服务器监听,连接被带回你这一侧,这就是在不直接公开的情况下暴露本地服务的方式。
ssh -N -L 5432:db.internal.example:5432 [email protected] ssh -N -D 1080 [email protected] ss -ltnp | grep -E '5432|1080'
第一条命令在笔记本上绑定 5432 端口,并转发到从堡垒机视角看到的数据库。第二条在 1080 端口上建立 SOCKS 代理。第三条命令确认到底是哪些进程在监听,而这是转发确实存在、而不是因端口冲突悄悄失败的唯一证据。
动态转发值得单独提醒:它只覆盖遵守 SOCKS 的应用。浏览器可以指向它,命令行工具可以被告知使用它,但自带连接栈的数据库驱动通常做不到。以为机器上每个程序现在都走隧道,是这个领域里最常见的错误。
隧道设备模式是唯一的真正网络例外
OpenSSH 也支持 -w 隧道设备转发,它传递的是点对点接口而不是 TCP 监听器。那是真正的网络隧道,而且是刻意配置的,不是打开 shell 的副作用。
sudo ssh -w 0:0 [email protected] ip addr show tun0
它需要两端都有相应的接口编号、服务器上有正确的配置,以及客户端有足够的权限来创建设备。它还需要地址与路由规划,因为裸接口并不携带哪些目标该走它的策略。
即便如此,隧道终止在远端主机。SSH 传输提供的保护在那里结束,后续连接是另一跳。VPN 网关的边界形状相同,所以比较应当围绕覆盖范围和终止点,而不是谁更强。把这条边界明说出来,也顺带结束了隧道是否「加密一切」的争论,因为显然没有。
验证你的应用确实在用你搭的接口
验证是按应用而不是按会话进行的。转发能用只证明端口已绑定、远端能够拨号;它不证明你在意的那个程序走了这条通路。
curl -sS --socks5-hostname 127.0.0.1:1080 -o /dev/null -w '%{http_code}\n' https://app.internal.example/health
psql "host=127.0.0.1 port=5432 dbname=app sslmode=require" -c 'select 1'第一条命令通过 SOCKS 端点发请求,主机名由远端解析;第二条在仍然要求 TLS 的前提下测试被转发的数据库端口。把转发停掉再跑同样的测试,确认它们会失败,因为两边都通过的测试从来没有用过隧道。
然后只在真正重要的维度上与路由式 VPN 比较:覆盖哪些目标、哪些协议可用、保护在哪里结束、谁必须批准访问。单一服务的转发可能是更窄也更好的答案,而当许多应用需要同一条网络路径时,VPN 才是对的形状。两者都不给予从各自远端可以到达的一切。