我已经从 iptables 迁移到 nftables,但遇到了一个恼人的问题。在旧系统中,我有一个脚本每天删除/添加一些规则。我能够根据 iptables 规则的位置轻松添加/删除它们,这是恒定的。
现在我们有了烦人的手柄。当我从脚本添加规则时,它会通过动态句柄编号进行分配。我可以首先通过检索该句柄号来删除规则。问题是当相同的规则被删除并重新添加时。之后,它的句柄编号会增加,因此不相同。因此,我无法使用与旧系统中相同的简单方法。
任何人都可以给我一个解决方案来解决这个问题 - 链中至少有 40 条规则,我想删除相同的规则,每天重新添加几条规则。使用 iptables 是如此简单,但现在我找不到简单的解决方案
谢谢。
答案1
OP使用这种规则,这就是我将用作示例的布局,以作为答案的基础:
add rule ip nat postrouting oifname $inet_if ip saddr 172.xx.xx.xx counter snat to $inet_if_ip add rule ip nat postrouting oifname $inet_if ip saddr 172.xx.xx.xx counter snat to $inet_if_ip
...等等...
使用一套
nftables特征套,类似于IP集iptables 的伴侣,但更通用。
因此可以因式分解纳特将规则合并为一个规则,然后仅操作关联的集合,而不再触及该规则。对集合元素的操作(包括删除)不需要任何句柄,因为与规则相反,它的内容顺序是无关的,因此可以使用哈希列表在内核内存中有效地搜索和重新排序。
如果独立计数纳特流(即:保持 的角色counter
很重要,这需要最新的工具(至少 >= 0.9.1,但建议 nftables >= 0.9.4,请参阅我的答案对此,还请参阅本答案末尾的“错误”)。nftables仍在不断发展,因此某些功能取决于版本nftables或内核。
此答案中使用了nftables 0.9.6 0.9.8。这可能会影响某些语法和功能(例如 JSON)。
因此,而不是这种规则集:
define inet_if = eth0
define inet_if_ip = 192.0.2.2
table ip nat # for idempotency
delete table ip nat # for idempotency
table ip nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname $inet_if ip saddr 172.17.1.2 counter snat to $inet_if_ip
oifname $inet_if ip saddr 172.17.1.3 counter snat to $inet_if_ip
}
}
可以使用一组可以稍后填充的集合(或者也可以直接填充):
define inet_if = eth0
define inet_if_ip = 192.0.2.2
table ip nat
delete table ip nat
table ip nat {
set curfewlist {
type ipv4_addr
counter # optional
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname $inet_if ip saddr @curfewlist snat to $inet_if_ip
}
}
然后可以在 06:00 在 crontab 中填充该集合(遵循前面的示例):
nft 'add element ip nat curfewlist { 172.17.1.2, 172.17.1.3 }'
到了23:00,就可以逐个删除了:
nft 'delete element ip nat curfewlist { 172.17.1.2 }'
nft 'delete element ip nat curfewlist { 172.17.1.3 }'
或一次多个元素:
nft 'delete element ip nat curfewlist { 172.17.1.2, 172.17.1.3 }'
或者简单地冲洗:
nft flush set ip nat curfewlist
注意:a 的范围(也称为名称空间)放位于其表内:集合不能从其他表引用,但与iptables桌子不限于一种挂钩类型。一个可以和可能应该同一个表中有多种类型的链,允许重复使用相同的集合,例如在筛选规则并在纳特规则,而不是模仿iptables' 每个表限制一种钩子类型。
使用meta hour
比赛
对于内核 >= 5.5,甚至不需要再更改任何内容,只需使用meta hour
可以在数据包路径中进行检查。单人纳特规则可以替换为:
meta hour 06:00-23:00 oifname $inet_if ip saddr @curfewlist snat to $inet_if_ip
并且这个集合永远填满了。每年只有两次,在夏令时更改期间,应该重新加载规则集,因为内核始终使用 UTC 时间。完整的规则集是:
define inet_if = eth0
define inet_if_ip = 192.0.2.2
table ip nat
delete table ip nat
table ip nat {
set curfewlist {
type ipv4_addr
counter
elements = { 172.17.1.2, 172.17.1.3 }
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
meta hour 06:00-23:00 oifname $inet_if ip saddr @curfewlist snat to $inet_if_ip
}
}
注意:存储在内核内存中的小时间隔采用 UTC 时区,并且始终以 24 小时为模的单个连续间隔。根据当地时区,小时表达式可能会以不同的方式读回,例如!= "23:00"-"06:00"
,其含义与 相同"06:00"-"23:00"
。
虽然我认为上面简化了结果,但下面我将介绍如何解决或仍然尝试以其他方式使用句柄。
避免需要操纵手柄
如果上述方法不足以满足用例,您仍然可以移动用户链中的所有逻辑规则,并在链级别而不是规则级别执行操作。这在这里给出:
define inet_if = eth0
table ip nat
delete table ip nat
table ip nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname $inet_if jump curfew
}
chain curfew { }
}
在 06:00 将加载以下规则集部分(例如:)nft -f curfew.nft
:
define inet_if_ip = 192.0.2.2
flush chain ip nat curfew #for idempotency
table ip nat {
chain curfew {
ip saddr 172.17.1.2 counter snat to $inet_if_ip
ip saddr 172.17.1.3 counter snat to $inet_if_ip
}
}
在 23:00 只需运行以下命令:
nft flush chain ip nat curfew
还有其他方法仍然可以使用手柄吗?
这需要以一种或另一种方式使用脚本,而不仅仅是NFFT。
稍后尝试识别规则需要转储全部链的规则(即:将它们从内核转移到用户态)。这是什么nftables” 开发人员从一开始就试图避免。
如果您计划添加稍后可能在给定用例中删除的规则,请在添加它们时存储它们的句柄,这样以后就不需要查找句柄了。
使用--echo
该--handle
选项将自动显示刚刚添加的内容,包括句柄(单独运行nft --handle monitor
也可以)。使用先前的curfew.nft
文件将给出(句柄在每次调用时发生变化):
# nft --echo --handle -f curfew.nft
add chain ip nat curfew # handle 2
add rule ip nat curfew ip saddr 172.17.1.2 counter packets 0 bytes 0 snat to 192.0.2.2 # handle 4
add rule ip nat curfew ip saddr 172.17.1.3 counter packets 0 bytes 0 snat to 192.0.2.2 # handle 5
因此,使用合理的过滤器再次运行(通过了解此特定规则集部分中添加的内容可以更轻松):
# nft --echo --handle -f curfew.nft | sed -n 's/^add rule.*# handle \(.*\)$/\1/p' | tee curfew-handles.txt
6
7
人们可以手动附加一条附加规则并存储其句柄:
# nft --echo --handle add rule ip nat curfew ip saddr 172.17.1.4 snat to 192.0.2.2 | sed -n 's/^add rule.*# handle \(.*\)$/\1/p' | tee -a curfew-handles.txt
8
然后删除列表中句柄的所有规则:
# for i in $(cat curfew-handles.txt); do printf 'delete rule ip nat curfew handle %d\n' $i; done | nft -f - && : > curfew-handles.txt
更新:nftables >= 0.9.8 更好的 JSON 支持
nftables支持JSON 输出,但需要版本 0.9.8:nft --echo --handle --json add rule ...
在版本 0.9.6 上不回显任何内容,但在 0.9.8 上正常工作(并在此处使用内核 5.10.x 进行测试)。
检索刚刚添加的规则句柄的示例,使用jq
。附加输出注释行本身不是 JSON 输出,因此必须被过滤掉:
# nft --echo --json add rule ip nat curfew ip saddr 172.17.1.4 snat to 192.0.2.2 |
grep -v '^#' |
jq '.nftables[].add.rule.handle'
40
nft monitor
或者同时使用,同时沿着规则提供链信息,在这种情况下更有用:
nft --json monitor |
grep --line-buffered -v '^#' |
jq -j '
.add.rule |
if . != null then
(.handle, " ", .family, " ", .table, " ", .chain, "\n")
else
empty
end'
执行上面的前一个规则命令时的结果:
40 ip nat curfew
警告:
某些 nftables 版本中的错误
目前(v0.9.8),
--echo --handle
在某些完整的规则集上使用可能会导致nft
发出警告(当之前的规则集为空时)甚至崩溃(例如:似乎counter
集合中最近引入的关键字会导致此类命令或正在运行的命令崩溃nft --handle monitor
)。目前最好坚持在仅添加规则的命令上使用它。
答案2
这可以工作:
handle=$(nft list ruleset -a | grep "$your_rule" | grep "#" | awk '{print $NF}')
test -n "$handle" && nft delete rule filter output handle "$handle"
nft insert rule filter output position XX ...
答案3
我会做什么
是一个自定义表格,并且每次都更换它。如果通过一次调用即可完成表更新,那么表更新是原子的nft
。
因此,更改我的程序中的规则,重新生成表,调用nft
.
答案4
元素超时可能会提供您需要的解决方案。您可以将地址或端口放在一个集合中,到期后它们将自动从该集合中删除。
超时可以以小时、分钟、秒或组合形式指定,例如 2 小时 10 分 5 秒。
引用 nftables wiki 中的示例:
% nft add table inet myfilter
% nft add set inet myfilter myset {type ipv4_addr\; flags timeout\; }
% nft add element inet myfilter myset {10.0.0.1 timeout 10s }
结果:
% nft list ruleset
table inet myfilter {
set myset {
type ipv4_addr
flags timeout
elements = { 10.0.0.1 timeout 10s expires 8s}
}
}
来源:https://wiki.nftables.org/wiki-nftables/index.php/Element_timeouts