1

I'm trying to atomically replace nftables rules. Nftables's official wiki states that -f is the recommended way to achive this. However, when I run nft -f /path/to/new/rules on Debian Buster, the new rules get added to the current rules instead of replacing them, and I end up with a system enforcing both rule-sets simultaneously.

The same thing occurs when I try to reload configuration via systemd's nftables.service.

How can I get nft to discard the current rule-set while also adding a new rule-set in a single atomic operation?

0

1 Answer 1

4

The operation done with nft -f /path/to/new/rules is atomic: that means it's either done completely or not done at all (ie: reverted), and will affect all at once the next packet hitting the rules only once committed. It won't end (because of an error) only half-done. So if you don't delete the previous ruleset before, it behaves as expected: it atomically adds rules once more, as described in the notes of Atomic rule replacement in the wiki:

Duplicate Rules: If you prepend the flush table filter line at the very beginning of the filter-table file, you achieve atomic rule-set replacement equivalent to what iptables-restore provides. The kernel handles the rule commands in the file in one single transaction, so basically the flushing and the load of the new rules happens in one single shot. If you choose not to flush your tables then you will see duplicate rules for each time you reloaded the config.

For this you must, in the same transaction delete older rules before adding again your ruleset. The easiest (but affecting all of nftables, including iptables-nft if also used) is simply, similarly to what is described above, to prepend your ruleset /path/to/new/rules with:

flush ruleset

If you're loading different tables at different times to keep logical features separated (in nftables, a table can include any type of base chain (for the given family), it's not the direct equivalent of a table in iptables which has a fixed set of possible chain) it becomes a bit more complex because flush ruleset in one rule file would delete other tables (including iptables-nft rules if used along nftables). Then this should be done at the table level with for example (but read further before doing):

delete table inet foo

followed by the redefinition of it (table inet foo { ...). As-is, this creates an other chicken-and-egg problem: the first time this file would be read, eg at boot, the delete operation would fail, and thus everything would atomically fail as a whole because the table didn't exist. As declaring an already declared table's name is considered a no-op and thus won't fail, in the end this can be done:

table inet foo
delete table inet foo

table inet foo {
[...]
  • note 1: For this to work properly in all cases kernel >= 3.18 is required, else better stick to flush ruleset.

  • note 2: the wiki's note above suggests using for this case flush table inet foo but this should probably be avoided, because if sets are present this won't delete element in sets, leading again to adding instead of replacing elements if the elements are added by the ruleset and were changed there. It won't either allow to redefine the type/hook of a base chain. Using table inet foo + delete table inet foo doesn't have these drawbacks. Of course if one needs to keep elements in sets when reloading rules, one might ponder using flush table inet foo and adapt to the limitations of this method.


In all cases you should be careful when using nft list {ruleset, table inet foo, ...} > /path/to/new/rules to dump the current rules to a rule file: it won't include any flush or delete command, and you'll have to add them back manually. You can probably use include to overcome this by keeping your "plumber" statements outside of the actual rules.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.