用 nftables 缓解 SYN flood 攻击

查看 29|回复 0
作者:mdzz   
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 来讲,
    有配置结构化、易于理解的优点。
    公网上的服务器还是要有一定程度的防护的,
    不能太佛系,否则会被当成肉鸡。
    另外希望攻击者对于肉鸡列表做定期有效性检验,
    然后把我的服务器从列表中移除。😂
  • 您需要登录后才可以回帖 登录 | 立即注册

    返回顶部