This is an old revision of the document!
NFTables Configuration
The best reference for nftables is at the dedicated wiki wiki nftables. Some other references I found nftables router. The reference at stosb is good, but not for a router Explaining My Configs: nftables.
After a lot of experimenting the following is my NFTables router configuration file. Create the following file called: “router.nft”.
Sample NFTables configuration
sudo vim /etc/nftables.conf
Some configuration notes
Refer to nftables wiki Matching packets by interface name. The use of interface index of the interface name causes an error on boot of the nftables.service, probably because the interface has not been brought up and indexed. Yet the name index of lo does work, perhaps it is predefined. Using the network interface name works. Use of name indexes is said to be faster than names. One possible way around this would be to have a basic nftables.service script for boot, dropping all connectivity, except local ssh ports and have a separate end of boot script with the full nftables script.
It is important to remember that the order of the commands in a chain is very important. An earlier accept, drop or reject action will terminate further processing of a packet within that chain.
My setup is based upon a static wan ip address. Dynamic ip wan address would require additional configuration and would not be as stable.
Some key related commands:
- To load a nft configuration file:
sudo nft -f /etc/nftables.conf
(not used for start configuration) (was router.nft?) - The nftables configuration file can be made into an executable script as follows.
- Add the following at the top of the file:
- #!/user/sbin/nft -f
chmod +x router.nft
- Use
whereis nft
to find where the nft executable is located - Remember that if located in same directory use
sudo ./router.nft
to run.
- To list currently loaded nft tables:
sudo nft list tables
- To list a loaded table use:
sudo nft list table inet firewall
, the inet firewall as listed fromsudo nft list table
- To list a loaded table use:
sudo nft list table ip nat
. (The -a option lists handles.) - To add elements to nat tcp_nat_map:
sudo nft add element nat tcp_nat_map { 81 : 192.168.1.100, 8080 : 192.168.1.101 }
.
There seem to be a number of way to to allow port forwarding from WAN to LAN.
Using type filter hook forward priority 0; policy drop;
for forwarding and type nat hook prerouting priority 0;
for prerouting.
Prerouting: iif $external tcp dport { http, https } dnat home_srv
, where “home_srv” host DNS resolves to IP address
- ip daddr home_srv ct status dnat accept
- ip daddr home_srv tcp dport {http, https} accept (not checked, taken from IPTables -A FORWARD -p tcp -d 192.168.99.100 –dport 80 -j ACCEPT)
- tcp dport {http, https} ct state new accept
Hairpin NAT
After setting up and getting my router to function I found that I could not access my local http pages. This is a NAT problem*. The solution is hairpin NAT also known as NAT loopback or NAT reflection.
The following are some resources that discuss this:
- StackExchange How does NAT reflection (NAT loopback) work?
The following commands provide a complete ipv4 NAT solution, including hairpin NAT using NFTables firewall:
- prerouting (standard)
- sudo nft add rule ip nat prerouting ip daddr 2.3.4.5/32 tcp dport {http, https} dnat 192.168.3.11
- postrouting (standard)
- Using masquerade: sudo nft add rule ip nat postrouting ip saddr 192.168.3.0/24 ip oifname ppp1 masquerade
- Using snat: sudo nft add rule ip nat postrouting ip saddr 192.168.3.0/24 ip oifname ppp1 snat 2.3.4.5/32
- postrouting (hairpin)
- Using snat to external ip: sudo nft add rule ip nat postrouting ip saddr 192.168.3.0/24 ip daddr 192.168.3.11/32 tcp dport {http, https} counter snat 2.3.4.5/32
- Using snat to internal ip: sudo nft add rule ip nat postrouting ip saddr 192.168.3.0/24 ip daddr 192.168.3.11/32 tcp dport {http, https} counter snat 192.168.3.1/32
- Using masquerade (to internal ip): sudo nft add rule ip nat postrouting ip saddr 192.168.3.0/24 ip daddr 192.168.3.11/32 tcp dport {http, https} counter masquerade
Notes:
- Where options for snat or masquerade are given, either snat or masquerade can be used, not both.
- Hairpin snat to the router/gateway external address is expected to function correctly.
- masquerade versus snat:
- Masquerade operates directly with dynamic wan ip.
- Dynamic wan ip does not operate with snat reliably.
- Masquerade requires more router resources than snat
This only affects local networks that use NAT which is basically mandatory for IPv4 and not required for IPv6, hence unless NAT is used in a IPv6 local network hairpin.
* Whilst investigating this matter commentary it was often stated that this problem is better solved using DNS. I was some what confused by SSL certificate DNS verification, and local DNS resolution. Then there was all the talk about dual horizon or split DNS. Basically the certificate authority uses WAN DNS to verify ownership of web site. I need to program my domain name suppliers DNS with the certificate info and this is used to verify my control ownership of the domain and allow issue of certificate to me. My LAN DNS is effectively a split horizon in that LAN DNS server resolves my LAN addresses, all external address are ultimately pas to the WAN for resolution, although the results may be temporarily store locally to speed up subsequent DNS queries. If my LAN servers have valid certificates and the LAN DNS addresses used are valid then it serves the secure service reliably off the LAN. Presumably it still needs to perform CA verification of the WAN, not sure about this. The local DNS server cannot be directly addressed from the WAN. If a WAN incoming service passes my WAN router firewall and is correctly directed directed to the LAN server and this server has a valid certificate then this will also function using the WAN address. A LAN DNS is definitely a more elegant method to serve local content than hairpin NAT!. This also explains why my NFTables hairpin NAT has always been showing no usage since I first experimented. The LAN DNS makes it unnecessary. I have recently been using Traefik in a Docker container to act as my back-end server proxy and manager of certificates main and sub-domain wildcard.
Diagram 1
DNAT, Simple, Hairpin, not working and Hairpin working
(Where the gateway (router) is on the network WAN device)
Some additional hand NFtables commands to adjust online:
sudo nft list table inet firewall -a -n
; where:-a
: show handle number (line numbers)-n
: shows names (default), e.g. dport ssh-nn
: shows numbers e.g. dport 22
sudo nft list table ip nat -a
: list table ip nat with handlessudo nft delete rule ip nat postrouting handle 12
: deletes rule in table ip nat postrouting handle 12sudo nft delete rule inet firewall forward handle 32
: deletes rule table inet firewall forward handle 32sudo nft add rule inet firewall input position 39 tcp dport domain counter
: within table inet firewall input, before handle 39, adds rule tcp port domain counter
A good reference for this is in wiki.nftables.org Quick reference-nftables in 10 minutes
IPv6 NFTables Setup (For Hurricane Electric Tunnel)
sudo nft add rule inet firewall forward iifname “he-ipv6” counter jump base_checks
sudo nft add rule inet firewall forward iifname “br0” oifname “he-ipv6” counter accept
The following rule did not work: sudo nft add rule inet firewall forward iifname “he-ipv6” oifname “br0” counter jump base_checks
Some other handy pointers and commands related to the NFtables setup:
- To load the nft configuration file:
sudo nft -f router.nft
- The nftables configuration file can be made into an executable script as follows.
- Add the following at the top of the file:
- #!/user/sbin/nft -f
- chmod +x router.nft
- Use
whereis nft
to find where the nft executable is located - Remember that if located in same directory use
sudo ./router.nft
to run.
- To list currently loaded nft tables: “sudo nft list tables”
- To list a loaded table use:
sudo nft list table inet firewall
, the inet firewall as listed fromsudo nft list tables
- To list a loaded table use:
sudo nft list table ip nat
. (The-a
option lists handles.) - To add elements to nat tcp_nat_map:
sudo nft add element nat tcp_nat_map { 81 : 192.168.1.100, 8080 : 192.168.1.101 }
. (For some reason the version of nft I have will not read in the elements via a nft -f command.)
Some info on NAT with NFTables and be seen in the following references: wiki.nftables.org - Performing Network Address Translation (NAT), Ars Technica SNAT vs. DNAT vs. Masquerading and wiki.gentoo.org Nftables/Examples. It is interesting to see that the nftables wiki states that iptables NAT must be turned off with sudo rmmod iptable_nat
.
How to make NFTables configuration permanent / restore on boot
The systemd service nftables can be used to auto start nftables and is setup to load the nftable configuration file at /etc/nftables.conf. Use sudo systemctl status/start/stop/reload/enable/disable nftables
.
Start and reload basically run /usr/sbin/nft -f /etc/nftables.conf
, whereas stop runs /usr/sbin/nft flush ruleset
The command systemctl show -p FragmentPath nftables
shows where the systemctl script is located; /lib/systemd/system/nftables.service, in my case.
See nftables-systemd on github.com