什么是 VPN 隧道,保护在哪里停止
隧道在你原有的链路之上,给特定数据包再套一层信封。跟踪一个请求,看看网站实际记录的到底是哪个地址。
客户端显示已连接,锁标记也在,网站却仍然记录着你家的地址。什么都没有坏。隧道并不取代你的 Wi-Fi 或移动链路;它给特定数据包套上第二层信封,再把信封沿着你原有的网络发出去。理解这套把戏,主要是跟踪一个请求,并注意信封在哪里被打开,因为几乎所有令人困惑的症状,从家庭地址泄露到大文件下载卡住,都来自那道边界。
两个信封,一条物理链路
笔记本保持着到路由器的正常连接,路由器也保持着到运营商的正常连接。隧道建立时,这条路径上的任何东西都没有改变。改变的是:被选中的数据包交给一个虚拟接口,包进一个以 VPN 对端为目标的新数据包,再作为普通流量从物理链路发出去。
接收端是一台设备,它拆开信封,把原始数据包转发向目的地。在两端之间,观察者只看得到外层包头,看不到内层的内容——除非协议对其实施加密,多数消费级协议都会加密,而通用技术本身并不要求。
这就是虚拟接口既真实又具有误导性的原因。它作为内核对象存在,有地址和最大传输单元,操作系统像对待其他接口一样把流量路由进去。它不是一根网线,它的存在只能证明某个组件已经安装。浏览器是否使用它由路由决定,而不是由接口是否处于开启状态决定。
观察一个请求的旅程
选一个目的地,问内核会走哪个接口。这一条命令就能解决大多数关于隧道是否起作用的争论:
ip route get 203.0.113.7
ip -brief addr show
ip -4 addr show dev wg0
如果路由输出指出隧道接口,数据包就会被封装。如果指出物理接口,就不会被封装,客户端重启多少次都改变不了。第二条命令把两个接口并排显示出来,你正是这样发现一个没有地址、或者地址属于错误协议族的隧道。
接着看信封内部的第一跳,并与外部的第一跳作对比:
traceroute -n -m 3 203.0.113.7
sudo tcpdump -ni wg0 -c 5
在隧道内部,第一跳是对端的内部地址,而不是你的路由器。在隧道接口上抓包,看到的是内层数据包,也就是应用以为自己正在发送的流量。同时在物理接口上抓包,看到的是外层数据包,两者合在一起,就是对隧道究竟做了什么的最清楚演示。
三个地址,网站只看得到其中一个
人们经常把这些混为一谈,混淆又会制造虚假的泄漏报告。它们是不同报头里的不同字段,生命周期也不同。
| Address | Where it appears | Who can see it |
|---|---|---|
| Endpoint address | Outer header, the peer you connect to | Your access network and the path to it |
| Inner tunnel address | Inside the envelope, on the virtual interface | The peer, and anything the peer routes to |
| Exit address | Source of the packet that reaches the website | The website and its upstream |
虚拟适配器旁边的数字是第二行。它不是网站记录的地址,把它说成你的公网 IP 完全是错的。网站看到的地址由出口分配,而在通往私有网络的隧道里,可能根本没有公网出口,因为目的地位于组织内部,而不是互联网上。
站点到站点的隧道是同一套机制,只是端点不同。两台路由器在两张网络之间封装流量,没有任何一台单独设备安装了客户端。信封在原理上相同,而管理边界不同。
信封在哪里被打开,以及什么能穿过它
隧道终止于它的对端。如果应用自身的加密延伸得更远,它就能穿过那道边界,这正是 HTTPS 的常态:浏览器的会话终止在网站,因此出口转发的是它读不懂的密文。
默认情况下别的都穿不过去。未加密的请求在出口可读,和它在咖啡馆 Wi-Fi 上一样可读,隧道并没有增加应用从来没有过的保护。同样,出口的防火墙规则和转发策略决定接下来发生什么;握手成功只是关于对端连接的事实,与旅程的其余部分无关。
开销是另一个可见后果。每个数据包都多带一个报头,外层传输还可能再加一个,于是每个包的可用载荷变小。在最大传输单元固定的路径上,隧道能通过 ping 却卡住大文件上传,原因正在于此。
核实应用实际走过的路径
最后一步是确认具体的应用,而不是整台机器。解析目的地,查看该地址的路由,然后在连接打开时观察它:
getent hosts example.com
ip route get "$(getent hosts example.com | awk '{print $1; exit}')"
ss -tnp state established '( dport = :443 )'
如果路由指出隧道,但套接字列表显示的连接源地址来自物理接口,那么应用绑定到了某个具体对象,或者有第二条隧道在争抢默认路由。这是路由问题,靠这三条输出就能回答,而不必看客户端的状态界面。
RFC 4301 安全架构描述了这种封装在 IPsec 中的 IP 层版本,同样的分层逻辑也适用于 WireGuard 和 OpenVPN。一旦你能把内层数据包和外层数据包当作两个独立对象指出,隧道的大部分行为就不再神秘,而变成一张你读得懂的路由表。