nftables 本身不支持 geoip,需将地理黑名单预处理为 cidr 列表后导入具名集合;推荐使用 maxmind 或 ip2location 数据源,创建带 interval 标志的 ipv4_addr 集合,并用循环批量加载网段。

直接用 nftables 本身无法识别地理位置——它不解析 IP 归属地,也不内置 GeoIP 数据库。但你可以把地理黑名单“落地”为标准的 IP 或网段集合,再由 nftables 高效执行封禁。关键不是让 nftables 做判断,而是把判断结果(比如“中国以外的 IP”或“俄罗斯所有 ASN”)提前转成 CIDR 列表,喂给它的具名集合。
先准备地理 IP 数据源
你需要一份按国家/地区划分的 IPv4/IPv6 网段列表,常用可靠来源包括:
- IP2Location LITE:免费版提供国家代码映射,CSV 格式易解析
- MaxMind GeoLite2 Country:需注册获取,含精确 CIDR 和 ISO 3166-1 代码(如 CN、RU、US)
- RIPE NCC / APNIC 公开分配数据:适合按 RIR 区域(如亚太、欧洲)粗粒度筛选
例如,提取所有属于“CN”(中国)的 IPv4 网段,保存为 cn-blocks.txt,每行一个 CIDR:
1.0.1.0/24
1.0.2.0/23
…
创建支持网段与自动过期的具名集合
地理封禁通常是长期策略,但保留 timeout 可用于临时调整(比如只封某国新出现的攻击网段 24 小时):
nft add set inet firewall geo_blacklist { \
type ipv4_addr; \
flags interval; \
timeout 24h; \
size 524288 \
}
- flags interval 必须启用——否则每个 /24 会被拆成 256 条独立条目,内存和性能崩盘
- size 524288 对应约 50 万网段,按你导入的地理数据量预估(MaxMind CN 约 12 万条,留余量)
- 去掉 timeout 即永久生效;设为 1y 效果等同
批量导入地理网段并验证
一行命令完成加载(假设文件无空行、格式合规):
while read cidr; do \
[ -n "$cidr" ] && nft add element inet firewall geo_blacklist { "$cidr" }; \
done
<p>验证是否成功:</p>
- 查条目数:
nft list set inet firewall geo_blacklist | grep -c "element" - 查某 IP 是否命中:
nft list set inet firewall geo_blacklist | grep "1.0.1.5" - 确认规则已引用:
nft list chain inet firewall input | grep "@geo_blacklist"
在 INPUT 链中部署单条拦截规则
这条规则必须前置(建议 priority 0),确保早于 ACCEPT 规则匹配:
nft add rule inet firewall input ip saddr @geo_blacklist drop
- 内核包进入时直接哈希查表,命中即丢,平均耗时 几十纳秒
- 不遍历规则链,不 reload,不中断连接
- 后续增删地理网段,只需操作集合元素,无需动规则
配合日志与自动化更新
加 counter 和 log 前缀便于审计:
nft replace rule inet firewall input handle X ip saddr @geo_blacklist counter log prefix "GEO-DROP-CN: " drop
-
nft list counters查命中次数 -
dmesg -t | grep "GEO-DROP-CN"看实时拦截记录 - 每月用 cron 自动拉取最新 GeoLite2 数据,生成新列表并增量更新集合(
nft delete element+nft add element),避免 flush 导致放行窗口











