在 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.1010000 - 11999运行轻量级 Web、GOST 隧道
KVM 虚拟机10.0.0.2020000 - 21999运行 Windows Server、全虚拟化应用
特定服务10.0.0.30443 -> 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. 注意事项

  1. 防火墙冲突: 如果你开启了 PVE 自带的 Firewall,确保在“数据中心 -> 防火墙”或“节点 -> 防火墙”中允许这些端口通过,否则 IPTables 转发可能会被拦截。
  2. 回流 (Hairpin NAT): 如果你需要在内网通过公网 IP 访问这些端口,还需要额外配置回流规则,否则在容器内部访问公网 IP 会不通。
  3. 持久化: 建议将命令写入 interfaces 的 post-up 中,这样重启宿主机后规则依然有效。


1. 为什么需要回流?

在标准的 NAT 流程中:

  1. 内网容器(10.0.0.10)发起请求到公网 IP。
  2. 宿主机(10.0.0.1)收到请求,发现目标是自己,于是通过 DNAT 将目标地址改为内网服务 IP(10.0.0.20)。
  3. 内网服务直接回应给 10.0.0.10(因为它们在同一个网段)。
  4. 问题来了: 容器 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 解析到公网 IP 1.2.3.4。
  • 在内部,domain.com 直接解析到私网 IP 10.0.0.20。

这样流量根本不需要经过宿主机的 NAT 转换,性能也更好。

你的公网 IP 是静态的还是动态的? 如果是动态 IP,可能需要使用 MASQUERADE 替代 SNAT --to-source 来简化规则。