VirtualBox 的 NAT 与桥接:虚拟机的 VPN 在哪里结束
NAT 把客户机藏在宿主机之后,因此宿主机上的隧道就会覆盖它。桥接给客户机自己的地址,因此隧道必须留在虚拟机内部。
每当虚拟机运行 VPN 客户端,两个不同的问题会同时出现,而答案并不相同。第一个问题是客户机内部的隧道是否保护宿主机,第二个问题是宿主机上的隧道是否覆盖客户机。决定这两者的是网卡模式,而不是 VPN 软件本身。NAT 模式让宿主机成为可见的出口,桥接模式则让客户机带着自己的地址直接上网络。
究竟是谁终止了加密会话
加密会话属于终止它的那台机器,客户端在虚拟机里运行时,那就是客户机。只有进入客户机虚拟网卡的流量才会经过那条隧道,而管理程序不会自作主张把另一台机器送进客户机。所以客户机可以维持一条可用隧道,同时宿主机仍在明文网络上浏览。反过来并不对称,这种不对称正是大多数教程弄错的地方。
外部网络看到的是谁的地址,这正是网卡模式回答的问题。这一点确定之后,覆盖就不再是策略问题,而变成一个机械问题。下面所有内容都从这两个事实推出。
NAT 会一直改写客户机,直到它看起来像宿主机
使用 NAT 时,管理程序会在数据包到达第一台交换机之前,把客户机的每个数据包改写成宿主机的地址。随后这些流量被交给宿主机自己的网络栈,在那里遵循宿主机的路由表,其中包括宿主机的隧道所拥有的默认路由。因此 NAT 客户机在完全没有配置的情况下就被宿主机上的隧道覆盖了,这与客户机到宿主机那个方向给人的暗示正好相反。
# on the host: adapter mode and NAT DNS behaviour
VBoxManage showvminfo "lab-vm" | grep -i -E "nic|nat"
VBoxManage modifyvm "lab-vm" --natdnshostresolver1 on
运行自己客户端的客户机会在客户机内部终止那条隧道,加密流量随后经由同一次改写出去,于是宿主机上的应用保有自己的路径和自己的出口地址。VirtualBox 在这种模式下也掌管名称解析,除非启用了 --natdnshostresolver1,它会用自己的机制转发客户机的查询。启用之后,客户机的查询被交给宿主机上的解析器,这在宿主机自身视图正确时修正了解析,同样也把一个可能只存在于隧道内部的解析器交了出去。修改设置之前必须关机,而且设置属于网卡,不属于客户机里的软件。
桥接让客户机拥有自己的租约和暴露面
桥接把客户机的帧送到物理网段,因此 DHCP 会发给它一个租约,网络把它当作一台独立的主机。每台被桥接的客户机都可以拥有一条带自己默认路由的独立隧道,而宿主机的路由表从不改变。代价是暴露:客户机会应答广播,出现在邻居表里,并受到交换机的端口安全和无线客户端隔离的约束。宿主机上的隧道同样覆盖不了这种布局,因为客户机的帧从不经过宿主机网络栈。
与 NAT 情形的这个差别值得直说,而不是留成暗示。被桥接的客户机只要流量必须走隧道,就需要自己的客户端和自己的凭据。如果要求客户机对本地网络保持不可见,那么 NAT 或者一条受控路由的纯内部网络才是更好的设计。
为什么客户机不能成为宿主机的网关
把客户机的隧道借给宿主机,在两大管理程序里都没有受支持的开关。宿主机没有通往客户机隧道的路由,也没有指向那里的转换规则,因此客户机与宿主机的流量仍是各自归属的独立数据流。无论网卡模式如何,这个方向都行不通。
可行的替代是转发和代理。SSH 本地转发让宿主机连接自己的回环地址,同时由客户机把数据流带进隧道,这在不安装第二个客户端的前提下覆盖了 TCP 服务。让宿主机自己的 IP 流量改走客户机的隧道,则需要客户机内部有一个路由器,并在宿主机上设置明确的路由和转换。那是一项小型网络设计,而不是勾一个复选框,并且需要为回程路径做计划。如果客户机存在的唯一目的就是跑客户端,那就把客户端放到宿主机上,删掉多余的一跳。
ssh -N -f -L 8080:internal-host.example:80 user@lab-vm
解析器和路由都不会跨过边界
DNS 跟随接收该选项的那块网卡,因此客户机不会继承宿主机的隧道解析器,宿主机也从不知道被推给客户机的服务器。被推送的 dhcp-option DNS 只对接受它的接口生效,所以没有客户端的桥接客户机无法解析内部名称。NAT 客户机通常改用管理程序的 DNS 处理,差别在每个环境里用一条命令就能看出。
# on the host
VBoxManage showvminfo "lab-vm" | grep -i -E "nic|nat"
# inside a Linux guest
ip -4 route
resolvectl status
# inside a Windows guest
Get-DnsClientServerAddress
在假设解析器或路由跟随宿主机之前,先在客户机内部把它们读出来。如果内部名称必须在客户机里解析,就把隧道的解析器加到客户机自己的网卡上,然后用一个只存在于隧道后面的名称来验证。为每台虚拟机记录网卡模式、隧道归属和解析器来源,因为这三项事实几乎能回答之后的所有问题。其他管理程序会改掉模式的名称,却让这个原理保持不变。