不能用匿名映射表管理上万条黑名单ip,因其根本不存在;nftables中仅有匿名集合(仅支持匹配)和命名map(支持键值映射),上万条ip须用命名map或interval set配合连接状态过滤实现高效、安全封禁。

不能用匿名映射表管理上万条黑名单 IP —— 它根本不存在。
匿名集合 ≠ 匿名映射表
所谓“匿名映射表”是概念混淆。nftables 中只有两类内联结构:
-
匿名集合:写在规则里的一组值,如
{ 80, 443, 8080 }或{ 192.0.2.10, 203.0.113.0/24 },仅支持匹配(==或!=),不支持动作映射; -
命名 Map:预先创建的键值对结构,如
type ipv4_addr : verdict,才能把 IP 映射到drop、reject等动作。
匿名结构天生不可复用、不可动态增删,上万条 IP 写进一条规则会触发命令行截断、解析失败,内核也拒绝加载超长表达式。
真正适合上万条服务端口黑名单的方案是命名 Map + interval Set 混合使用
若目标是“封禁特定 IP 访问特定端口”(例如:禁止 192.0.2.5 访问 22 端口,但允许访问 80),需用 Map;若只是“全局封禁 IP”,用带 interval 的 Set 更轻量。
- 封禁 IP+端口组合(精准控制):
nft add map inet filter portblk_map { type ipv4_addr . inet_service : verdict; timeout 1h }
添加条目:nft add element inet filter portblk_map { 192.0.2.5 . tcp dport 22 : drop } - 只封 IP、不限端口(通用黑名单):
nft add set inet filter ip_blacklist { type ipv4_addr; flags interval; timeout 1h }
添加条目:nft add element inet filter ip_blacklist { 192.0.2.5 }
规则链中必须前置引用,且搭配连接状态过滤
无论用 Map 还是 Set,拦截规则必须放在链首,并跳过已建立连接,否则会中断正常业务:
- 先放行已有连接:
nft add rule inet filter input ct state established,related accept - 再拦截黑名单:
nft add rule inet filter input ip saddr @ip_blacklist drop(Set)
或nft add rule inet filter input meta nftrace set 1 ip saddr . tcp dport @portblk_map(Map)
批量导入要脚本化,严禁 flush
上万条数据必须通过循环逐条添加,避免清空窗口:
- 安全导入(支持 CIDR 和单 IP):
while IFS= read -r ip; do [[ -n "$ip" ]] && nft add element inet filter ip_blacklist { "$ip" }; done - 查数量验证:
nft list set inet filter ip_blacklist | grep elements | wc -l - 删单条而非 flush:
nft delete element inet filter ip_blacklist { 192.0.2.5 }











