GUIDES / LINUX & OPENWRT

Linux 服务器与 OpenWRT 如何用 ipset 自动封堵攻击 IP 黑名单

简答:curl 定时拉取 TrapCloud 的 TXT 黑名单,整批导入 ipset 集合后原子 swap 切换, iptables 只需一条引用该集合的 DROP 规则;nftables 系统用 named set 同理。 全程无第三方依赖,几十万 IP 的匹配开销与几十个几乎相同。

直接把几万条 IP 写成防火墙规则是错误做法——规则是线性匹配,会拖垮转发性能。 正确姿势是内核集合(ipset 或 nftables set)加单条引用规则。 下面两个脚本可直接复制使用,内置行数安全闸(拉取失败绝不清空现有防线)与原子切换(更新瞬间无保护空窗)。

方案一:ipset + iptables(通用)

适合 CentOS / Debian / Ubuntu 及多数仍用 iptables 的系统,保存为 /usr/local/bin/tc_sync.sh 并加执行权限:

#!/bin/sh
# TrapCloud 攻击 IP 黑名单 -> ipset 自动同步(iptables 方案)
URL="https://www.trapcloud.cn/api/export?key=你的APIKey&type=all&level=all&service=all&hours=72&format=txt"
SET="trapcloud"
TMP="/tmp/tc_block.txt"

curl -fsS -m 60 "$URL" -o "$TMP" || exit 1

# 安全闸:必须是合法 IP 且条数达标,否则保留现有名单
CNT=$(grep -Ec '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' "$TMP")
[ "$CNT" -lt 100 ] && exit 1

ipset create "$SET" hash:ip maxelem 300000 -exist
ipset create "$SET"_tmp hash:ip maxelem 300000 -exist
ipset flush "$SET"_tmp
sed -n -E 's/^([0-9]{1,3}(\.[0-9]{1,3}){3})$/add '"$SET"'_tmp \1/p' "$TMP" \
  | ipset restore -!
ipset swap "$SET"_tmp "$SET"      # 原子切换,无保护空窗
ipset destroy "$SET"_tmp

# 首次运行时挂上 DROP 规则(已存在则跳过)
iptables -C INPUT -m set --match-set "$SET" src -j DROP 2>/dev/null \
  || iptables -I INPUT -m set --match-set "$SET" src -j DROP

方案二:nftables(新系统 / OpenWRT fw4)

Debian 12+、RHEL 9+ 与 fw4 版 OpenWRT 默认 nftables:

#!/bin/sh
# nftables 方案(Debian 12+ / RHEL 9+ / OpenWRT fw4)
URL="https://www.trapcloud.cn/api/export?key=你的APIKey&type=all&level=all&service=all&hours=72&format=txt"
TMP="/tmp/tc_block.txt"

curl -fsS -m 60 "$URL" -o "$TMP" || exit 1
CNT=$(grep -Ec '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' "$TMP")
[ "$CNT" -lt 100 ] && exit 1

# 首次运行:建表、建集合、挂一条 drop 规则
nft list table inet trapcloud >/dev/null 2>&1 || {
  nft add table inet trapcloud
  nft "add set inet trapcloud blocklist { type ipv4_addr; size 300000; }"
  nft "add chain inet trapcloud input { type filter hook input priority -10; }"
  nft add rule inet trapcloud input ip saddr @blocklist drop
}

# 整批替换集合内容
{
  echo "flush set inet trapcloud blocklist"
  sed -n -E 's/^([0-9]{1,3}(\.[0-9]{1,3}){3})$/add element inet trapcloud blocklist { \1 }/p' "$TMP"
} > /tmp/tc_block.nft
nft -f /tmp/tc_block.nft

定时更新

# 每小时同步一次(订阅配额每天 100 次,24 次/天在配额内)
17 * * * * /usr/local/bin/tc_sync.sh >/dev/null 2>&1

OpenWRT 用户也可以不写脚本:banIP 插件支持自定义源(Custom Feed), 把导出 URL 配置为自定义黑名单源即可。等级与时间窗的选择同其它设备: hours=72 全量约十几万条,level=high 约数万条,level=critical 约数千条, 小内存路由器建议从 critical 起步。

常见问题

Linux 服务器怎么批量封堵几万个攻击 IP?

用 ipset 而不是逐条 iptables 规则。ipset 是内核哈希集合,几十万个 IP 的匹配开销与几十个几乎相同;iptables 只需一条引用该集合的 DROP 规则。名单更新用「新建临时集合、整批导入、原子 swap」的方式,更新瞬间也不会出现保护空窗。

十几万条 IP 会不会拖慢服务器或路由器?

不会。ipset 的 hash:ip 集合按哈希查找,单包匹配是常数时间,与条目数无关;内存占用在几十 MB 量级。主流家用路由器跑 OpenWRT 也能承载,条目上限可通过 maxelem 参数设定。

重启后 ipset 名单会丢吗?

ipset 集合存在于内核内存,重启即清空。解决办法:把同步脚本挂进 cron(如每小时一次),开机后第一次执行就会重建集合;要求开机即时生效的,可在网络就绪后的启动脚本里先跑一次同步脚本。

OpenWRT 上怎么接入?

两种方式:一是 banIP 插件支持自定义源(Custom Feed),把 TrapCloud 导出 URL 配置为自定义黑名单源即可;二是直接用本文的 ipset / nftables 脚本加 crontab,fw4(nftables)版本的 OpenWRT 用 nft 方案。

免费档怎么用这套脚本?

脚本的自动拉取需要订阅 API Key。免费档可在邮箱注册后每天手动下载 1 次近 24 小时黑名单(TXT 纯文本),保存为 ip.txt 后用脚本里的导入部分(ipset restore + swap)手动灌入,同样可用。

为什么脚本里要做行数检查?

这是安全闸:如果网络故障或鉴权失败导致拉到空文件或错误页,直接灌入会把现有防护名单清空。脚本先校验内容是合法 IP 且条数达标,不达标就保留现有名单不动,宁可这一轮不更新也不清空防线。

还没有 API Key?免费档邮箱注册即可每天下载 1 次近 24 小时黑名单,手动走脚本的导入部分; 自动直连与多维过滤见会员与订阅, 接入疑问可到留言反馈