上一篇 下一篇 分享链接 返回 返回顶部

香港服务器上的CentOS 7系统如何通过内核参数限制SYN Flood攻击,防御大规模DDoS?

发布人:Minchunlin 发布时间:2025-08-27 09:20 阅读量:612


半夜 2:17,NOC电话把我吵醒,香港机房两台前端 Web(VIP 在云上做四层直通)突然 SYN_RECV 飙升,LB 健康探测间歇失败,访问从“卡顿”到“白屏”。监控上 pps(packets per second)拉升到 3.5M pps,80/443 的半连接排队瞬间堆满。
我赶到机房,冷通道温度 18℃,交换机蓝灯扎眼。首先确认不是上游黑洞(null-route)或清洗(scrubbing)的误判,然后在业务允许的范围内,决定先用内核参数把 TCP 半连接态管住,再用 iptables SYNPROXY 做前置握手,缓和内核队列压力;同时把 conntrack / backlog / rps 做到位,保证高并发下不崩。

环境与基线

硬件/网络(当班现场)

机房:香港(多线 BGP,国际带宽 100G 汇聚,上游支持 RTBH)

服务器(两台同配,前端 Web 层):

  • CPU:Intel Xeon Silver 4210R(10C/20T)× 2
  • 内存:128GB DDR4
  • 系统盘:2 × 480GB SSD(RAID1)
  • 数据盘:2 × 1.92TB NVMe(RAID1)
  • NIC:Intel X710 10GbE × 2(LACP 到接入交换机)

系统:CentOS 7.9(3.10 内核)

内核模块:nf_conntrack、nf_synproxy_core

用户态:Nginx(worker_processes 适配 CPU)、Keepalived(VRRP 备份 VIP,LB 健康探测)

流量画像(攻击开始 5 min 内)

  • 主要特征:TCP SYN 到 80/443,源 IP 分布分散、TTL 不统一、MSS 异常值较多
  • 峰值:约 3.5M pps(小包为主)
  • 现象:SYN_RECV 暴涨,tcp_max_syn_backlog 被打满,Nginx 日志 2xx 下降、499/502 上升

处置目标

不影响后端已建立连接;2) 控制半连接态,避免内核资源被拖死;3) 让“能握手的”尽量握手成功;4) 可回滚、可复盘。

总体策略(顺序很关键)

  • 先把内核兜底打开:SYN Cookies、backlog、队列、内存水位等——防止立刻倒。
  • 再把 conntrack 和 SYNPROXY 打成体系:减少半连接进入 conntrack、让攻击在内核前置被“虚接”。
  • 最后收口:细化速率限制、日志降噪、NIC/IRQ/队列微调;观察指标,滚动收敛。

实操步骤(可直接落地)

所有操作在 CentOS 7 上验证过。若使用 firewalld,可用 direct 规则或暂时切换 iptables-services。以下以 iptables 为例。

1. sysctl:内核参数兜底

新建配置 /etc/sysctl.d/99-ddos-syn.conf:

# ---- 基础防护:SYN/半连接/队列 ----
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 3
net.ipv4.tcp_max_syn_backlog = 131072
net.core.somaxconn = 16384
net.core.netdev_max_backlog = 65536

# ---- conntrack 容量与超时(避免表被打满)----
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_buckets = 262144
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 30

# ---- TCP 连接与内存姿态 ----
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_fin_timeout = 30
net.ipv4.ip_local_port_range = 10000 65535

# ---- 选项/健壮性 ----
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1

# ---- 反射路径过滤(多宿主环境用 2)----
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2

# ---- RPS/RFS 可按需开启(示例值,视 CPU/网卡队列调优)----
# net.core.rps_sock_flow_entries = 8192

应用:

sysctl --system
# 或
sysctl -p /etc/sysctl.d/99-ddos-syn.conf

注:tcp_syncookies=1 是兜底手段,Linux 在 SYN Cookie 路径下对 TCP 选项的支持已较早期好很多,但仍可能丢失部分选项细节(比如窗口扩大信息可能退化)。只在“被打”的时段打开,恢复平稳后可视压测再调整。

2. 安装与启用 iptables(如使用 firewalld,可跳过本节改用 direct)

yum install -y iptables-services
systemctl enable iptables
systemctl stop firewalld || true
systemctl start iptables

加载内核模块(一般会自动):

modprobe nf_conntrack
modprobe nf_synproxy_core

3. 关键:在 raw 表“不过 conntrack”+ INPUT 上做 SYNPROXY

要点:让初始 SYN 包不进入 conntrack,避免 conntrack 表先被打满;由 SYNPROXY 替我们完成握手,确认对端真能回 ACK,再放行到本机协议栈。

规则示例(80/443):

# 3.1 raw 表:对新建 SYN 不做跟踪(不过 conntrack)
iptables -t raw -I PREROUTING -p tcp -m tcp --syn -j CT --notrack

# 3.2 先丢奇怪的包,减负
iptables -I INPUT -m conntrack --ctstate INVALID -j DROP
iptables -I INPUT -p tcp ! --syn -m conntrack --ctstate NEW -j DROP

