logo NodeSeekbeta

大家帮我看看这个 PO0的nftables.conf还如何修改

#!/usr/sbin/nft -f

# 清空旧规则
flush ruleset

# =======================================================
# 📝 全局变量定义
# =======================================================
define RELAY_LAN_IP  = 10.100.100.110
define SSH_PORT      = 1001

# 白名单 IP / 网段
define WHITE_LIST_IPS = { 
    59.175.100.0/24, 
    59.175.100.0/24, 
    10.6.201.0/24, 
    192.168.0.0/24 
}

# =======================================================
# 🌐 核心转发与安全表
# =======================================================
table ip nat {
    # 📌 白名单集合
    set white_list {
        type ipv4_addr
        flags interval
        elements = $WHITE_LIST_IPS
    }

    # 📌 端口转发映射表 (仅需在此维护此表)
    map forward_map {
        type inet_service : ipv4_addr . inet_service
        elements = {
            10086 : 172.81.111.111 . 95004,
            20086 : 172.81.111.111  . 20001,
            9527  : 23.249.111.111  . 95007,
            50086 : 10.77.111.111   . 97009,
            60086 : 10.77.111.111   . 98009,
            60099 : 10.42.111.111    . 85009,
            9528  : 23.249.111.111  . 95008,
            9529  : 10.77.111.111   . 95009,
            26242 : 23.249.111.111  . 26002
        }
    }

    chain prerouting {
        type nat hook prerouting priority dstnat; policy accept;
        
        # 严格安全转发:仅允许白名单,且在 map 中存在的端口才会触发 DNAT
        ip saddr @white_list meta l4proto { tcp, udp } dnat to th dport map @forward_map
    }

    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        
        # 🌟 完美替代方案:只要该连接被执行过 DNAT (证明其来自 forward_map),就自动执行 SNAT
        ct status dnat meta l4proto { tcp, udp } snat to $RELAY_LAN_IP fully-random
    }
}

