V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
通过以下 Referral 链接购买 DigitalOcean 主机,你将可以帮助 V2EX 持续发展
DigitalOcean - SSD Cloud Servers
mdzz
V2EX  ›  VPS

用 nftables 缓解 SYN flood 攻击

  •  
  •   mdzz · 2023-03-19 10:26:03 +08:00 · 1557 次点击
    这是一个创建于 643 天前的主题,其中的信息可能已经有所发展或是发生改变。

    0x0: 初现端倪

    偶然一次登录到 VPS 上做日常维护, 用 ss -anpt 查看连接的时候发现大量连接处于 SYN-RECV 状态, 而且是同一 IP 大量连接到本机的 80 和 443 端口。 隔了一段时间后还是同样的情况,不过换了另一个 IP 。

    第一反应:“这不正常”。

    0x1: 肉鸡竟是我自己

    搜索后得知这是 SYN flood 的典型表现。

    攻击者向肉鸡发送大量伪造源地址的 SYN 包, 肉鸡收到 SYN 包后向伪造地址发送 SYN-ACK 包。 如果没有收到伪造地址的 ACK 包,则会重新发送 SYN-ACK 包。 重发机制使得攻击被放大了。 如下所示:

    A
     \ SYN
      \
       B
    \ \ \ \ \SYN-ACK
     \ \ \ \ \
          C
    

    虽然对单一肉鸡来讲,流量和性能都没有很大的影响, 但对被攻击者来说就是另一种感受了。

    0x2: 尝试 Synproxy

    启用 synproxy 模块后,TCP 三次握手将由 synproxy 模块处理, 并在连接建立成功后再向上层抛出这三个包。 从而保护上层系统不受攻击。

    首先配置需要的 sysctl 选项

    net.netfilter.nf_conntrack_tcp_loose = 0
    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_timestamps = 1
    

    /etc/nftables.conf 中设置 synproxy 相关规则

    table inet filter {
    	chain prerouting {
    		type filter hook prerouting priority raw; policy accept;
    
    		tcp dport { 80, 443 } tcp flags syn notrack
    	}
    
    	chain input {
    		type filter hook input priority filter; policy accept;
    
    		tcp dport { 80, 443 } ct state { invalid, untracked } synproxy mss 1460 wscale 7 timestamp sack-perm
    		ct state invalid drop
    	}
    }
    

    系统默认没有启用 nftables, 用 systemctl start nftables 启用即可。

    之后再观察流量,对于每个 SYN 攻击包,仅发送一次 SYN-ACK, 攻击的强度大大的减弱了。 如下图所示:

    A
     \ SYN
      \
       B
        \ SYN-ACK
         \
          C
    

    然而,每收到一个攻击包,相应的会发出一个回复包。 作为一台肉鸡,对此不是很认可,发出的包还是太多。 能不能识别出攻击包并直接丢弃呢?

    0x3: 更换防御策略

    现在已知 SYN flood 攻击时不会建立连接, 而正常情况下可以建立连接。 针对这一点的区别,可以将策略定为: 对于连接未成功的 IP 不允许再新建连接。

    具体来将就是在一个时间段内(下述配置中为 10 分钟), 同一 IP ,仅处理第一个 SYN 包, 后续的 SYN 包则直接丢弃。

    下面利用 nftables 规则实现此策略:

    table inet filter {
    	# 状态为连接中的 IP 集合
    	set syn_staging {
    		type ipv4_addr
    		size 65536
    		flags dynamic
    		timeout 10m
    	}
    
    	chain input_http_check {
    		# 来自原始方向的第二个包,即 ACK ,此时连接已建立,可以移除限制
    		ct original packets 2 ct state established delete @syn_staging { ip saddr }
    
    		ct state { established, related } accept
    
    		# IP 在集合中,不允许再新建连接,直接丢弃,并计数
    		ip saddr @syn_staging counter drop
    
    		# 将新连接的 IP 添加到集合中
    		ct state new update @syn_staging { ip saddr counter }
    	}
    
    	chain input {
    		type filter hook input priority filter; policy accept;
    
    		ct state invalid drop
    
    		# 对 HTTP 端口的数据做检查
    		tcp dport { 80, 443 } jump input_http_check
    	}
    }
    

    设置后观察一段时间, 可以通过 nft list set inet filter syn_staging 列出集合中的 IP 。 其中计数较大的就是被攻击者的 IP 。

    在被 flood 过程中, 本方案发包数量上要少于 synproxy。 但缺点也比较明显,在网络环境较差时容易造成误封。 所以在上述规则中设置超时时间较短, 仅处理 80 和 443 端口, 以减少对其他连接的影响。

    如果有误封的情况,可以用以下命令解封: nft delete element inet filter syn_staging { 1.2.3.4 }

    0x4: 后续优化

    上述规则也可以视为一种限速策略,限制单 IP 处于连接中的连接数量为 1 。 对于流量较大的服务器来说可能会无法接受。 那么能不能将限制数量从 1 改为 2 或者更大呢? 要实现这一点,需要下面两点:

    • 对集合中计数的自减操作(上述规则使用了自加操作用于计数)
    • 判断集合中的计数大小(超过限制后丢弃、等于零则删除)

    由于目前的规则已经大大缓解了 SYN flood 攻击,就没有再继续深究。

    0x5: 总结

    借此机会又回顾了一下 TCP 三次握手过程,巩固了相关知识。 同时上手体验了一下 nftables , 相对于 iptables 来讲, 有配置结构化、易于理解的优点。

    公网上的服务器还是要有一定程度的防护的, 不能太佛系,否则会被当成肉鸡。

    另外希望攻击者对于肉鸡列表做定期有效性检验, 然后把我的服务器从列表中移除。😂

    2 条回复    2023-03-20 09:45:02 +08:00
    datocp
        1
    datocp  
       2023-03-20 02:53:47 +08:00 via Android
    曾经有人说这是程序员专属语法,学习新东西真是累,


    -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m comment --comment "!fw3" -j syn_flood
    # iptables -S syn_flood
    -N syn_flood
    -A syn_flood -m limit --limit 100/sec --limit-burst 50 -j RETURN
    -A syn_flood -j DROP
    mdzz
        2
    mdzz  
    OP
       2023-03-20 09:45:02 +08:00
    @datocp
    OpenWrt 的规则条件定的比较高,实际流量达不到这么高,也就 2~3 分钟内几百个这样的水平,但是定低了影响正常流量。所以才设计的针对性规则
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2155 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 01:31 · PVG 09:31 · LAX 17:31 · JFK 20:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.