如何使用nftables在接口间转发时对IP地址段进行NAT转换
刚好碰到过类似的部署场景,我来帮你搞定这两个头疼的问题——不用让VPN客户端手动加路由,还能解决多设备间的vlan子网IP冲突,结合WireGuard的原生配置和nftables的段映射NAT就能完美解决,一步步来:
一、搞定客户端手动加路由的问题
其实不用碰nftables,直接在WireGuard服务端配置里就能自动给客户端推送目标子网的路由,省得用户每次连接都要敲命令:
- 编辑WireGuard服务端的
wg0.conf配置文件:- 如果你是给特定客户端推送路由,就在对应的
[Peer]段落里更新AllowedIPs,先把原来的192.168.0.0/24加进去(后续做NAT后再调整):[Peer] PublicKey = <你的客户端公钥> AllowedIPs = 10.0.0.2/32, 192.168.0.0/24 - 要是想给所有连接的客户端都推这条路由,就在服务端的
[Interface]段加PostUp命令,同时确保路由转发生效:[Interface] PrivateKey = <服务端私钥> Address = 10.0.0.1/16 ListenPort = 51820 PostUp = nft add rule ip nat POSTROUTING oifname wg0 masquerade; ip route add 192.168.0.0/24 dev eth0.vlan1 PostDown = nft delete rule ip nat POSTROUTING oifname wg0 masquerade; ip route del 192.168.0.0/24 dev eth0.vlan1
- 如果你是给特定客户端推送路由,就在对应的
- 重启WireGuard服务让配置生效:
wg-quick down wg0 && wg-quick up wg0
这样客户端一连接VPN,就会自动获取到目标子网的路由,再也不用手动执行ip route add了。
二、解决多设备vlan子网IP冲突:nftables段到段NAT映射
核心思路是把每个设备上的192.168.0.0/24子网,映射到各自WireGuard子网下的一个专属段——比如第一台设备用10.0.1.0/24映射192.168.0.0/24,第二台用10.1.1.0/24,这样就不会冲突了。用nftables的SNAT和DNAT就能实现整个网段的映射:
步骤1:准备nftables的NAT表和链
先确保nat表和必要的链存在(如果之前没配置过的话):
# 创建nat表(不存在就新建) nft add table ip nat # 创建POSTROUTING链,用来做源地址转换 nft add chain ip nat POSTROUTING { type nat hook postrouting priority 100 \; } # 创建PREROUTING链,用来做目的地址转换 nft add chain ip nat PREROUTING { type nat hook prerouting priority -100 \; }
步骤2:添加段到段的NAT规则
假设当前设备的WireGuard子网是10.0.0.0/16,我们把192.168.0.0/24映射到10.0.1.0/24:
1. SNAT:把vlan设备的源IP转成映射后的10段IP
当eth0.vlan1上的设备访问VPN客户端时,自动把源IP从192.168.0.x换成10.0.1.x:
nft add rule ip nat POSTROUTING oifname wg0 ip saddr 192.168.0.0/24 snat to 10.0.1.0/24
2. DNAT:把VPN客户端的目标IP转成vlan设备的真实IP
当VPN客户端访问10.0.1.x时,自动把目标IP换成对应的192.168.0.x:
nft add rule ip nat PREROUTING iifname wg0 ip daddr 10.0.1.0/24 dnat to 192.168.0.0/24
步骤3:保存规则,确保重启不丢失
把当前的nftables规则保存到配置文件,这样重启后还能生效:
nft list ruleset > /etc/nftables.conf
然后启用nftables服务开机自启:
systemctl enable --now nftables
步骤4:更新WireGuard的路由推送
现在客户端不需要访问原来的192.168.0.0/24了,而是访问映射后的10.0.1.0/24,所以要更新服务端的AllowedIPs:
[Peer] PublicKey = <客户端公钥> AllowedIPs = 10.0.0.2/32, 10.0.1.0/24
这样客户端连接后会自动获取10.0.1.0/24的路由,访问这个网段的流量会通过VPN转发,再被nftables转换成真实的192.168.0.0/24地址。
三、验证配置是否生效
- 查看WireGuard的连接状态,确认客户端的AllowedIPs已经更新:
wg show wg0 - 查看nftables的规则,确认SNAT和DNAT规则已经添加:
nft list table ip nat - 客户端连接VPN后,尝试ping
10.0.1.1(对应真实的192.168.0.1),看看能不能连通。
几个要注意的点
- 每台VPN设备的映射子网要不一样:比如第二台设备用
10.1.1.0/24映射192.168.0.0/24,第三台用10.2.1.0/24,这样映射后的网段就不会冲突了。 - 一定要开启Linux内核的IP转发:
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf sysctl -p - 如果你之前有其他nftables的转发规则,要确保这些NAT规则和现有规则兼容,别让流量被拦截了。
内容的提问来源于stack exchange,提问作者Oliver Gaskell

