必须用 map 而不是 set,因为 nft set 不支持 per-entry timeout,设 timeout 会报语法错误;map 可存 ip 为 key、时间戳为 value,实现动态过期判断。

用 nftables 的 map 实现动态黑名单,核心是把 IP 地址作为 key、时间戳作为 value 存储,再由用户态脚本判断是否过期。nft 本身不执行超时计算,只负责高速查表。
为什么必须用 map 而不是 set
nft set 不支持 per-entry timeout,写 timeout 30s 会直接报错:syntax error, unexpected timeout。set 只适合长期封禁(如固定恶意 IP 段)。而 map 允许你存任意值,时间戳就是最自然的“过期凭证”。
创建和填充带时间戳的黑名单 map
先定义 map,注意 value 类型用 integer(time 是伪类型,底层仍是秒级整数):
nft add map inet filter ip_blacklist { type ipv4_addr : integer \; }
插入一个带当前时间戳的条目(192.168.1.20 封禁 10 分钟):
echo $(($(date +%s) + 600)) | xargs -I{} nft add element inet filter ip_blacklist { 192.168.1.20 : {} }
这里存的是**过期时间点**(绝对时间),后续判断更直观:当前时间 > 存的值 → 已过期。
在规则中调用并做逻辑判断
nft 规则不能做算术运算,所以匹配要分两步:
- 第一步:用
lookup查 IP 是否在 map 中,并把时间戳加载到寄存器(例如@ip_blacklist) - 第二步:用 shell 脚本读取该值,与
date +%s比较;若未过期,则执行drop
典型工作流示例(用于 SSH 暴力破解防护):
• 收到失败登录 → 触发脚本 → 查 IP 是否已在 map 中 → 若无或已过期 → 插入新过期时间
• 流量进入 input 链 → 规则尝试 lookup → 若命中 → 启动检查脚本 → 过期则跳过,未过期则 drop
维护与清理建议
map 本身不会自动清理过期项,需定期扫描:
- 用
nft list map inet filter ip_blacklist导出全部条目 - 配合
awk和date筛出过期项(如$2 ) - 对过期项执行
nft delete element inet filter ip_blacklist { $ip } - 建议每分钟 cron 执行一次,避免 map 膨胀
不复杂但容易忽略。











