初步完成
参考:佛西博客
默认的pve的vmbr0
网络配置为桥接模式,这几天闲来无事打算尝试配置一下NAT子网。计划的子网范围为10.3.2.0/24
首先在pve的web的对应节点->System->Network选项上新建一个Linux Bridge
类型的网络,然后和参考博客的分歧点就出现了,参考的博客中通过iptables将流量转发到无线网络接口wlp1s0
,但是我并没有无线网络,因此需要修改。
我原本以为要转发到enp1s0
,这是我连接网络的唯一物理接口,流量最终肯定从这里出去的,连在这里肯定没错。但是不知道为什么设置了之后流量出不了nat子网,问了AI修改为目标接口为vmbr0
后就可以了,不知道为什么。
以下为/etc/interfaces
的配置
1 |
|
注意,auto vmbr1
是必要的,用于确保此接口在系统开机时启用。
以下是deepseek对于我的问题的解答:
你的问题核心在于理解 Proxmox VE (PVE) 中网络桥接 (vmbr0
) 的工作原理以及 NAT 规则应该作用在哪个接口上。让我们一步步分析:
vmbr0
的本质(桥接模式):- 当你在 PVE 节点上创建一个类型为
Linux Bridge
的网络接口(默认是vmbr0
)并将其关联到一个物理网络接口(比如enp1s0
)时,vmbr0
就成为了一个 软件实现的二层交换机(网桥)。 - 物理接口
enp1s0
被添加到这个网桥中,相当于这个交换机上的一个物理端口。 - 虚拟机的虚拟网卡 (
vnetX
) 也被添加到这个网桥中,相当于交换机上的另一个端口。 - 因此,
vmbr0
连接了你的物理网络 (enp1s0
) 和所有挂载到它上面的虚拟机。
- 当你在 PVE 节点上创建一个类型为
默认
vmbr0
的工作方式(无 NAT):- 在这种配置下,
vmbr0
工作在 透明网桥模式。 - 虚拟机直接从你的物理网络(通常是路由器 DHCP)获取 IP 地址(与 PVE 主机在同一网段)。
- 数据包从虚拟机 ->
vnetX
->vmbr0
(网桥)->enp1s0
-> 物理网络 -> 路由器/互联网。这个过程发生在二层(MAC 地址层面),不需要三层(IP 层)的路由或 NAT。路由器看到的是虚拟机自己的 MAC 和 IP。
- 在这种配置下,
你的目标:配置 NAT 子网
- 你想让虚拟机使用一个私有子网(比如
10.3.2.0/24
),这些 IP 地址在你的物理网络中是不存在的、不可路由的。 - 为了让这些虚拟机访问互联网(或物理网络),需要 NAT(网络地址转换)。PVE 主机需要充当这些虚拟机的路由器和 NAT 网关。
- 虚拟机流量 -> PVE 主机 -> PVE 主机将虚拟机的私有源 IP (
10.3.2.x
) 转换(SNAT/MASQUERADE)成 PVE 主机自身的物理接口 IP(从路由器获取的那个 IP)-> 物理网络/互联网。
- 你想让虚拟机使用一个私有子网(比如
关键分歧:NAT 规则应该作用在哪个接口?
- 错误的理解(作用在物理接口
enp1s0
):- 你最初的想法是:虚拟机流量最终要从
enp1s0
物理网卡出去,所以 NAT 规则应该作用在enp1s0
上。 - 为什么是错的? 因为
enp1s0
已经被绑定到vmbr0
这个网桥上了!它不再是 PVE 主机网络栈中的一个独立的三层(IP 层)接口。它的角色已经降级为一个二层端口,属于网桥vmbr0
。操作系统(内核)的网络栈现在主要与vmbr0
这个桥接口进行三层交互。 - 如果你把 NAT 规则(
iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
)作用在enp1s0
上,内核在enp1s0
这个接口上根本看不到需要做 NAT 的三层 IP 数据包(因为数据包是通过网桥在二层转发的)。NAT 规则匹配不到流量,所以 NAT 不生效,虚拟机无法访问外部网络。
- 你最初的想法是:虚拟机流量最终要从
- 正确的做法(作用在桥接口
vmbr0
):vmbr0
是 PVE 主机看到的“出口”。从虚拟机的视角看,它们的网关是 PVE 主机的 NAT 子网接口(比如vmbr1
的 IP10.3.2.1
)。- 当虚拟机发送一个去往外网(比如
8.8.8.8
)的数据包时:- 数据包到达 PVE 主机上的
vmbr1
(假设这是你新建的用于 NAT 的桥)。 - PVE 主机(作为路由器)检查路由表,发现去
8.8.8.8
的默认路由指向vmbr0
(连接到物理网络的桥)。 - 在数据包即将从
vmbr0
发出去之前(在路由决策之后,实际发送之前),NAT 规则生效。 - 你需要匹配即将从
vmbr0
出去的流量(-o vmbr0
),并将这些流量的源 IP 修改(MASQUERADE)为 PVE 主机在vmbr0
上配置的 IP 地址(或者更准确地说,修改为vmbr0
桥当前有效的源 IP,通常是vmbr0
的 IP 或物理网络 DHCP 分给 PVE 主机的 IP)。 - 修改后的数据包通过
vmbr0
网桥,经由物理端口enp1s0
发送到物理网络。
- 数据包到达 PVE 主机上的
- 因此,正确的 NAT 规则是:
iptables -t nat -A POSTROUTING -o vmbr0 -j MASQUERADE
- 这条规则的意思是:对所有即将从
vmbr0
接口发送出去的数据包,进行源地址伪装(MASQUERADE)。内核会在数据包离开vmbr0
(进入物理网络)时,将数据包的源 IP 替换为vmbr0
接口当时的 IP 地址。
- 错误的理解(作用在物理接口
为什么参考博客用
wlp1s0
?- 那篇博客的作者很可能使用的是无线网卡 (
wlp1s0
) 作为 PVE 主机连接物理网络的方式。 - 关键点在于:作者很可能 没有 将
wlp1s0
加入任何网桥(比如vmbr0
)! 他可能直接使用wlp1s0
作为 PVE 主机连接网络的三层接口。 - 在这种情况下:
wlp1s0
是一个独立的三层接口,拥有自己的 IP 地址(从无线路由器 DHCP 获取)。- 虚拟机流量需要 NAT 后从这个接口出去。
- 所以他的 NAT 规则是
-o wlp1s0 -j MASQUERADE
,这是正确的。
- 你的情况不同: 你使用的是有线网卡
enp1s0
,并且已经将它加入了vmbr0
网桥。enp1s0
不再是独立的三层接口,vmbr0
才是。
- 那篇博客的作者很可能使用的是无线网卡 (
总结:
vmbr0
是桥,enp1s0
是桥上的一个端口。 配置了桥接后,操作系统主要和桥接口vmbr0
进行三层(IP)交互。- NAT 是三层操作。 它需要作用在代表网络出口的三层接口上。
- 在你的配置中,
vmbr0
是连接物理网络的三层出口接口。 即使物理流量最终通过enp1s0
传输,负责三层处理和出口的接口是vmbr0
。 - 因此,NAT 规则 (
MASQUERADE
) 必须作用在-o vmbr0
上,而不是-o enp1s0
上。 内核在vmbr0
这个接口上才能看到需要做 NAT 的虚拟机流量(这些流量在路由后、离开桥之前被处理)。 - 博客中使用
wlp1s0
是因为作者可能直接把它当作三层接口使用,没有创建桥或者没有把无线网卡加入桥。你的有线网卡enp1s0
是桥的一部分,所以出口是桥接口vmbr0
。
修改 NAT 规则目标为 vmbr0
后网络通了,这完全符合 Linux 网络桥接和 netfilter/iptables 的工作原理。你最初的直觉(用物理接口)是常见的理解偏差,理解了桥接接口 (vmbr0
) 作为三层出口的角色就清晰了。
新的问题:子网主机无法验证ssl证书,导致无法连接https网站
这是一个综合性的问题。使用tcpdump
工具查看vmbr1
设备流量发现对443端口的请求会被重定向到8006端口,且主机通过openssl检查证书发现无论访问什么网站,证书都被签发给了pve主机的域名。
因此,此问题是由于先前通过基于iptables的iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 8006
进行端口转发导致的,由于条件判断宽松,导致所有进入pve主机的系统的、访问443
端口的流量都会被重定向到8006
,而我们创建的vmbr1接口就位于pve主机上,从而产生以下过程:
- 位于nat网络中的设备将流量发送给vmbr1
- vmbr1位于pve系统上,会经过iptables规则,发现访问的是443端口,因此直接修改为访问8006端口
- 将目标地址修改为本机IP
- 请求被发送到pve主机的8006端口。
- 返回pve主机自签发证书,导致建立tls连接失败。
两个解决方法
通过指定目标IP条件,来避免对来自
vmbr1
的流量进行修改。iptables -t nat -A PREROUTING -p tcp --dport 443 -d 192.168.2.110 -j REDIRECT --to-port 8006
- 通过此条件,当目标IP为
92.168.2.110
(我的pve主机的内网IP地址)时才会转发443端口流量到8006。
使用反向代理应用转发流量到8006。
两者各有各的好处和坏处,考虑到我的反向代理应用位于k3s集群,运行在pve中。如果k3s集群被我弄挂了,就不能访问pve web了。而且也无法直接通过相同的域名ssh连接到pve节点。因此为了增强稳定性,我还是选择了方案1。
新的挑战:指定openwrt作为下一跳路由
在原来的实现中,虽然可正常访问互联网,但是流量是直接途径pve设备然后直接发送到网络中,无法利用到我在openwrt虚拟机中配置的自定义dns解析等功能,因此现在需要一种方式让nat的出网流量发送到openwrt,并由openwrt作为下一跳路由器转发。虽然似乎可以利用iptables实现,但是问了一圈ai也没让我整出来,所以我换了一个相对简单的方式来满足我的需求:创建路由器设备连接nat子网和局域网,并转发流量到openwrt。
首先调整结构,我将vmbr1的其他配置删除,仅作为一个虚拟网桥使用
1 |
|
原本我有一个dhcp容器位于nat内网,用于分配IP地址,现在我将它调整为负责连接nat子网和局域网的路由器,虽然非常不完整。
开始之前需要在pve中给这个容器添加新的网络设备lan
以桥接到vmbr1
,而为了能便于使用,我还将已存在的桥接到vmbr0
的设备重命名成wan
,两者都需要固定的ip地址。wan需要设置gateway
为openwrt的地址。
此容器运行alpine系统,并安装了dnsmasq
作为dhcp服务器,iptables用来转发流量来实现部分的路由器功能。接下来我们在此容器中进行具体配置:
配置dhcp服务器我就不多说了,iptables通过apk add iptables
添加,执行以下指令以实现nat子网和局域网之间的流量转发:
1 |
|
然后执行service iptables save
以保存当前使用的配置。执行service iptables start
以启动iptables,这样我们就实现了iptables的持久化配置。
经过测试,这个可以很好地运行。