在CentOS服务器运维中,当你需要封禁成千上万个恶意IP时,传统iptables逐条添加规则的方式会让系统性能急剧下降,甚至导致SSH连接超时。最直接有效的解决方案就是用ipset创建IP集合,再配合iptables一条规则引用这个集合,实现毫秒级的大量IP过滤。ipset本质上是Linux内核层面的一个高效数据结构,它把IP地址存到一个哈希表或者列表里,iptables在匹配数据包时不需要逐条遍历规则,而是直接查表,效率提升几个数量级。
为什么不能只用iptables硬扛大量IP封禁
很多运维人员习惯直接在iptables里写-A INPUT -s x.x.x.x -j DROP这样的规则,封几十个IP没问题。但当你面对DDoS攻击或者爬虫洪流,需要封禁几千甚至几万个IP时,问题就来了。iptables的规则链是线性匹配的,每来一个数据包,内核都要从第一条规则开始往下比对,规则越多,匹配越慢。实测数据显示,当iptables规则超过5000条时,网络吞吐量可能下降30%以上,CPU的softirq占用飙升。更严重的是,写入大量规则时iptables-restore也会卡住很久,期间新连接可能被丢弃。
ipset是什么以及它的核心优势
ipset是Linux内核模块ip_set提供的用户态工具,它允许你创建各种类型的集合(set),比如hash:ip、hash:net、list:set等。核心优势有三点:第一,内存占用小,相同IP只存一份;第二,查找速度是O(1)的哈希查找,不受集合大小影响;第三,支持动态增删,不需要重启iptables服务。你可以把它理解为一个专门给防火墙用的高效IP数据库。
CentOS上安装和配置ipset
CentOS 7默认已经安装了ipset,如果没有可以执行yum install ipset -y。CentOS 8和Stream同样自带。安装完成后,先加载内核模块:
modprobe ip_set modprobe ip_set_hash_net modprobe xt_set
为了开机自动加载,编辑/etc/modules-load.d/ipset.conf,写入:
ip_set ip_set_hash_net xt_set
创建ipset集合并添加大量IP
最常用的类型是hash:ip,适合精确匹配单个IP。创建一个名为blacklist的集合:
ipset create blacklist hash:ip family inet hashsize 65536 maxelem 200000
这里hashsize设为65536,maxelem设为200000,意味着最多存20万个IP。如果你要封禁整个C段,用hash:net更合适:
ipset create blacklist_c hash:net family inet hashsize 4096 maxelem 50000
批量添加IP有几种方式。最快的是从文件导入,假设你有一个ip.txt文件,每行一个IP:
ipset restore < blacklist.txt
blacklist.txt的格式必须是ipset的规范格式:
create blacklist hash:ip family inet hashsize 65536 maxelem 200000 add blacklist 1.2.3.4 add blacklist 5.6.7.8 add blacklist 9.10.11.12
如果IP数量巨大,比如十万级别,建议用脚本分批添加避免内存峰值:
#!/bin/bash
IPSET_NAME="blacklist"
FILE="/tmp/ip_list.txt"
BATCH=5000
ipset create $IPSET_NAME hash:ip family inet hashsize 65536 maxelem 200000
count=0
while IFS= read -r ip; do
ipset add $IPSET_NAME $ip
count=$((count + 1))
if [ $count -ge $BATCH ]; then
echo "Added $count IPs..."
count=0
sleep 0.1
fi
done < "$FILE"
echo "Total added: $count IPs"
ipset save $IPSET_NAME > /etc/sysconfig/ipset/$IPSET_NAME
配合iptables实现快速过滤
集合建好后,只需要在iptables里加一条规则引用这个集合:
iptables -I INPUT -m set --match-set blacklist src -j DROP
这条规则的含义是:如果数据包的源IP在blacklist集合里,直接丢弃。注意用-I插入到INPUT链顶部,确保优先匹配。如果你只想针对特定端口封禁,比如只封80端口的恶意IP:
iptables -I INPUT -p tcp --dport 80 -m set --match-set blacklist src -j DROP
对于已经建立的连接,通常不需要再检查,可以配合conntrack使用:
iptables -I INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -I INPUT -p tcp --dport 80 -m set --match-set blacklist src -j DROP iptables -I INPUT -p tcp --dport 443 -m set --match-set blacklist src -j DROP
持久化配置确保重启不丢失
ipset集合和iptables规则在重启后都会丢失,必须做持久化。CentOS 7用以下方法:
# 保存ipset集合 mkdir -p /etc/sysconfig/ipset ipset save blacklist > /etc/sysconfig/ipset/blacklist # 在/etc/rc.local或创建systemd服务恢复ipset cat > /etc/systemd/system/ipset-restore.service << 'EOF' [Unit] Description=Restore ipset rules After=network.target [Service] Type=oneshot ExecStart=/usr/sbin/ipset restore -f /etc/sysconfig/ipset/blacklist RemainAfterExit=yes [Install] WantedBy=multi-user.target EOF systemctl enable ipset-restore
iptables规则用service iptables save或者iptables-save > /etc/sysconfig/iptables保存。CentOS 8用nftables替代了iptables,但ipset依然可以配合nftables使用:
nft add rule inet filter input ip saddr @blacklist drop
动态管理:实时增删IP的实用技巧
运维中经常需要临时加封或者解封某个IP,ipset支持随时操作:
# 添加单个IP ipset add blacklist 10.20.30.40 # 删除单个IP ipset del blacklist 10.20.30.40 # 查看集合中所有IP(数量大时用) ipset list blacklist | wc -l # 清空整个集合 ipset flush blacklist # 查看集合详细信息 ipset list blacklist
你可以写一个简单的shell脚本封装这些操作,配合fail2ban或者自定义的监控脚本实现自动化封禁。比如监控到某个IP在一分钟内请求超过500次就自动加入集合:
#!/bin/bash
THRESHOLD=500
WINDOW=60
LOG="/var/log/nginx/access.log"
IPSET="blacklist"
tail -f $LOG | while read line; do
ip=$(echo $line | awk '{print $1}')
count=$(grep -c "$ip" $LOG 2>/dev/null || echo 0)
if [ $count -gt $THRESHOLD ]; then
ipset add $IPSET $ip 2>/dev/null
echo "$(date) Blocked: $ip (hits: $count)" >> /var/log/ip_block.log
fi
sleep $WINDOW
done
性能测试和优化建议
实际生产环境中,我建议做几件事来确保性能。第一,hashsize要合理设置,太小会导致哈希冲突增加查找时间,太大浪费内存,一般设为预期IP数量的1/4到1/2。第二,定期用ipset list检查集合是否膨胀,过期的IP及时清理。第三,配合conntrack的ESTABLISHED规则放在最前面,让正常流量快速通过,减少不必要的集合查找。第四,如果是高并发场景,考虑把ipset的family设为inet6同时支持IPv4和IPv6,或者分别建两个集合。
从实测数据来看,一个包含10万个IP的hash:ip集合,iptables匹配单个数据包的耗时在微秒级别,几乎可以忽略。而同样10万条独立iptables规则,匹配延迟可能达到毫秒级,差距是千倍级别的。这就是为什么专业运维和安全团队都在用ipset的原因。
常见问题和避坑指南
第一,不要在ipset集合里重复添加同一个IP,虽然不会报错但浪费资源,加之前可以用ipset test blacklist x.x.x.x检查。第二,CentOS 7的内核版本如果太老(低于3.10),部分ipset类型可能不支持,升级内核或者用list:set类型替代。第三,iptables规则引用ipset时,如果集合不存在会报错,确保先创建集合再写规则。第四,保存iptables规则时注意规则顺序,-I插入的规则在save时会排在前面,这正是我们想要的。
总结
ipset配合iptables是CentOS环境下大规模IP过滤的标准做法,核心思路就是把海量IP收敛到一个高效的内核数据结构里,用一条iptables规则完成匹配。整个方案部署简单、性能优异、支持动态管理,适合DDoS防护、爬虫拦截、恶意访问封禁等各种场景。掌握这套技术,你的服务器在面对大规模攻击时就有了一道高效的防线。