# 3.3 对新连接的 SYN 用 SYNPROXY“虚接”
# 参数含义:
# --sack-perm 允许 SACK
# --timestamp 允许时间戳
# --wscale 7 窗口扩大(根据内网/应用压测可调)
# --mss 1460(10G 环境下以 MTU 1500 估,若有隧道或 MTU 变化需相应调整)
iptables -I INPUT -p tcp -m tcp --dport 80  -m conntrack --ctstate NEW \
  -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
iptables -I INPUT -p tcp -m tcp --dport 443 -m conntrack --ctstate NEW \
  -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460

# 3.4 已建立的放行
iptables -I INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# 3.5 (可选)对异常高频 SYN 做简单速率限制(防误杀,先放宽)
iptables -I INPUT -p tcp --syn -m hashlimit \
  --hashlimit 2000/second --hashlimit-burst 4000 \
  --hashlimit-mode srcip --hashlimit-name syn_rate \
  -j ACCEPT

保存:

service iptables save
# 规则会写入 /etc/sysconfig/iptables,随服务启动加载

坑 1:很多人的 SYNPROXY 不生效,是因为忘了 raw 表 notrack,结果 SYN 先把 conntrack 打爆,后面都谈不上。

坑 2:--mss 请和实际 MTU/链路情况匹配;若上游有 GRE/隧道,MSS 需要适配。

坑 3:线上先“单端口灰度”,确认再批量;SYNPROXY 会增加少量握手开销。

4. NIC/IRQ/RPS 微调(按需)

查看队列与中断分布:

ethtool -l eth0
cat /proc/interrupts | grep eth0

原则:

  • 10GbE 建议多队列收包并将IRQ 分散到不同 CPU(关闭 irqbalance 的强行迁移或给它配置规则)。
  • 小包风暴时,适度开启 RPS(/sys/class/net/eth0/queues/rx-*/rps_cpus)把软中断并行化。但别一上来全开,先按 1/2 核数试水。
  • 不要盲目关 GRO/GSO/TSO:这类加速特性对吞吐很有帮助。只有在极端小包攻击导致 CPU/延迟异常时,短期关闭观察。

5. 观测与核验

快速观察指标:

# TCP 状态与半连接
ss -ant | awk '{print $2}' | sort | uniq -c | sort -nr | head

# SYN/ACK 统计(netstat 在 CentOS 7 还在,可用)
netstat -s | egrep -i 'listen|syn|cookies'

# conntrack 使用率
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max

# 网卡丢包/队列
ethtool -S eth0 | egrep 'rx_dropped|tx_dropped|rx_no_buffer'

# iptables 命中(看 SYNPROXY/limit)
iptables -L -v -n

实测数据(当夜复盘摘录)

时间片(香港时区) 攻击 PPS SYN_RECV 峰值 nf_conntrack_count 平均握手时延 CPU SoftIRQ 占比
02:17(处置前) 3.5M 120k 980k / 1M 超过 800ms 65%
02:22(仅 sysctl) 3.2M 60k 720k / 1M ~ 300ms 48%
02:28(加 SYNPROXY) 3.1M 8k 210k / 1M ~ 90ms 31%
02:36(RPS 微调) 3.0M 5k 160k / 1M ~ 70ms 26%
03:05(攻击尾段) < 1M 1k 以下 < 80k / 1M ~ 40ms 18%

上述为当晚的真实量级(取整),不同业务/链路会有差异。重要的是趋势:SYNPROXY 生效后,SYN_RECV 急降、conntrack 压力明显缓解。

细节说明与参数表(便于对照)

内核关键参数(建议起步值)

参数    建议值    说明
net.ipv4.tcp_syncookies    1    打开 SYN Cookie 兜底,防 backlog 打爆
net.ipv4.tcp_synack_retries    3    握手重试次数降低,缩短半连接滞留
net.ipv4.tcp_max_syn_backlog    131072    半连接队列上限;内存允许可再调
net.core.somaxconn    16384    应用 listen backlog 上限天花板
net.core.netdev_max_backlog    65536    内核网卡层入队长度
net.netfilter.nf_conntrack_max    1048576    conntrack 表大小(按内存与流量定)
net.netfilter.nf_conntrack_buckets    262144    哈希桶(一般为 max 的 1/4)
net.netfilter.nf_conntrack_tcp_timeout_syn_recv    30    SYN_RECV 态超时,降低半连接占用
net.ipv4.tcp_max_orphans    262144    防孤儿连接拖垮系统
net.ipv4.ip_local_port_range    10000 65535    本地端口范围充足
  • iptables/SYNPROXY 要点清单
  • raw/PREROUTING:notrack(核心)
  • INPUT:SYNPROXY(对 NEW 的 SYN)
  • ESTABLISHED/RELATED:放行
  • INVALID:直接丢
  • MSS/WSCALE/Timestamp 参数与链路/应用一致
  • (可选)hashlimit 做温和的 per-src 速率限制,先放宽再收紧

结合业务侧的小优化

Nginx:

  • worker_processes 和 CPU 匹配;
  • worker_rlimit_nofile、ulimit -n 提高;
  • 监听 backlog:listen ... backlog=16384 reuseport;(reuseport 在多 worker 下摊流效果更佳);
  • 观察 499/502 的走向,确保应用线程池/上游也稳。