table ip filter {
    # 🛡️ 本机入站安全控制
    chain input {
        type filter hook input priority 0; policy drop;

        # 1. 放行本地回环接口流量
        iifname "lo" accept

        # 2. 放行已建立连接和相关联的流量
        ct state established,related accept

        # 3. 🔓 全局放行:允许全球任意 IP 访问本机的 1001 端口
        tcp dport $SSH_PORT accept
    }

    # 🔄 流量中继转发控制
    chain forward {
        type filter hook forward priority 0; policy drop;
        
        # 1. 优先修改 TCP SYN 包的 MSS
        meta l4proto tcp tcp flags syn tcp option maxseg size set 1452


        # 2. 放行已建立连接的转发流量
        ct state established,related accept

        # 3. 放行通过白名单并成功 DNAT 的新连接
        ct state new ct status dnat accept
    }
}

  • 来自chatgpt的否定

    这个脚本的整体思路是:

    公网/入口端口 → 白名单校验 → DNAT 到后端 IP:端口 → SNAT 成 $RELAY_LAN_IP → 放行已 DNAT 的转发流量。

    思路没问题,但当前脚本有几个严重问题,其中有些会直接导致 nftables 规则加载失败。


    一、最大问题:端口号超过 65535,脚本会直接失败

    你的 forward_map 里有很多目标端口是非法的:

    10086 : 172.81.111.111 . 95004,
    9527  : 23.249.111.111 . 95007,
    50086 : 10.77.111.111  . 97009,
    60086 : 10.77.111.111  . 98009,
    60099 : 10.42.111.111  . 85009,
    9528  : 23.249.111.111 . 95008,
    9529  : 10.77.111.111  . 95009,
    

    TCP/UDP 端口范围只能是:

    1 - 65535
    

    所以这些端口全部不合法:

    95004
    95007
    97009
    98009
    85009
    95008
    95009
    

    只有下面这种是合法的:

    20086 : 172.81.111.111 . 20001,
    26242 : 23.249.111.111 . 26002
    

    这是当前脚本最关键的问题。你必须把后端端口改成真实存在的、合法的端口。


    二、table ip 只管 IPv4,IPv6 会完全裸奔

    你开头写了:

    flush ruleset
    

    这会清空所有 nftables 规则,包括 IPv6 防火墙规则。

    但是你后面只写了:

    table ip nat
    table ip filter
    

    table ip 只对 IPv4 生效,不管 IPv6。

    也就是说,如果这台机器有公网 IPv6,那么执行这个脚本后:

    IPv4 有防火墙
    IPv6 基本没有防火墙
    

    这是很危险的。

    如果这台机器有 IPv6,建议至少加一个 table inet filter 或者单独加 table ip6 filter,否则 SSH、面板、Docker 服务、系统服务都有可能从 IPv6 暴露出去。


    三、SNAT 到 10.100.100.110 不一定成立

    你这里写的是:

    ct status dnat meta l4proto { tcp, udp } snat to $RELAY_LAN_IP fully-random
    

    也就是把经过 DNAT 的连接统一伪装成:

    10.100.100.110
    

    这只有在一种情况下是合理的:

    后端服务器能够正常把返回流量发回 10.100.100.110
    

    如果你的后端是内网机器,例如:

    10.77.111.111
    10.42.111.111
    

    并且它们跟 10.100.100.110 在可达的内网/VPN/专线环境里,那么这个写法可能没问题。

    但如果后端是公网 IP,例如:

    172.81.111.111
    23.249.111.111
    

    你把源地址 SNAT 成 10.100.100.110,公网后端看到的是一个私网源地址,返回包大概率回不来。

    对于转发到公网 IP 的场景,一般应该用:

    masquerade
    

    或者 SNAT 成出口网卡上的公网 IP,而不是 10.100.100.110


    四、fully-random 一般没必要

    这句:

    snat to $RELAY_LAN_IP fully-random
    

    fully-random 会随机化源端口。不是不能用,但大多数端口转发场景不需要。

    更稳妥的写法通常是:

    snat to $RELAY_LAN_IP
    

    或者:

    masquerade
    

    除非你明确需要强随机源端口,否则不建议默认加 fully-random


    五、没有限制入口网卡,所有接口都会触发 DNAT

    你的 DNAT 规则是:

    ip saddr @white_list meta l4proto { tcp, udp } dnat to th dport map @forward_map
    

    这意味着只要源 IP 在白名单里,不管从哪个网卡进来,只要访问这些端口,都可能触发 DNAT。

    如果这台机器有多个网卡,例如:

    公网网卡 eth0
    内网网卡 eth1
    VPN 网卡 tun0
    Docker 网桥 docker0
    

    建议加入口网卡限制,比如:

    iifname "eth0" ip saddr @white_list meta l4proto { tcp, udp } dnat to th dport map @forward_map
    

    否则内网访问、VPN 访问、本机相关流量可能会被误 DNAT。


    六、白名单里有重复项

    这里重复了:

    59.175.100.0/24, 
    59.175.100.0/24,
    

    不影响功能,但应该清理掉。

    建议改成:

    define WHITE_LIST_IPS = { 
        59.175.100.0/24, 
        10.6.201.0/24, 
        192.168.0.0/24 
    }
    

    七、本机入站规则过于严格,ICMP 被全部丢弃

    你的 input 链是:

    chain input {
        type filter hook input priority 0; policy drop;
    
        iifname "lo" accept
        ct state established,related accept
        tcp dport $SSH_PORT accept
    }
    

    这会导致本机不响应 ping,也可能影响一些网络诊断和路径 MTU 发现。

    建议至少加:

    ip protocol icmp accept
    

    或者更精细一点放行必要 ICMP。

    否则以后你排查网络时会很麻烦。


    八、SSH 端口是全球开放的

    你注释里写得很明确:

    # 允许全球任意 IP 访问本机的 1001 端口
    tcp dport $SSH_PORT accept
    

    这不是语法问题,但从安全角度看不太好。

    如果这个 1001 是 SSH,建议改成白名单访问:

    ip saddr @white_list tcp dport $SSH_PORT accept
    

    但注意:你现在的 white_list 集合定义在 table ip nat 里,table ip filter 不能直接引用它。

    所以如果想在 input 链里复用白名单,建议把 filter 和 nat 放在同一个 table inet / table ip 里,或者在 filter 表里重新定义一个白名单 set。


    九、ct status dnat 的思路是对的,但要注意它会匹配所有 DNAT 连接

    你这里:

    ct state new ct status dnat accept
    

    这个逻辑是合理的,表示只放行经过 DNAT 的新连接。

    因为你的 DNAT 前面已经做了白名单判断,所以理论上非白名单不会进入 DNAT,也不会被 forward 放行。

    这部分设计是可以的。


    十、MSS 修改规则可以更精确

    当前写法:

    meta l4proto tcp tcp flags syn tcp option maxseg size set 1452
    

    可以工作的话问题不大,但它对所有进入 forward 链的 TCP SYN 都修改,不只限于 DNAT 流量。

    建议更精确一点:

    ct status dnat meta l4proto tcp tcp flags syn tcp option maxseg size set 1452
    

    或者如果你有固定出口接口,可以配合 oifname 限制。


    建议修改方向

    方案一:先修正最小问题

    至少先改这几处:

    # 1. 修正所有超过 65535 的端口
    # 2. 删除重复白名单
    # 3. 如果有 IPv6,补充 IPv6 防火墙
    # 4. 明确 SNAT 是不是应该用 10.100.100.110
    # 5. 建议 SSH 改为白名单访问
    

    尤其是端口号问题,不改脚本基本无法正常加载。


    方案二:更稳妥的结构示例

    下面是一个更稳的框架,注意里面的目标端口我没有替你乱改,超过 65535 的必须你自己换成真实合法端口。

    #!/usr/sbin/nft -f
    
    flush ruleset
    
    define RELAY_LAN_IP = 10.100.100.110
    define SSH_PORT     = 1001
    define WAN_IF       = "eth0"
    
    table ip relay {
        set white_list {
            type ipv4_addr
            flags interval
            elements = {
                59.175.100.0/24,
                10.6.201.0/24,
                192.168.0.0/24
            }
        }
    
        map forward_map {
            type inet_service : ipv4_addr . inet_service
            elements = {
                20086 : 172.81.111.111 . 20001,
                26242 : 23.249.111.111 . 26002
            }
        }
    
        chain prerouting {
            type nat hook prerouting priority dstnat; policy accept;
    
            iifname $WAN_IF ip saddr @white_list meta l4proto { tcp, udp } dnat to th dport map @forward_map
        }
    
        chain postrouting {
            type nat hook postrouting priority srcnat; policy accept;
    
            ct status dnat meta l4proto { tcp, udp } snat to $RELAY_LAN_IP
        }
    
        chain input {
            type filter hook input priority 0; policy drop;
    
            iifname "lo" accept
            ct state established,related accept
            ct state invalid drop
    
            ip protocol icmp accept
    
            # 建议 SSH 也走白名单
            ip saddr @white_list tcp dport $SSH_PORT accept
        }
    
        chain forward {
            type filter hook forward priority 0; policy drop;
    
            ct state invalid drop
    
            ct status dnat meta l4proto tcp tcp flags syn tcp option maxseg size set 1452
    
            ct state established,related accept
            ct state new ct status dnat accept
        }
    }
    

    还需要检查系统转发开关

    nftables 写对了还不够,系统必须开启 IPv4 转发:

    sysctl net.ipv4.ip_forward
    

    如果结果是:

    net.ipv4.ip_forward = 0
    

    需要开启:

    echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-ip-forward.conf
    sysctl -p /etc/sysctl.d/99-ip-forward.conf
    

    建议你先这样验证

    把脚本保存后,不要直接加载,先检查语法:

    nft -c -f /etc/nftables.conf
    

    如果检查通过,再加载:

    nft -f /etc/nftables.conf
    

    查看实际规则:

    nft list ruleset
    

    看转发连接:

    conntrack -L | grep dnat
    

    我的判断

    这个脚本的核心问题不是 DNAT/SNAT 思路,而是这几个点:

    1. 多个目标端口超过 65535,脚本会失败
    2. 只写了 IPv4,IPv6 可能完全裸奔
    3. SNAT 到 10.100.100.110 是否正确,要看后端网络路径
    4. SSH 1001 全球开放,安全风险较高
    5. 没有限制入口网卡,可能误匹配其他接口流量
    

    优先级最高的是:

    先修正非法端口 → 再确认 SNAT 地址 → 再补 IPv6 防护 → 最后优化 SSH 和入口接口限制
    
  • 让ai给优化不好吗?

  • 直接问ai更快

  • 我一般是发给AI,它弄这些跟玩儿一样

  • 直接用老陈给的脚本就行,还是说你这款写了bot呢?

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有59865位seeker

🎉欢迎新用户🎉