On Wed, 23 Oct 2024, at 11:10 PM, Matt Zagrabelny wrote: > Greetings, > > I have, what could be, a simple question. > > Do folks put their connection tracking rules near the top (earlier > rule) of their ruleset, or near the bottom (later rule)? > > I used to believe that it "made sense" to have the conntrack rules > earlier in the rule set, because the packets would be > related/established and wouldn't need to hit the other rules > unnecessarily. > > However, the conntrack requires more kernel memory to track the > packets. Therefore if every connection is tracked, then that would > drive up the memory required for having the conntrack track every > connection/packet - thus possibly exhausting the memory and not > allowing other packets through. You may be under the impression that rules of this sort electively subject the matching packets to the machinations of the conntrack subsystem at large but that's not quite how it works. Once the conntrack subsystem has been activated, all packets are within its purview past a certain threshold. The latest revision of the nft(8) manual briefly touches on this. "With nat type chains, there’s a lower excluding limit of -200 for priority values, because conntrack hooks at this priority and NAT requires it." Therefore, if you want for a given packet not to be able to produce a new entry in the conntrack table, you must go out of your way to make it so. Consider the following nftables ruleset, which employs "notrack" to that effect. table inet filter { chain INPUT { type filter hook input priority filter; policy accept; # The mere presence of this rule will be enough to # activate conntrack at large. For many distributions, # such entails loading the relevant kernel modules on # demand. ct state established,related accept # There is no chance of otherwise matching packets # traversing lo based on ct state (see below). iifname "lo" accept } } table inet raw { chain OUTPUT { # raw evaluates as -300 n.b. lower than -200 type filter hook output priority raw; policy accept; # Don't bother tracking packets traversing the loopback # interface. In many cases, this can be an acceptable # means of conserving resources. oifname "lo" notrack } } The same is true of iptables, only the syntax is different. There, one might use the built-in raw table and the CT xtables extension. -t raw -A OUTPUT -o lo -j CT --notrack > > So, now I am wondering if the conntrack rules should (always?) go at > the end of my ruleset. Rules that match _exclusively_ on ct state should not be situated at the end of a chain. The consequence of doing so will be that the probability of inadvertently composing a faulty ruleset is markedly increased. > > Or do folks put the conntrack rules at the top/middle/bottom of the > ruleset on a case-by-case basis? Generally speaking, they do not. Keep in mind that if a ruleset defines up front a suitable verdict for all four of the states, "established", "related", "invalid" and "untracked" then, by definition, any packet that continues being processed must be in the "new" state. Such is convenient because it obviates the utility of explicitly matching on the "new" state. tcp dport 22 accept # convenient tcp dport 22 ct state new accept # less convenient -A INPUT -p tcp --dport 22 -j ACCEPT # convenient -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT # tiresome However, many rulesets never have to contend with untracked packets at all. That is why it is common to see examples of stateful rulesets that only ever explicitly match against the "established", "related" and "invalid" states. -- Kerin Millar