在 Proxmox VE (PVE) 环境下,由于宿主机通常只有一个公网 IPv4 地址,最灵活且标准的方法是使用 IPTables 或 Nftables 进行端口转发(NAT)。
你可以通过划分不同的端口范围,分别定向给 KVM 虚拟机和 LXC 容器。
1. 核心逻辑:NAT 网桥
首先,你需要确保 PVE 宿主机上有一个内部私有网桥(通常是 vmbr0 或新建一个 vmbr10),并开启了 IP 转发。
2. 配置步骤(以 IPTables 为例)
编辑宿主机的网络配置文件 /etc/network/interfaces。假设你的公网网卡是 eth0,私有网桥是 vmbr0(网段为 10.0.0.0/24)。
方案 A:直接在 Interfaces 文件中配置(持久化)
在 vmbr0 的配置下方添加 post-up 钩子:
auto vmbr0
iface vmbr0 inet static
address 10.0.0.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
# 开启 NAT 转发
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
post-up iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o eth0 -j MASQUERADE
post-down iptables -t nat -D POSTROUTING -s '10.0.0.0/24' -o eth0 -j MASQUERADE
# --- 端口范围分配示例 ---
# 1. 分配端口 10000-19999 给 LXC (假设 LXC IP 为 10.0.0.10)
post-up iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 10000:19999 -j DNAT --to-destination 10.0.0.10
post-up iptables -t nat -A PREROUTING -i eth0 -p udp --dport 10000:19999 -j DNAT --to-destination 10.0.0.10
# 2. 分配端口 20000-29999 给 KVM (假设 KVM IP 为 10.0.0.20)
post-up iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 20000:29999 -j DNAT --to-destination 10.0.0.20
post-up iptables -t nat -A PREROUTING -i eth0 -p udp --dport 20000:29999 -j DNAT --to-destination 10.0.0.20
3. 进阶:如何科学地管理端口范围?
如果你有很多容器和虚拟机,手动改 interfaces 容易出错。建议根据功能划分“端口段”:
| 资源类型 | 内部 IP 示例 | 外部端口范围 | 用途建议 |
|---|---|---|---|
| LXC 容器 | 10.0.0.10 | 10000 - 11999 | 运行轻量级 Web、GOST 隧道 |
| KVM 虚拟机 | 10.0.0.20 | 20000 - 21999 | 运行 Windows Server、全虚拟化应用 |
| 特定服务 | 10.0.0.30 | 443 -> 443 | 专门的 Anytype 或 Vaultwarden |
单个端口转发命令
如果你只想转发一个特定的端口(例如 RDP 或 SSH):
# 将宿主机的 3389 转发给 KVM 虚拟机的 3389
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 3389 -j DNAT --to-destination 10.0.0.20:3389
4. 注意事项
- 防火墙冲突: 如果你开启了 PVE 自带的 Firewall,确保在“数据中心 -> 防火墙”或“节点 -> 防火墙”中允许这些端口通过,否则 IPTables 转发可能会被拦截。
- 回流 (Hairpin NAT): 如果你需要在内网通过公网 IP 访问这些端口,还需要额外配置回流规则,否则在容器内部访问公网 IP 会不通。
- 持久化: 建议将命令写入
interfaces的post-up中,这样重启宿主机后规则依然有效。
1. 为什么需要回流?
在标准的 NAT 流程中:
- 内网容器(
10.0.0.10)发起请求到公网 IP。 - 宿主机(
10.0.0.1)收到请求,发现目标是自己,于是通过 DNAT 将目标地址改为内网服务 IP(10.0.0.20)。 - 内网服务直接回应给
10.0.0.10(因为它们在同一个网段)。 - 问题来了: 容器
10.0.0.10期待的是来自“公网 IP”的回包,结果收到了来自10.0.0.20的包。由于源地址不匹配,连接被静默丢弃。
2. IPTables 设置方法
假设:
- 公网 IP:
1.2.3.4 - 私有网段:
10.0.0.0/24 - 私有网桥:
vmbr0 - 内网服务 IP:
10.0.0.20 - 服务端口:
80
你需要增加一条 SNAT 规则,强制让经过回流的包在返回时也经过宿主机。
核心规则模板
# 1. 传统的 DNAT (你已经做的)
iptables -t nat -A PREROUTING -d 1.2.3.4 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.20
# 2. 回流 SNAT (关键:让发往内网服务的包,其源地址看起来像是网桥 IP)
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -d 10.0.0.20 -p tcp --dport 80 -j SNAT --to-source 10.0.0.1
3. 在 PVE /etc/network/interfaces 中的持久化配置
为了方便管理,你可以将之前的端口转发和回流合并在一起。
auto vmbr0
iface vmbr0 inet static
address 10.0.0.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
# 基础 NAT 转发 (上网用)
post-up iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o eth0 -j MASQUERADE
# --- 端口转发 + 回流设置 ---
# 转发 80 端口给 10.0.0.20
post-up iptables -t nat -A PREROUTING -d 1.2.3.4 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.20
# 增加回流规则
post-up iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -d 10.0.0.20 -p tcp --dport 80 -j SNAT --to-source 10.0.0.1
# 对应清理规则 (post-down)
post-down iptables -t nat -D PREROUTING -d 1.2.3.4 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.20
post-down iptables -t nat -D POSTROUTING -s 10.0.0.0/24 -d 10.0.0.20 -p tcp --dport 80 -j SNAT --to-source 10.0.0.1
4. 另一种“偷懒”的替代方案:本地 DNS
如果你觉得 Hairpin NAT 太麻烦,且容器/虚拟机不多,可以在 PVE 的内网环境中建立一个简单的 DNS 服务(或修改 /etc/hosts):
- 在外部,
domain.com解析到公网 IP1.2.3.4。 - 在内部,
domain.com直接解析到私网 IP10.0.0.20。
这样流量根本不需要经过宿主机的 NAT 转换,性能也更好。
你的公网 IP 是静态的还是动态的? 如果是动态 IP,可能需要使用 MASQUERADE 替代 SNAT --to-source 来简化规则。