On Tue, 30 Jan 2024 22:13:26 +0200 (EET) Timo Lindfors <timo.lindfors@xxxxxx> wrote: > Hi, > > I spent some time trying to figure out how to programmatically answer > questions like "Does the ruleset R allow reaching service listening on port P from > interface I?" or "Does the ruleset R1 allow something that ruleset R2 does > not allow?". > > I tried to find prior work on this area and was able to find "Verified > iptables Firewall Analysis and Verification" [1] that uses > the Isabelle proof assistant for iptables rules and "Automated Analysis > and Debugging of Network Connectivity Policies" [2, 3] that uses Z3 for > some simple Azure firewall ACL rules. > > I could not find anything relevant for netfilter so I began experimenting > a bit by writing a python function that tries to simulate nftables > behavior given a ruleset and a packet. I used output of "nft --json list > ruleset" to and soon noticed that it doesn't quite contain all the > necessary information for this task. > > For example, the default ufw configuration uses iptables to setup > > Chain ufw-not-local (1 references) > pkts bytes target prot opt in out source destination > 779 46756 RETURN 0 -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL > > which shows up as > > $ sudo nft -a list ruleset | grep "handle 247" > fib daddr type local counter packets 772 bytes 46336 Given that the rule is supposed to have been created with iptables-nft, I am surprised that it is presented in that way at all (the line you are showing doesn't match "handle 247" either). # uname -r; nft -V | head -n1 6.7.2-arch1-2 nftables v1.0.9 (Old Doc Yak #3) # nft flush ruleset # iptables-nft -A INPUT -m addrtype --dst-type LOCAL # nft list ruleset 2>/dev/null | sed -n 4p xt match "addrtype" counter packets 8 bytes 778 > > in in the "normal" output. However, in the JSON output critical > information (mainly "LOCAL") is entirely missing: > > $ sudo nft -a --json list ruleset | jq . | grep -B5 -A19 '"handle": 247' > { > "rule": { > "family": "ip", > "table": "filter", > "chain": "ufw-not-local", > "handle": 247, > "expr": [ > { > "xt": { > "type": "match", > "name": "addrtype" > } > }, > { > "counter": { > "packets": 778, > "bytes": 46696 > } > }, > { > "return": null > } > ] > } > }, > > > I understand that ufw is using a compatibility interface. Nevertheless, Indeed, the output you are getting is symptomatic of the presence of xtables glue. The output of nft list ruleset won't be round-trip safe. > I'm in need of a way to analyze firewall configurations of large number of > real-world systems programmatically. Can you suggest how I should approach > this? I can think of at least the following options: > > 1) Fix the JSON output. The xtables glue seems so disjoint that I wonder whether it would ever be fixed. > > 2) Ignore the JSON output and try to parse the output of "nft list > ruleset". This would not be reliable either. > > 3) Use JSON output of most of the stuff but fill the gaps by also > parsing "nft list ruleset". Nor this. > > 4) Try to parse the raw netlink traffic seen in "nft --debug=netlink list > ruleset". This doesn't seem very useful either. # nft --debug=netlink list ruleset 2>/dev/null | head -n3 ip filter INPUT 2 [ match name addrtype rev 1 ] [ counter pkts 287 bytes 59823 ] > > 4) Create separate tools for parsing iptables and netfilter rules and > hope that no system mixes these two. As things stand, this seems more realistic to me. Perhaps one of the developers would have a better idea, though. -- Kerin Millar