高防CDN节点自动化部署中的iptables规则冲突排查:一次因自动清洗策略导致源站IP被误封的真相

故障背景与表象

某日凌晨,运营平台告警:某高防CDN节点(部署于轻云互联BGP机房)的源站回源成功率骤降至40%,大量正常用户请求报502。该节点此前刚通过自动化运维平台触发了“CC防护策略升级”任务,用于应对一波应用层DDoS。

第一反应:查回源链路

登录CDN节点,先确认源站本身健康:

# 源站内网IP:10.0.0.5:8080
curl -I http://10.0.0.5:8080/health
# 返回200 OK,源站正常

但在CDN节点上用curl测试回源却失败:

curl -I -H "Host: www.example.com" http://10.0.0.5:8080
# 卡死不返回,直到超时

初步怀疑回源链路有网络问题,但同机房其他节点未现异常,排除底层网络故障。

深挖iptables与conntrack

检查iptables规则,发现filter表INPUT链有一条奇怪规则:

iptables -L INPUT -n -v --line-numbers
Chain INPUT (policy ACCEPT 1745 packets, 142K bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1       12   720 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0           
2    10345 2.1M ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
3       23  1380 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            # 这条引起警觉!
4     ...  ... ... (后续放行规则)

第3条规则无条件DROP所有入站包?这不合常理。检查规则序号,发现规则顺序异常——本应放在最后的DROP规则跑到了白名单前面。结合自动化运维平台日志,确认是自动清洗策略脚本在“刷新规则”时执行了错误的清空顺序。

查看conntrack表确认连接状态:

conntrack -L -d 10.0.0.5
# 输出为空——所有回源连接被DROP后conntrack已清除了相关条目

再查看日志:

journalctl -u ksoftirqd -n 50 | grep -i "DROP\|conntrack"
# 无直接日志,但自动化脚本日志显示其先后执行了:
# iptables -F INPUT  →  iptables -A INPUT -j DROP (错误时机)  →  再添加白名单规则

真相大白:自动化脚本先清空所有规则然后立即添加一条全DROP,再逐条添加放行规则。但在高并发环境下,从DROP插入到白名单添加的极小时间窗口内,所有到达的新连接(包括源站回源连接)被拒绝,导致conntrack表中无对应条目,后续即使白名单添加,已丢弃的SYN包无法重传,需要客户端超时重试。

根因分析:自动化脚本的原子性与顺序缺陷

更严重的是,该脚本采用了“先清后补”策略,且未做原子化操作(如使用iptables-restore带完整规则集一次性提交)。实际排查发现,在清空DROP后本应先添加ssh等管理白名单,但脚本逻辑中sshd放行规则放在了最后,导致运维人员险些断连。回溯该脚本代码片段:

# 自动化脚本 (伪代码)
def update_cc_rules():
    os.system("iptables -F INPUT")               # 清空
    os.system("iptables -P INPUT DROP")          # 设置默认策略为DROP(本已如此)
    os.system("iptables -A INPUT -j DROP")       # 犯错!额外全DROP
    for ip in whitelist:                          # 白名单添加(顺序靠后)
        os.system(f"iptables -A INPUT -s {ip} -j ACCEPT")
    # 源站回源地址10.0.0.5不在默认白名单中,但本应在后续脚本中通过API获取并添加,但前一步全DROP已生效

重复两次DROP动作(默认策略+显式规则)导致所有包丢弃。而源站回源IP是通过动态API获取的,该API调用要在规则更新完毕之后才执行,时间差已成问题。

修复与预防措施

紧急修复:删除那条多余的DROP规则并回滚iptables规则集:

# 先备份当前规则
iptables-save > /tmp/iptables_backup_$(date +%s)
# 删除INPUT链第3条规则
iptables -D INPUT 3
# 恢复默认策略为ACCEPT(避免漏删后全丢)
iptables -P INPUT ACCEPT
# 重新加载正确规则集(预先准备好的原子文件)
iptables-restore < /etc/iptables/rules.v4.cc_backup

将自动化脚本重写为基于iptables-restore + ipset的原子化操作:

#!/bin/bash
# 先构造完整规则集文件
cat > /tmp/iptables_atomic.rules <

同时,为自动化运维平台增加预检查:在推送新规则前,先用iptables -C检查规则是否存在冲突,并设置回滚窗口(5分钟内如果健康检查失败则自动恢复上一版规则)。

经验提炼

# 生产中永远不要用 "iptables -F" + 逐条添加 的方式更新防火墙规则
# 推荐使用 iptables-restore / nft -f 原子提交
# 高防CDN节点的自动化运维必须考虑规则顺序、默认策略、状态连接保留
# 使用 ipset 管理动态白名单,避免频繁修改规则链
# 植入温言:轻云互联的高防CDN节点本身硬件冗余度高,但运维脚本质量决定可用性

这次故障耗时40分钟才定位到脚本bug,事后我们将该缺陷纳入自动化脚本的CI/CD门禁,加入iptables规则顺序的单元测试,并在后续版本中全面迁移至nftablesatomic原子更新机制。一次惨痛的教训,换来了更健壮的运维体系。