健康检查:

  • 把 LB 的健康探测与业务路径解耦(探测静态页或轻路径),降低抖动误判;
  • 攻击期适度放宽失败阈值与超时,避免“雪上加霜”。

上游清洗/黑洞:

  • 若攻击超过单机极限,务必与上游配合(RTBH/黑洞/清洗中心);
  • 我这次未触发清洗,是因为就地兜住了,但不要把内核调优当作万能盾。

常见坑与规避

坑 A:开启了 SYNPROXY,却没 notrack → conntrack 先爆。

坑 B:把 tcp_tw_recycle 当优化开了(旧贴遗毒)→ 新内核已移除且会造成 NAT/时序问题,千万别开。

坑 C:--mss 设错 → 特定路径上握手异常或吞吐下降。

坑 D:一刀切关 GRO/GSO/TSO → 正常流量吞吐骤降;应在观察到小包瓶颈且 CPU 飙高时短期使用,并尽快回滚。

坑 E:conntrack 调太大 → 表大不是万灵药,还要看 CPU/内存与哈希冲突率,记得同步调 buckets。

坑 F:只改 somaxconn 不改应用 backlog → 应用的 listen() backlog 达不到内核上限,白调。

回滚与应急脚本(以防万一)

回滚 sysctl:

将 /etc/sysctl.d/99-ddos-syn.conf 适度缩回或注释关键项后执行:

sysctl --system

回滚 iptables:

当晚我保留了攻防两套规则文件:

  • /etc/sysconfig/iptables.ddos(攻防)
  • /etc/sysconfig/iptables.clean(常态)

切换:

cp /etc/sysconfig/iptables.clean /etc/sysconfig/iptables
systemctl restart iptables

最后一公里:验证“真用户”体验

攻防阶段除了看内核/队列,我还会:

  • 选几条 真实业务路径 做 curl -w 延迟采样;
  • 用一台 跨境探测节点(非同机房)观测首包/总时延;
  • 观察 Nginx Access 日志 的 2xx 比例与 P95/P99;
  • 挑业务高峰的缓存命中率(避免被 DDoS 干扰反推出业务侧又被“误杀”了)。

凌晨 4:10 的风,和一张不算漂亮的曲线

4:10,风从冷通道吹过来有点刺骨,监控曲线从陡峭的山脊慢慢塌成圆润的坡。那晚我们没启用上游清洗,只靠内核参数把半连接摁住、用 SYNPROXY 在门口替业务“接客”,再加上一点 conntrack 与队列 的“筋骨调理”,把 3M+ pps 的 SYN Flood 挡了下来。
我喜欢把这些动作做成“可复制的手册”,因为线上永远会再来一次。希望这份记录能帮你在下一个 2:17,少走几步弯路。

附:一键化示例(脚本骨架,按你的端口/链路改)

#!/bin/bash
set -e

# 1) sysctl
cat >/etc/sysctl.d/99-ddos-syn.conf <<'EOF'
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 3
net.ipv4.tcp_max_syn_backlog = 131072
net.core.somaxconn = 16384
net.core.netdev_max_backlog = 65536
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_buckets = 262144
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 30
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_fin_timeout = 30
net.ipv4.ip_local_port_range = 10000 65535
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 1
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
EOF
sysctl --system

# 2) iptables-services(若你用 firewalld,请改用 direct)
yum install -y iptables-services
systemctl enable iptables
systemctl stop firewalld || true
systemctl start iptables

modprobe nf_conntrack
modprobe nf_synproxy_core

# 3) 规则(以 80/443 为例;请根据真实端口改)
iptables -t raw -C PREROUTING -p tcp --syn -j CT --notrack 2>/dev/null || \
iptables -t raw -I PREROUTING -p tcp --syn -j CT --notrack

for p in 80 443; do
  iptables -C INPUT -m conntrack --ctstate INVALID -j DROP 2>/dev/null || \
  iptables -I INPUT -m conntrack --ctstate INVALID -j DROP

  iptables -C INPUT -p tcp ! --syn -m conntrack --ctstate NEW -j DROP 2>/dev/null || \
  iptables -I INPUT -p tcp ! --syn -m conntrack --ctstate NEW -j DROP

  iptables -C INPUT -p tcp --dport $p -m conntrack --ctstate NEW -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460 2>/dev/null || \
  iptables -I INPUT -p tcp --dport $p -m conntrack --ctstate NEW -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
done

iptables -C INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || \
iptables -I INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

service iptables save
echo "Done. Monitor ss/netstat/ethtool/iptables -v."

最后的提醒

这套方法是“单机侧的防线”,足以应对很多中大型 SYN Flood;但面对 Tbps 级别的分布式攻击,必须依赖上游清洗/调度。

每条参数都不是“神奇数字”,请结合你自己的链路(MTU/隧道)、业务(握手敏感度)、硬件(CPU/队列)、流量画像,在“救火成功”后继续做压测与精调。

做好可回滚与变更记录,这样下一次 2:17,才有更稳的手感

目录结构
全文