This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. {{tag>linux router NFtables firewall}} =====NFTables Configuration===== The best reference for nftables is at the dedicated wiki [[https://wiki.nftables.org/|wiki nftables]]. Some other references I found [[https://gist.github.com/wolfhechel/db7ed3be31feb104752e|nftables router]]. The reference at stosb is good, but not for a router [[https://stosb.com/blog/explaining-my-configs-nftables/|Explaining My Configs: nftables]]. After a lot of experimenting the following is my NFTables router configuration file. Create the following file called: "router.nft". Dont forget to ensure the router is allowed to forward packets: * ''sudo sysctl net.ipv4.ip_forward'' to check * ''sudo sysctl net.ipv4.ip_forward=1'' to set or =0 turn off * ''sudo sysctl net.ipv6.conf.all.forwarding=1'' similar for ipv6 ====iptables==== It is not a good idea to have both iptables and nftables rules setup at the same time. *To check existing iptables rules: ''sudo iptables -S'', the output should be as follows, indicating no restrictions: <code text>-P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT</code> *The iptables rules can be flushed with ''sudo iptables -F''. ====Sample NFTables configuration==== ''sudo vim /etc/nftables.conf'' ++++ Router NFtable Setup - ''/etc/nftables.conf''| <color blue>#!/usr/sbin/nft -f </color> ++ | + <color #202000/#F0F0E0> nftables scripts need this shebang with the -f option to run</color>++ \\ <tab3> \\ <tab3><color blue># script variable definitions</color> ++ | \\ +<color #202000/#F0F0E0> * Device definitions can be found using ''ip a'' \\ * IP address should also be able to be found using ''ip a''.\\ * Where services are on separate server, virtual, container, etc., the ''ip a'' may need to be run in on these servers.\\ * Another option would be to use ''sudo nmap -sn 192.168.1.0/24'' to find all the related active ip address ip range. </color>++ \\ <tab3>define <color red>wan = eno1</color><color blue> #definition of wan device label, setup as ppp1 for pppoe</color>\\ <tab3>define <color red>modem = eno1</color><color blue> #definition of modem device label</color>\\ <tab3>define <color red>lan = br0</color><color blue> #definition of lan device label</color>\\ <tab3>define <color red>lan_ip4 = 192.168.1.0/24</color><color blue> #definition of lan IPV4 range</color>\\ <tab3>define <color red>router_ip4 = 192.168.1.1</color><color blue> #definition of router IPv4 address</color>\\ <tab3>define <color red>modem_ip = 192.168.5.2</color><color blue> #definition of modem IPv4 address</color> ++ | \\ + <color #202000/#F0F0E0>The $modem router interface has been assigned the IP address 192.168.5.2, as the modem device IP address is 192.168.5.1, See [[https://wiki.kptree.net/doku.php?id=linux_router:network#ubuntu_network_-_interface_setup|network interface]].</color>++\\ <tab3>define <color red>modem_ip4 = 192.168.5.0/24</color><color blue> #definition of modem IPv4 address range</color> ++ | \\ + <color #202000/#F0F0E0>The $modem router interface has been assigned the IP address 192.168.5.2, as the modem device IP address is 192.168.5.1, See [[https://wiki.kptree.net/doku.php?id=linux_router:network#ubuntu_network_-_interface_setup|network interface]].</color>++\\ <tab3>define <color red>http_server = 192.168.1.12</color><color blue> #definition of http server IPv4 address</color>\\ <tab3>define <color red>mail_server = 192.168.1.18</color><color blue> #definition of mail server IPv4 address</color>\\ <tab3>define <color red>wan_ip4 = 112.213.222.38</color><color blue> #definition of wan IPv4 address</color>\\ <tab3>define <color red>vpn = wg0</color><color blue> #definition of wireguard vpn device label</color>\\ <tab3>define <color red>vpn_ip = 192.168.6.0/24</color><color blue> #definition of wireguard IPv4 address range</color>\\ <tab3>define <color red>vpn_ip4 = 192.168.6.1</color><color blue> #definition of wireguard IPv4 server address</color>\\ <tab3>define <color red>vpn_port = 51914</color><color blue> #definition of wireguard server port</color>\\ <tab3><color #000060/#F0F0FF>#define</color> <color #060000/#FFF0F0>he_tunnel = he-ipv6</color><color blue> #</color>\\ <tab3> \\ <tab3><color blue> # Clean out the current ruleset</color>\\ <tab3> flush ruleset ++ | \\ +<color #202000/#F0F0E0>Clears the previous ruleset. This flushes out all the tables, chains and rules so we can start clean. This is not done automatically so without this, previously added rules would still be in effect. See nft man page <tab1> [[https://www.netfilter.org/projects/nftables/manpage.html#lbAO|Ruleset]] for more information.</color>++ \\ <tab3> \\ <tab3> table inet firewall { ++ | \\ +<color #202000/#F0F0E0>This defines a table. A table is a container for chains (and sets). This line defines a table with the family ''inet'' and name ''firewall''. ''inet'' is a family that encompasses both IPv4 and IPv6 internet protocols. I wanted a shared configuration between both so I chose this one, alternatively I could have restricted it to either by using ip or ip6. See nft man page <tab1> [[https://www.netfilter.org/projects/nftables/manpage.html#lbAP|Tables]] for more information.</color> ++ \\ <tab6> \\ <tab6><color blue> # The set controllist is used to dynamically add IP address(es) to a timed drop list for LAN access control </color> ++ | \\ +<color #202000/#F0F0E0> This has been separately described in detail at the following link: [[linux_router:nftables_control]], also see wiki.nftables on<tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Sets#Named_sets| Names Sets]] & nft man page <tab1> [[https://www.netfilter.org/projects/nftables/manpage.html#lbAS|Sets]].</color> ++ \\ <tab6> set controllist {\\ <tab9> type ipv4_addr\\ <tab9> flags interval\\ <tab9> flags timeout\\ <tab6> }\\ <tab2> \\ <tab6><color blue> # Remember that the input and output chains only refer to this machine.</color>\\ <tab6><color blue> # The forward and nat chains refer to routing.</color>\\ <tab6><color blue> # open ports refers to ports to open for input to this machine.</color>\\ <tab6><color blue> # ssh is port 22 for SSH on both TCP and UDP, but we only use TCP!</color>\\ <tab6><color blue> # bootps, port 67 and bootpc, port 68 are for DHCP on both TCP and UDP, but we only use UDP!</color>\\ <tab6><color blue> # domain is port 53 for DNS requests on TCP & UDP.</color>\\ <tab6><color blue> # openvpn is port 1194 on both TCP and UDP, but we will not be using this antiquated protocol.</color>\\ <tab6>\\ <tab6> set tcp_open_ports { ++ | \\ + <color #202000/#F0F0E0> This creates a set of type inet_service (port number or range). The flags interval directive enables port ranges to be used. After that, you just set elements to add members to the set. Having a set is a clean and efficient way to later reference all of these values in the rules. We can use port numbers, service names and port ranges.\\ Sets provide a performant way to use generic sets of data, that can be dynamically manipulated, so they are very suitable for tasks like IP blocking. More about sets on the nftables wiki page<tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Sets#Named_sets| Names Sets]] & nft man page <tab1> [[https://www.netfilter.org/projects/nftables/manpage.html#lbAS|Sets]]. </color>++ \\ <tab9> type inet_service; flags interval;\\ <tab9> elements = {\\ <tab12> ssh,\\ <tab12> domain\\ <tab9> }\\ <tab6> }\\ <tab6> \\ <tab6> set udp_open_ports { ++ | \\ + <color #202000/#F0F0E0> This creates a set of type inet_service (port number or range). The flags interval directive enables port ranges to be used. After that, you just set elements to add members to the set. Having a set is a clean and efficient way to later reference all of these values in the rules. We can use port numbers, service names and port ranges.\\ Sets provide a performant way to use generic sets of data, that can be dynamically manipulated, so they are very suitable for tasks like IP blocking. More about sets on the nftables wiki page<tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Sets#Named_sets| Names Sets]] & nft man page <tab1> [[https://www.netfilter.org/projects/nftables/manpage.html#lbAS|Sets]]. </color>++ \\ <tab9> type inet_service; flags interval;\\ <tab9> elements = {\\ <tab12> domain,\\ <tab12> <color red>$vpn_port</color>, <color blue># Wireguard port</color>\\ <tab12> 60000-61000 <color blue># mosh port interval</color>\\ <tab9> }\\ <tab6> }\\ <tab3>\\ <tab6> chain tcp_cek { ++ | \\ + <color #202000/#F0F0E0>A chain is a container for rules. This a regular (non-base) chain. As a regular chain does not define a type or hook it can only be called from another chain with a jump (or goto) statement. See wiki.nftables <tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains| Configuring Chains]] & nft man page <tab1> [[https://www.netfilter.org/projects/nftables/manpage.html#lbAQ|Chains]]for more information</color>++ \\ <tab9><color blue> # bad tcp -> avoid network scanning: </color>\\ <tab9> tcp flags & (fin|syn) == (fin|syn) counter drop\\ <tab9> tcp flags & (syn|rst) == (syn|rst) counter drop\\ <tab9> tcp flags & (fin|syn|rst|psh|ack|urg) < (fin) counter drop\\ <tab9> tcp flags & (fin|syn|rst|psh|ack|urg) == (fin|psh|urg) counter drop\\ <tab6> } \\ <tab3> \\ <tab6> chain icmp_cek {++ | \\ + <color #202000/#F0F0E0>A chain is a container for rules. This a regular (non-base) chain. As a regular chain does not define a type or hook it can only be called from another chain with a jump (or goto) statement. See wiki.nftables <tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains| Configuring Chains]] for more information.\\ More information on icmp can be seen at nftables man [[https://www.netfilter.org/projects/nftables/manpage.html#lbCG|ICMP Header Expression]].\\ Our table type is inet which means IPv4 or IPv6. However, ICMP is different to ICMPv6. This means that we have to do our checks with version specific directives. The ip and ip6 directives do that. After more version specific checks, we match the version specific types.</color>++ \\ <tab9><color blue> # no ping floods</color>\\ <tab9> ip protocol icmp counter\\ <tab9> ip protocol icmp limit rate over 2/second burst 5 packets counter drop ++ | + <color #202000/#F0F0E0> This limits the incoming rate of icmp packets handled </color>++\\ <tab9> ip6 nexthdr ipv6-icmp counter\\ <tab9> ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 2/second burst 5 packets counter drop ++ | + <color #202000/#F0F0E0> This limits the incoming rate of icmpv6 packets handled </color>++\\ <tab9> \\ <tab9><color blue> # allow icmp</color> \\ <tab9> ip protocol icmp icmp type { ++ | + <color #202000/#F0F0E0> This allows the icmp packets to be handled </color>++\\ <tab12> echo-request, echo-reply, time-exceeded,\\ <tab12> parameter-problem, destination-unreachable\\ <tab9> } counter accept\\ <tab9> \\ <tab9> ip6 nexthdr icmpv6 icmpv6 type { ++ | + <color #202000/#F0F0E0> This allows the icmpv6 packets to be handled </color>++ \\ <tab12> echo-request, echo-reply, time-exceeded,\\ <tab12> parameter-problem, destination-unreachable,\\ <tab12> packet-too-big, nd-router-advert, nd-router-solicit,\\ <tab12> nd-neighbor-solicit, nd-neighbor-advert,\\ <tab12> mld-listener-query\\ <tab9> } counter accept\\ <tab6> }\\ <tab6> \\ <tab6> chain input {++ | \\ + <color #202000/#F0F0E0> See wiki.nftables <tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains| Configuring Chains]] for more information. The name does not actually matter, but was chosen to align with iptables. This is a base chain, hence base chain type and base chain hook must be defined.</color>++ \\ <tab9> type filter hook input priority 0; policy drop; ++ | \\ + <color #202000/#F0F0E0> This statement defines the the base chain's: type (filter), hook (input), priority (0) and policy (drop), any packet that makes it way through all the table chain is at the end drop (nftable default policy is accept, hence it is necessary to set so). See wiki.nftables <tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains| Configuring Chains]] for more information.</color>++ \\ <tab9> \\ <tab9><color blue> # drop connections from blacklisted addresses</color> \\ <tab9><color blue> # ip saddr @blacklist counter drop</color> \\ <tab9>\\ <tab9><color blue> # Cek valid tcp packet</color>\\ <tab9> ip protocol tcp jump tcp_cek ++ | + <color #202000/#F0F0E0> We use the jump statement as we want to return and continue to process chains after this statement.</color>++\\ <tab9> \\ <tab9><color blue> # Cek valid icmp communications</color>\\ <tab9> jump icmp_cek ++ | + <color #202000/#F0F0E0> We use the jump statement as we want to return and continue to process chains after this statement.</color>++\\ <tab9>\\ <tab9><color blue> #count all the wan SSH attemps</color>\\ <tab9><color #000060/#F0F0FF> #iifname </color><color #060000/#FFF0F0>$wan</color><color #000060/#F0F0FF> tcp dport ssh counter</color> \\ <tab9>\\ <tab9><color blue> # avoid brute force on ssh: (if using external ssh)</color> \\ <tab9><color #000060/#F0F0FF> #tcp dport ssh limit rate over 8/minute counter drop</color> ++ | \\ + <color #202000/#F0F0E0>Rate limit and count WAN ssh attempts. As I only allow ssh locally on LAN or via VPN I drop all such incoming traffic. (Also probably would not work without NAT prerouting dnat.)</color>++ \\ <tab6>\\ <tab6><color blue> # In general input filter drops and limits list should be placed above here here</color> ++ | \\ + <color #202000/#F0F0E0>Position of rules is important in chains. Once a chain rule accepts, drops or rejects a packet subsequent chain rules are no longer processed!)</color>++ \\ <tab6>\\ <tab9><color blue> # accept established connections</color> ++ | \\ + <color #202000/#F0F0E0>This rule is to allow already established or related connections through. If the connection has already been established, it probably means it was already allowed by us earlier and we can just continue allowing it.\\ ''ct'' is used to tap into the connection tracking entry associated with the packet. In this case, we are accessing the state of the connection, and checking if it is in the set ''{established, related}''. If it is in the set, accept the packet, otherwise, continue to the next rule. </color>++ \\ <tab9> ct state established, related counter accept \\ <tab9> \\ <tab9><color blue> # drop invalid state packets</color> ++ | \\ + <color #202000/#F0F0E0>This is similar to the previous line, but this time, instead of checking if the state is within a set, we only check if the connection state is ''invalid'' and if so, we drop the packet. That is, we just ignore the packet as if it never came in. Otherwise continue on to the next rule.</color>++ \\ <tab9> ct state invalid counter drop\\ <tab9> \\ <tab9><color blue> # accept input from loopback interface</color> ++ | \\ + <color #202000/#F0F0E0>It is generally accepted practice to accept all packets from the loopback interface.</color>++ \\ <tab9> iif lo counter accept\\ <tab9> \\ <tab9><color blue> # accept input from LAN</color> ++ | \\ + <color #202000/#F0F0E0>Filtering of LAN incoming packets. It can be done by port or port sets, however in general all the LAN interface inputs can be accepted. Filtering by port probably does improve security, but any applications using ports not in the filter will be broken until added.</color>++ \\ <tab9> iifname <color red>$lan</color> counter accept <color blue># By port is a pain in the arse!</color>\\ <tab9><color #000060/#F0F0FF> # iifname </color><color #060000/#FFF0F0>$lan</color><color #000060/#F0F0FF> tcp dport { ssh, domain, 667, 953 } counter accept</color> ++ | + <color #202000/#F0F0E0>This is used to accept specific LAN tcp ports, required if not accepting all.</color>++ \\ <tab9><color #000060/#F0F0FF> # iifname </color><color #060000/#FFF0F0>$lan</color><color #000060/#F0F0FF> udp dport { domain, bootps, bootpc, 667, 953} counter accept</color> ++ | + <color #202000/#F0F0E0>This is used to accept specific LAN tcp ports, required if not accepting all.</color>++ \\ <tab9> \\ <tab9><color blue> # accept input from VPN</color> ++ | \\ + <color #202000/#F0F0E0>Filtering of VPN incoming packets. It can be done by port or port sets, however in general the all the VPN interface inputs can be accepted. Filtering by port probably does improve security, but any applications using ports not in the filter will be broken until added.</color>++ \\ <tab9> iifname <color red>$vpn</color> counter accept <color blue># By port is a pain in the arse!</color>\\ <tab9> ip daddr <color red>$vpn_ip4</color> udp dport <color red>$vpn_port</color> counter accept ++ | \\ + <color #202000/#F0F0E0> As the VPN is hosted on this local machine this chain allows packets that have been preroute dnat to the VPN IP address to be accepted.</color>++ \\ <tab9><color #000060/#F0F0FF> # iifname </color><color #060000/#FFF0F0>$wan</color><color #000060/#F0F0FF> udp dport </color><color #060000/#FFF0F0>$vpn_port</color><color #000060/#F0F0FF> ct state new counter accept</color>\\ <tab9>\\ <tab9><color blue> # everything else reject port unreachable</color> \\ <tab9> ct state new counter reject ++ | \\ + <color #202000/#F0F0E0> Any other new packets coming to the filter input are unknown and should be dropped or rejected. Dropped means the packet is simply ignored and not further processed. Reject means the packet is ignored and not further processed and a ICMP(v4 or v6) host-unreachable message is returned to the source. See wiki.nftables <tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Rejecting_traffic| Rejecting traffic]] for more information. </color>++\\ <tab6> }\\ <tab6>\\ <tab6> chain forward {++ | \\ + <color #202000/#F0F0E0> See wiki.nftables <tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains| Configuring Chains]] for more information. The name does not actually matter, but was chosen to align with iptables. This is a base chain, hence base chain type and base chain hook must be defined.</color>++ \\ <tab9> type filter hook forward priority 0; policy accept; ++ | \\ + <color #202000/#F0F0E0> This statement defines the the base chain's: type (filter), hook (forward), priority (0) and policy (accept), any packet that makes it way through all the table chain is at the end drop (nftable default policy is accept, hence it is necessary to set so). See wiki.nftables <tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains| Configuring Chains]] for more information.</color>++ \\ <tab9>\\ <tab9><color blue> #drop all IP addresses in the control list both source and destination</color> ++ | \\ +<color #202000/#F0F0E0> This has been separately described in detail at the following link: [[linux_router:nftables_control]], also see wiki.nftables on<tab1> [[https://wiki.nftables.org/wiki-nftables/index.php/Sets#Named_sets| Names Sets]] & nft man page <tab1> [[https://www.netfilter.org/projects/nftables/manpage.html#lbAS|Sets]].</color> ++ \\ <tab9> ip daddr @controllist counter drop\\ <tab9> ip saddr @controllist counter drop\\ <tab9>\\ <tab9><color blue> #limit the IP address range bandwidth using WAN</color> ++ | \\ +<color #202000/#F0F0E0> This is a simple form of traffic control. My DHCP / DNS place critical IP address in the 192.168.1.1 to 99 range, other equipment is automatically assigned in the 100-200 range. The chains below then limit traffic speed into / out of these IP addresses. This has been separately described in detail at the following link: [[linux_router:nftables_control#Limit Rate on IP Address Range]] </color> ++ \\ <tab9> iifname <color red>$wan</color> ip daddr { 192.168.1.100-192.168.1.200 } limit rate over 1300kbytes/second burst 3000kbytes counter drop\\ <tab9> oifname <color red>$wan</color> ip saddr { 192.168.1.100-192.168.1.200 } limit rate over 750kbytes/second burst 1200kbytes counter drop\\ <tab9>\\ <tab9> oifname <color red>$wan</color> tcp flags syn tcp option maxseg size set rt mtu <color blue> # This automatically set the tcp mtu</color>\\ <tab9><color #000060/#F0F0FF> #oifname </color><color #060000/#FFF0F0>$wan</color><color #000060/#F0F0FF> tcp flags syn tcp option maxseg size set 1452 counter</color> <color blue> # This set the tcp mtu directly to a maximum value</color>\\ <tab6>\\ <tab6><color blue> # In general forward filter drops and limits list should be placed above here here</color> ++ | \\ + <color #202000/#F0F0E0>Position of rules is important in chains. Once a chain rule accepts, drops or rejects a packet subsequent chain rules are no longer processed!)</color>++ \\ <tab6>\\ <tab9><color blue> # accept established connections</color> ++ | \\ + <color #202000/#F0F0E0>This rule is to allow already established or related connections through. If the connection has already been established, it probably means it was already allowed by us earlier and we can just continue allowing it.\\ ''ct'' is used to tap into the connection tracking entry associated with the packet. In this case, we are accessing the state of the connection, and checking if it is in the set ''{established, related}''. If it is in the set, accept the packet, otherwise, continue to the next rule. </color>++ \\ <tab9> ct state established, related counter accept\\ <tab9>\\ <tab9><color blue> # drop invalid state packets</color> ++ | \\ + <color #202000/#F0F0E0>This is similar to the previous line, but this time, instead of checking if the state is within a set, we only check if the connection state is ''invalid'' and if so, we drop the connection. That is, we just ignore the packet as if it never came in. Otherwise continue on to the next rule.</color>++ \\ <tab9> ct state invalid counter drop\\ <tab9>\\ <tab9><color blue> #All traffic from lan is accepted</color>\\ <tab9> iifname <color red>$lan</color> counter accept <color blue># accept anything from the LAN, keep it simple.</color>\\ <tab9>\\ <tab9><color blue> # Allow traffic coming from the vpn</color> \\ <tab9> iifname <color red>$vpn</color> counter accept <color blue># accept anything from the VPN, keep it simple.</color>\\ <tab9>\\ <tab9><color blue> # Allow new connections to internal servers, but only those that have had dnat performed.</color> \\ <tab9><color blue> # The server names can be used where host DNS resolves to IP address</color>\\ <tab9> ip daddr <color red>$http_server</color> ct status dnat counter accept ++ | \\ + <color #202000/#F0F0E0> The incoming WAN packets that have been preroute dnat to the http server IP address to be accepted.</color>++ \\ <tab9> ip daddr <color red>$mail_server</color> ct status dnat counter accept ++ | \\ + <color #202000/#F0F0E0> The incoming WAN packets that have been preroute dnat to the mail server IP address to be accepted.</color>++ \\ <tab9><color #000060/#F0F0FF> #ip daddr </color><color #060000/#FFF0F0>$vpn_ip4</color><color #000060/#F0F0FF> ct status dnat counter accept # required if VPN service is not on main router IP</color>\\ <tab9><color blue> # other option is perhaps more verbose, e.g.</color>\\ <tab9><color #000060/#F0F0FF> #ip daddr </color><color #060000/#FFF0F0>$http_server</color><color #000060/#F0F0FF> tcp dport {http, https} counter accept</color>\\ <tab9><color #000060/#F0F0FF> #ip daddr </color><color #060000/#FFF0F0>$mail_server</color><color #000060/#F0F0FF> tcp dport {http, https, pop3s, imaps, smtp} counter accept</color>\\ <tab9><color #000060/#F0F0FF> #ip daddr </color><color #060000/#FFF0F0>$vpn_ip4</color><color #000060/#F0F0FF> udp dport </color><color #060000/#FFF0F0>$vpn_port</color><color #000060/#F0F0FF> counter accept</color>\\ <tab9>\\ <tab9><color blue> #Hurricane Electric IPv6 tunnel</color>\\ <tab9><color #000060/#F0F0FF> #iifname </color><color #060000/#FFF0F0>$he_tunnel</color><color #000060/#F0F0FF> counter jump base_checks # not required as we do this for all connections above</color>\\ <tab9><color #000060/#F0F0FF> #iifname </color><color #060000/#FFF0F0>$lan</color><color #000060/#F0F0FF> oifname </color><color #060000/#FFF0F0>$he_tunnel</color><color #000060/#F0F0FF> counter accept</color>\\ <tab9>\\ <tab9><color blue> # drop and count everything else not accounted for,</color>\\ <tab9><color blue> # probably does not have much use on forward</color>\\ <tab9> counter drop\\ <tab6> }\\ <tab6>\\ <tab6> chain output {\\ <tab9><color blue> # We allow everything out </color>\\ <tab9> type filter hook output priority 0; policy accept;\\ <tab9>\\ <tab9><color blue> #counters for different devices, ip address, port, etc. place first</color>\\ <tab9> ip saddr <color red>$vpn_ip4</color> counter\\ <tab6>\\ <tab6><color blue> # In general output filter drops and limits list should be placed above here here</color> ++ | \\ + <color #202000/#F0F0E0>Position of rules is important in chains. Once a chain rule accepts, drops or rejects a packet subsequent chain rules are no longer processed!)</color>++ \\ <tab6>\\ <tab9><color blue> # Allow packets with existing state & count</color>\\ <tab9> ct state established,related counter accept\\ <tab9>\\ <tab9><color blue> # Allow all new packets on Router output & count</color>\\ <tab9> ct state new counter accept\\ <tab9>\\ <tab9><color blue> # Drop all invalid packet and count</color>\\ <tab9> ct state invalid counter drop\\ <tab6> }\\ <tab3> }\\ <tab3>\\ <tab3> table ip nat {\\ <tab3>\\ <tab6><color #000060/#F0F0FF> #map tcp_nat_map { </color>\\ <tab9><color #000060/#F0F0FF> # type inet_service : ipv4_addr </color>\\ <tab9><color #000060/#F0F0FF> # elements = { http : 192.168.1.16, https : 192.168.1.16} </color>\\ <tab6><color #000060/#F0F0FF> #} </color>\\ <tab6>\\ <tab6><color #000060/#F0F0FF> #map udp_nat_map { </color>\\ <tab9><color #000060/#F0F0FF> # type inet_service : ipv4_addr </color>\\ <tab6><color #000060/#F0F0FF> #} </color>\\ <tab6>\\ <tab6><color #000060/#F0F0FF> #chain wan_in { </color>\\ <tab9><color #000060/#F0F0FF> # tcp dport { http, https} counter dnat to </color><color #060000/#FFF0F0>$http_server</color>\\ <tab9><color #000060/#F0F0FF> # tcp dport { pop3s, imaps, smtp} counter dnat to </color><color #060000/#FFF0F0>$mail_server</color>\\ <tab9><color #000060/#F0F0FF> # udp dport </color><color #060000/#FFF0F0>$vpn_port</color><color #000060/#F0F0FF> counter dnat to 192.168.6.1</color>\\ <tab9><color #000060/#F0F0FF> # dnat tcp dport map @tcp_nat_map </color>\\ <tab9><color #000060/#F0F0FF> # dnat udp dport map @udp_nat_map </color>\\ <tab6><color #000060/#F0F0FF> #}</color>\\ <tab6>\\ <tab6> chain prerouting { ++ | \\ + <color #202000/#F0F0E0> If we decalre postrouting nat we must also decalare prerouting nat, even if we do not need any rules in the prerouting chain.</color>++\\ <tab9> type nat hook prerouting priority 0; policy accept;\\ <tab9><color #000060/#F0F0FF> #iifname </color><color #060000/#FFF0F0>$wan</color><color #000060/#F0F0FF> jump wan_in</color>\\ <tab9><color blue> # dnat - direct allowed by port number wan incoming services to correct lan server ip.</color>\\ <tab9> ip daddr <color red>$wan_ip4</color> tcp dport {http, https} counter dnat to <color red>$http_server</color> ++ | \\ + <color #202000/#F0F0E0> Incoming WAN packets to the http or https ports are preroute dnat to the webserver IP address.</color>++\\ <tab9> ip daddr <color red>$wan_ip4</color> tcp dport { pop3s, imaps, imap2, smtp, submission, submissions } counter dnat to <color red>$mail_server</color> ++ | \\ + <color #202000/#F0F0E0> Incoming WAN packets to the mail ports, pop3s, imaps or smtp ports are preroute dnat to the mail server IP address.</color>++\\ <tab9> ip daddr <color red>$wan_ip4</color> udp dport <color red>$vpn_port</color> counter dnat to <color red>$vpn_ip4</color> ++ | \\ + <color #202000/#F0F0E0> Incoming WAN packets to the VPN port are preroute dnat to the VPN IP address.</color>++\\ <tab6> }\\ <tab6>\\ <tab6> chain postrouting {\\ <tab9> type nat hook postrouting priority 0; policy accept;\\ <tab9> \\ <tab9><color blue> #Allow internal clients to correctly see external address "hairpin dnat"</color> ++ | \\ + <color #202000/#F0F0E0> Hairpin nat is dicussed in greater death at [[https://wiki.kptree.net/doku.php?id=linux_router:nftables#hairpin_nat|hairpin nat]].</color>++\\ <tab9> ip saddr <color red>$lan_ip4</color> ip daddr <color red>$http_server</color> tcp dport { http, https } counter snat <color red>$router_ip4</color>\\ <tab9> ip saddr <color red>$lan_ip4</color> ip daddr <color red>$mail_server</color> tcp dport { http, https, pop3s, imap2, imaps, smtp, submission, submissions } counter snat <color red>$router_ip4</color>\\ <tab9>\\ <tab9><color blue> #Standard postrouting nat</color> ++ | \\ + <color #202000/#F0F0E0> The examples below show different levels of granularity in control.</color>++\\ <tab9><color #000060/#F0F0FF> #oifname </color><color #060000/#FFF0F0>$modem</color><color #000060/#F0F0FF> counter masquerade #needed with dynamic wan ip address</color>\\ <tab9> ip saddr <color red>$lan_ip4</color> ip daddr <color red>$modem_ip4</color> counter snat <color red>$modem_ip</color>\\ <tab9> ip saddr <color red>$vpn_ip</color> ip daddr <color red>$modem_ip4</color> counter snat <color red>$modem_ip</color>\\ <tab9><color #000060/#F0F0FF> #oifname </color><color #060000/#FFF0F0>$wan</color><color #000060/#F0F0FF> counter masquerade #needed with dynamic wan ip address</color>\\ <tab9> ip saddr <color red>$lan_ip4</color> oifname <color red>$wan</color> counter snat <color red>$wan_ip4</color>\\ <tab9> ip saddr <color red>$vpn_ip</color> oifname <color red>$wan</color> counter snat <color red>$wan_ip4</color>\\ <tab9><color #000060/#F0F0FF> #ip saddr { </color><color #060000/#FFF0F0>$lan_ip4, $vpn_ip4 </color><color #000060/#F0F0FF>}oifname </color><color #060000/#FFF0F0>$wan</color><color #000060/#F0F0FF> counter snat </color><color #060000/#FFF0F0>$wan_ip4 #with ip set</color>\\ <tab9><color #000060/#F0F0FF> #oifname { </color><color #060000/#FFF0F0>$wan, $modem</color><color #000060/#F0F0FF> } counter masquerade #with device set</color> \\ <tab9>\\ <tab6> }\\ <tab3> }\\ ++++ ++++mail server ports:| * smtp {25} / (smtps) submissions {465} / submission {587} - (My mail server uses smtp / submission on ports 25 / 587 respectivily) * imap {143} / imaps {993} - (My mail server uses starttls on port 143) * pop3 {110} / pop3s {995} - who still uses pop3? ''nft'' translates ports to service namesas defined in /etc/services ++++ ===Some configuration notes=== Refer to nftables wiki [[https://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation|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 from ''sudo 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: *Serverfault [[https://serverfault.com/questions/205040/accessing-the-dnatted-webserver-from-inside-the-lan|Accessing the DNAT'ted webserver from inside the LAN]] *StackExchange [[https://unix.stackexchange.com/questions/282086/how-does-nat-reflection-nat-loopback-work|How does NAT reflection (NAT loopback) work?]] *Serverfault [[https://serverfault.com/questions/55611/loopback-to-forwarded-public-ip-address-from-local-network-hairpin-nat|Loopback to forwarded Public IP address from local network - Hairpin NAT]] 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's 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 may use 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 and 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 passed 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. \\ \\ <fs:x-large; fw:bold> Diagram 1</fs> \\ <fs:large; fw:bold>DNAT, Simple, Hairpin, not working and Hairpin working</fs> \\ (Where the gateway (router) is on the network WAN device)\\ {{:linux_router:2rxte.png?800|}} ====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 handles *''sudo nft delete rule ip nat postrouting handle 12'' : deletes rule in table ip nat postrouting handle 12 *''sudo nft delete rule inet firewall forward handle 32'' : deletes rule table inet firewall forward handle 32 *''sudo 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 [[https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes|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 from ''sudo 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 [[https://wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Address_Translation_(NAT)|(NAT)]], Ars Technica [[https://arstechnica.com/civis/viewtopic.php?t=678069|SNAT vs. DNAT vs. Masquerading]] and wiki.gentoo.org [[https://wiki.gentoo.org/wiki/Nftables/Examples|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 [[https://github.com/devkid/nftables-systemd|nftables-systemd]] on github.com ---- <- linux_router:netfilter|Prev page ^ linux_router:start|Start page ^ linux_router:nftables_control|Next page ->