From 05e0a6de62cda82aeb827351507fc809ac3d1130 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 6 Jul 2026 14:59:40 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20NAT=20=E6=A3=80=E6=B5=8B=E4=B8=8E?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E8=84=9A=E6=9C=AC=E4=BC=98=E5=85=88=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20nft=EF=BC=8C=E8=A7=A3=E5=86=B3=20iptables-nft=20?= =?UTF-8?q?=E4=B8=8D=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - diag_linux: checkMasquerade 改为优先 nft list ruleset,iptables 执行失败时 fall through 到 nft 而非直接返回 - diag_linux: 区分"iptables-nft 与原生 nft 表不兼容"与"权限不足"两种失败场景 - install_linux.sh: NAT 工具优先级反转为 nft 优先,iptables 回退,解决 Debian 12+ 上 iptables-nft 包装器操作原生 nft nat 表报 incompatible 的问题 - install_linux.sh: nft 分支增加 /etc/nftables.conf include 配置与 systemctl enable nftables,确保重启后规则自动加载 --- install_linux.sh | 50 ++++++++++++++++++++------------------ internal/vpn/diag_linux.go | 38 ++++++++++++++++------------- 2 files changed, 48 insertions(+), 40 deletions(-) diff --git a/install_linux.sh b/install_linux.sh index 99754e3..f9f2f90 100755 --- a/install_linux.sh +++ b/install_linux.sh @@ -48,7 +48,7 @@ net.ipv4.ip_forward = 1 EOF sysctl -p /etc/sysctl.d/99-lmvpn.conf >/dev/null -echo ">>> 配置 iptables NAT 与转发规则..." +echo ">>> 配置 NAT 与转发规则..." # 自动检测默认路由出口网卡 WAN_IFACE=$(ip route show default 2>/dev/null | awk '{print $5; exit}') if [ -z "$WAN_IFACE" ]; then @@ -56,18 +56,38 @@ if [ -z "$WAN_IFACE" ]; then else echo "出口网卡: $WAN_IFACE" - # 选择可用的防火墙前端:优先 iptables,回退 nft + # 选择可用的防火墙前端:优先 nft(原生,Debian 12+ 的 iptables 是 nft 包装器,操作原生 nft nat 表会不兼容) NAT_TOOL="" - if command -v iptables >/dev/null 2>&1; then - NAT_TOOL="iptables" - elif command -v nft >/dev/null 2>&1; then + if command -v nft >/dev/null 2>&1; then NAT_TOOL="nft" + elif command -v iptables >/dev/null 2>&1; then + NAT_TOOL="iptables" fi if [ -z "$NAT_TOOL" ]; then - echo "警告: 未找到 iptables 或 nft,跳过 NAT 配置" - echo " 客户端将无法上外网。请安装: apt install iptables iptables-persistent" + echo "警告: 未找到 nft 或 iptables,跳过 NAT 配置" + echo " 客户端将无法上外网。请安装: apt install nftables" echo " 安装后重新执行本脚本即可自动配置" + elif [ "$NAT_TOOL" = "nft" ]; then + echo "使用 nft 配置 NAT..." + # 幂等:先删除旧表(忽略错误),再创建 + nft delete table inet lmvpn_nat 2>/dev/null || true + nft add table inet lmvpn_nat + nft 'add chain inet lmvpn_nat postrouting { type nat hook postrouting priority 100 ; }' + nft add rule inet lmvpn_nat postrouting oifname "$WAN_IFACE" ip saddr "$VPN_SUBNET" masquerade + nft 'add chain inet lmvpn_nat forward { type filter hook forward priority 0 ; policy accept ; }' + nft add rule inet lmvpn_nat forward ip saddr "$VPN_SUBNET" accept + nft add rule inet lmvpn_nat forward ip daddr "$VPN_SUBNET" accept + + # 持久化 nft 规则 + mkdir -p /etc/nftables.d + nft list ruleset > /etc/nftables.d/lmvpn.nft + # 确保主配置文件 include 该目录 + if [ -f /etc/nftables.conf ] && ! grep -q 'include "/etc/nftables.d' /etc/nftables.conf; then + echo 'include "/etc/nftables.d/*.nft"' >> /etc/nftables.conf + fi + systemctl enable nftables 2>/dev/null || true + echo "nft 规则已写入 /etc/nftables.d/lmvpn.nft" elif [ "$NAT_TOOL" = "iptables" ]; then echo "使用 iptables 配置 NAT..." # 幂等:先删除旧规则(忽略错误),再添加 @@ -91,22 +111,6 @@ else else echo "警告: 未找到 iptables-save,规则仅在本次运行有效,请手动持久化" fi - elif [ "$NAT_TOOL" = "nft" ]; then - echo "使用 nft 配置 NAT..." - # 幂等:先删除旧表(忽略错误),再创建 - nft delete table inet lmvpn_nat 2>/dev/null || true - nft add table inet lmvpn_nat - nft 'add chain inet lmvpn_nat postrouting { type nat hook postrouting priority 100 ; }' - nft add rule inet lmvpn_nat postrouting oifname "$WAN_IFACE" ip saddr "$VPN_SUBNET" masquerade - nft 'add chain inet lmvpn_nat forward { type filter hook forward priority 0 ; policy accept ; }' - nft add rule inet lmvpn_nat forward ip saddr "$VPN_SUBNET" accept - nft add rule inet lmvpn_nat forward ip daddr "$VPN_SUBNET" accept - - # 持久化 nft 规则 - mkdir -p /etc/nftables.d - nft list ruleset > /etc/nftables.d/lmvpn.nft - echo "nft 规则已写入 /etc/nftables.d/lmvpn.nft" - echo "注意: 需确保系统启动时加载 nftables 规则,编辑 /etc/nftables.conf 添加: include \"/etc/nftables.d/lmvpn.nft\"" fi fi diff --git a/internal/vpn/diag_linux.go b/internal/vpn/diag_linux.go index e16895f..f050b66 100644 --- a/internal/vpn/diag_linux.go +++ b/internal/vpn/diag_linux.go @@ -75,14 +75,32 @@ func findExecutable(names ...string) string { return "" } -// checkMasquerade 检测 NAT masquerade 规则,优先 iptables,回退 nft +// checkMasquerade 检测 NAT masquerade 规则,优先 nft(原生、无兼容问题),回退 iptables // 返回 (结果, 说明);结果为 nil 表示无法判定 func checkMasquerade() (*bool, string) { - // 优先 iptables + // 优先 nft:Debian 12+ 的 iptables 是 nft 包装器,操作原生 nft nat 表会 "incompatible" + nftPath := findExecutable("nft") + if nftPath != "" { + out, err := exec.Command(nftPath, "list", "ruleset").Output() + if err == nil { + has := strings.Contains(string(out), "masquerade") + if has { + return &has, "" + } + return &has, "未检测到 masquerade 规则,客户端无法出网" + } + // nft 存在但执行失败(权限不足等),仍回退 iptables 尝试 + } + + // 回退 iptables(老系统或 nft 不可用时) iptPath := findExecutable("iptables") if iptPath != "" { out, err := exec.Command(iptPath, "-t", "nat", "-L", "POSTROUTING", "-n").Output() if err != nil { + // iptables-nft 与原生 nft 表不兼容时,若 nft 也读不到则判定为无法检测 + if nftPath != "" { + return nil, "iptables 与原生 nft 表不兼容且 nft 不可执行,无法检测 MASQUERADE" + } return nil, "iptables 不可执行(权限不足?),无法检测 MASQUERADE" } has := strings.Contains(string(out), "MASQUERADE") @@ -92,19 +110,5 @@ func checkMasquerade() (*bool, string) { return &has, "未检测到 MASQUERADE 规则,客户端无法出网" } - // 回退 nft - nftPath := findExecutable("nft") - if nftPath != "" { - out, err := exec.Command(nftPath, "list", "ruleset").Output() - if err != nil { - return nil, "nft 不可执行(权限不足?),无法检测 MASQUERADE" - } - has := strings.Contains(string(out), "masquerade") - if has { - return &has, "" - } - return &has, "未检测到 masquerade 规则,客户端无法出网" - } - - return nil, "iptables 与 nft 均未安装,无法检测 NAT 规则。Debian/Ubuntu 安装: apt install iptables iptables-persistent" + return nil, "iptables 与 nft 均未安装,无法检测 NAT 规则。Debian/Ubuntu 安装: apt install nftables" }