In the latest iteration of my home networking stack I am factoring out my firewall from my router into discrete unit. I decided to try a transparent firewall as this has the advantage of a reduced attack surface. Plus i used it as a chance to try out nftables.
Hardware: I chose the raspberry pi compute module 4 with the DFRobot dual-nic carrier. This board is able to push about 1Gbit of traffic while consuming less than 2 watts of power! All this in about 2 inches square. To enable the 2nd NIC on this board I had to recompile the kernel – see my Recompiling the Kernel on the Pi Compute Module 4 post on this.
Once this is done, we enable the serial port so we can manage the firewall out of band. I specifically do not want a listening port on the firewall (remember, its transparent!). To do this I hooked up a FT232 USB->Serial converter to pins to the DFRobot headers. I then connect to the firewall using minicom, using 115200 8N1 for the serial params. Inside the pi ensure you run “raspi-config” and enable the serial port under the interfaces section.
I also turned off dhcpd to ensure the pi is only passing through – not actually on the network. “systemctl disable dhcpcd; systemctl stop dhcpd”
To get nftables, on the pi, I did have to install it: “apt-get install nftables.” Then we write some basic rules. Subjectively speaking, nftables is a lot nicer to write rules for than iptables, ebatables, etc. Here’s a brief sample:
table inet firewallip { chain c1 { type filter hook input priority 0; meta nftrace set 1 policy drop ip saddr 10.55.0.10 icmp type echo-request accept reject with icmp type host-unreachable } }
A brief description of this example is apropos:
- The table is named “firewallip” (doesn’t matter) but “inet” can be one of ip, arp, ip6, bridge, inet, netdev (see https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes)
- The chain, c1 (again, name doesn’t matter – although it makes sense to have it match the filter type) is instantiated with the type line – this is extremely significant, as rules will only match packets if on the proper filter type.
- meta nftrace set 1 (can be 0 or 1) lets us run “nft monitor trace” and trace the rules live
- there are a LOT of things that can go inside a chain – however it is worth noting “reject” phrases such as the one i show are not applicable in some chain types, such as bridge.
Note that for connection tracking inside a bridge i actually had to build my kernel yet again. If you don’t do this youll get “Protocol unsupported” errors if you try to do connection tracking inside a bridge. To enable this module, follow the same steps as in the procedure for adding realtek driver, however the menu item to enable in the kernel is under: “Networking support” -> “Networking options” -> “Network packet filtering framework” -> “IPV4/IPV6 bridge connection tracking support”. Or just add “CONFIG_NF_CONNTRACK_BRIDGE=m” to your “.config” file inside the linux tree. Once this is done you need to ensure “nf_conntrack_bridge” is loaded: “modprobe nf_conntrack_bridge” should do you.
At this point we can do things like connection tracking INSIDE the bridge. Which is great. I won’t post my actual tableset, but here’s a beginning one that works well, allowing for ssh/http/https from the LAN (assuming LAN is on eth1), and only established connections and arp otherwise.
table bridge firewall { chain input { type filter hook input priority 0; policy drop; } chain output { type filter hook output priority 0; policy drop; } chain forward { type filter hook forward priority 0; policy drop; meta protocol {arp} accept ct state established,related accept iifname eth1 tcp dport {ssh,http,https} accept } }
Unfortunately, all this fun on the pi doesn’t seem as readily available on centos. Cent 7.6 is still on a 3.x version of the kernel, and 8.4 is still on a 4.x version. You can upgrade to kernel using elrepo-kernel (http://elrepo.org/tiki/kernel-ml).