fix: NAT 检测与安装脚本适配无 iptables 的系统

- diag_linux: 用 findExecutable 在 /usr/sbin /sbin 等常见路径查找 iptables/nft,弥补 systemd 服务 PATH 不含这些目录
- diag_linux: iptables 不存在时回退 nft list ruleset 检测 masquerade,两者均无时返回明确安装提示
- diag_linux: 区分"未安装"与"权限不足",MASQUERADE 检测说明更精确
- install_linux.sh: NAT 段加 NAT_TOOL 选择,优先 iptables 回退 nft,无则提示安装而非 set -e 退出
- install_linux.sh: 新增 nftables 配置分支(lmvpn_nat 表,postrouting masquerade + forward accept),持久化到 /etc/nftables.d/lmvpn.nft
This commit is contained in:
2026-07-06 14:53:30 +08:00
parent 6ccfb57c80
commit 691ae99451
2 changed files with 99 additions and 34 deletions
+50 -21
View File
@@ -52,32 +52,61 @@ echo ">>> 配置 iptables NAT 与转发规则..."
# 自动检测默认路由出口网卡 # 自动检测默认路由出口网卡
WAN_IFACE=$(ip route show default 2>/dev/null | awk '{print $5; exit}') WAN_IFACE=$(ip route show default 2>/dev/null | awk '{print $5; exit}')
if [ -z "$WAN_IFACE" ]; then if [ -z "$WAN_IFACE" ]; then
echo "警告: 未能自动检测出口网卡,跳过 iptables 配置,请手动设置" echo "警告: 未能自动检测出口网卡,跳过 NAT 配置,请手动设置"
else else
echo "出口网卡: $WAN_IFACE" echo "出口网卡: $WAN_IFACE"
# 幂等:先删除旧规则(忽略错误),再添加 # 选择可用的防火墙前端:优先 iptables,回退 nft
# NAT: VPN 子网经出口网卡做源地址转换 NAT_TOOL=""
iptables -t nat -D POSTROUTING -s "$VPN_SUBNET" -o "$WAN_IFACE" -j MASQUERADE 2>/dev/null || true if command -v iptables >/dev/null 2>&1; then
iptables -t nat -A POSTROUTING -s "$VPN_SUBNET" -o "$WAN_IFACE" -j MASQUERADE NAT_TOOL="iptables"
elif command -v nft >/dev/null 2>&1; then
NAT_TOOL="nft"
fi
# FORWARD: 放行 VPN 子网进出 if [ -z "$NAT_TOOL" ]; then
iptables -D FORWARD -s "$VPN_SUBNET" -j ACCEPT 2>/dev/null || true echo "警告: 未找到 iptables 或 nft,跳过 NAT 配置"
iptables -D FORWARD -d "$VPN_SUBNET" -j ACCEPT 2>/dev/null || true echo " 客户端将无法上外网。请安装: apt install iptables iptables-persistent"
iptables -A FORWARD -s "$VPN_SUBNET" -j ACCEPT echo " 安装后重新执行本脚本即可自动配置"
iptables -A FORWARD -d "$VPN_SUBNET" -j ACCEPT elif [ "$NAT_TOOL" = "iptables" ]; then
echo "使用 iptables 配置 NAT..."
# 幂等:先删除旧规则(忽略错误),再添加
iptables -t nat -D POSTROUTING -s "$VPN_SUBNET" -o "$WAN_IFACE" -j MASQUERADE 2>/dev/null || true
iptables -t nat -A POSTROUTING -s "$VPN_SUBNET" -o "$WAN_IFACE" -j MASQUERADE
# 持久化 iptables 规则 iptables -D FORWARD -s "$VPN_SUBNET" -j ACCEPT 2>/dev/null || true
if command -v netfilter-persistent >/dev/null 2>&1; then iptables -D FORWARD -d "$VPN_SUBNET" -j ACCEPT 2>/dev/null || true
netfilter-persistent save iptables -A FORWARD -s "$VPN_SUBNET" -j ACCEPT
echo "iptables 规则已通过 netfilter-persistent 持久化" iptables -A FORWARD -d "$VPN_SUBNET" -j ACCEPT
elif command -v iptables-save >/dev/null 2>&1; then
mkdir -p /etc/iptables # 持久化 iptables 规则
iptables-save > /etc/iptables/rules.v4 if command -v netfilter-persistent >/dev/null 2>&1; then
echo "iptables 规则已写入 /etc/iptables/rules.v4" netfilter-persistent save
echo "注意: 重启后规则持久化需配合 iptables-persistent 包,建议安装: apt install iptables-persistent" echo "iptables 规则已通过 netfilter-persistent 持久化"
else elif command -v iptables-save >/dev/null 2>&1; then
echo "警告: 未找到 iptables-save,规则仅在本次运行有效,请手动持久化" mkdir -p /etc/iptables
iptables-save > /etc/iptables/rules.v4
echo "iptables 规则已写入 /etc/iptables/rules.v4"
echo "注意: 重启后规则持久化需配合 iptables-persistent 包,建议安装: apt install iptables-persistent"
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
fi fi
+49 -13
View File
@@ -21,13 +21,9 @@ func fillPlatformDiag(r *DiagResult) {
r.IPForwardNote = "未开启,执行: sysctl -w net.ipv4.ip_forward=1" r.IPForwardNote = "未开启,执行: sysctl -w net.ipv4.ip_forward=1"
} }
m := checkMasquerade() m, note := checkMasquerade()
r.Masquerade = m r.Masquerade = m
if m == nil { r.MasqueradeNote = note
r.MasqueradeNote = "iptables 未安装或不可执行"
} else if !*m {
r.MasqueradeNote = "未检测到 MASQUERADE 规则,客户端无法出网"
}
} }
func ptrBool(b bool) *bool { return &b } func ptrBool(b bool) *bool { return &b }
@@ -63,12 +59,52 @@ func readIPForward() *bool {
return &v return &v
} }
func checkMasquerade() *bool { // findExecutable 在常见路径中查找可执行文件,弥补 systemd 服务 PATH 不含 /usr/sbin、/sbin 的问题
cmd := exec.Command("iptables", "-t", "nat", "-L", "POSTROUTING", "-n") func findExecutable(names ...string) string {
out, err := cmd.Output() for _, name := range names {
if err != nil { if p, err := exec.LookPath(name); err == nil {
return nil return p
}
for _, dir := range []string{"/usr/sbin", "/sbin", "/usr/bin", "/bin"} {
full := dir + "/" + name
if fi, err := os.Stat(full); err == nil && !fi.IsDir() {
return full
}
}
} }
has := strings.Contains(string(out), "MASQUERADE") return ""
return &has }
// checkMasquerade 检测 NAT masquerade 规则,优先 iptables,回退 nft
// 返回 (结果, 说明);结果为 nil 表示无法判定
func checkMasquerade() (*bool, string) {
// 优先 iptables
iptPath := findExecutable("iptables")
if iptPath != "" {
out, err := exec.Command(iptPath, "-t", "nat", "-L", "POSTROUTING", "-n").Output()
if err != nil {
return nil, "iptables 不可执行(权限不足?),无法检测 MASQUERADE"
}
has := strings.Contains(string(out), "MASQUERADE")
if has {
return &has, ""
}
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"
} }