On Mon, 6 Jun 2022 09:48:23 -0400 Gio <gioflux@xxxxxxxxx> wrote: > I need some help understanding the proper way to do conntrack > bidirectionally in a secure way. Could someone help validate my > understanding and indicate if the below ruleset and approach is > correct? > > My goal is to allow incoming connections to tcp/80 (input hook) and > all response traffic (output hook) without blindly allowing more ports > than intended for this one service running on tcp/80. > > table ip legacy { > chain root_out { > type filter hook output priority filter; policy drop; > tcp sport 80 ct state established,related accept > } > > chain root_in { > type filter hook input priority filter; policy drop; > tcp dport 80 accept > } > } > > I have seen other firewall examples where the input chain just blindly > allows all reply packets via 'ct state established,related accept' > like so : > > chain root_in { > type filter hook input priority filter; policy drop; > ct state established,related > tcp dport 80 accept > } > > I am not sure if my first example is correct and secure; should 'ct > state established,related accept' always be in the input hook for > response packets or only needs to be in the output hook? I would describe your example as neither correct nor secure. For one thing, both chains would impede traffic traversing "lo", which is normally inadvisable. For another, the rule in the root_in chain does not match on the ctstate, despite being the ruleset being of a stateful design. For instance, you probably don't intend for your rule to match packets in the "invalid" state, but it would. Here is a sample ruleset that might prove instructive. table inet legacy { chain root_in { type filter hook input priority filter; policy drop; ct state established,related accept iifname "lo" accept meta l4proto ipv6-icmp accept tcp dport 80 fib daddr type local ctstate new accept } } Note the use of the inet family so as not to inadvertently leave things wide open in the case of IPv6. The conntrack state machine already does a good job of matching essential ICMPv4 packets pertaining to a TCP flow against the "related" ctstate. Filtering ICMPv6 in such a way that does not result in breakage is comparatively difficult, hence the blanket rule there. The fib check will ensure that the destination IP address was specified as one that is actually assigned to your host. The use of "ctstate new" should be self-explanatory. If you set the "net.netfilter.nf_conntrack_tcp_loose" sysctl to a value of "0", it will behave even more strictly in that a packet that would result in a new TCP flow (from the perspective of conntrack) must have only the SYN flag set. I would advise against hooking output with a drop policy, unless you have a concrete reason to do so. In the event that you do, the "root_out" chain could share some of the characteristics of the "root_in" chain, such as allowing established/related to pass, as well as packets traversing the lo interface. You would not then require any counterpart for the tcp dport 80 rule. However, you would need to consider other matters, such as allowing for the passage of packets destined to nameservers and those required for your package manager to function, to name a few. -- Kerin Millar