{{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: -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT *The iptables rules can be flushed with ''sudo iptables -F''. ====Sample NFTables configuration==== ''sudo vim /etc/nftables.conf'' ++++ Router NFtable Setup - ''/etc/nftables.conf''| #!/usr/sbin/nft -f ++ | + nftables scripts need this shebang with the -f option to run++ \\ \\ # script variable definitions ++ | \\ + * 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. ++ \\ define wan = eno1 #definition of wan device label, setup as ppp1 for pppoe\\ define modem = eno1 #definition of modem device label\\ define lan = br0 #definition of lan device label\\ define lan_ip4 = 192.168.1.0/24 #definition of lan IPV4 range\\ define router_ip4 = 192.168.1.1 #definition of router IPv4 address\\ define modem_ip = 192.168.5.2 #definition of modem IPv4 address ++ | \\ + 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]].++\\ define modem_ip4 = 192.168.5.0/24 #definition of modem IPv4 address range ++ | \\ + 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]].++\\ define http_server = 192.168.1.12 #definition of http server IPv4 address\\ define mail_server = 192.168.1.18 #definition of mail server IPv4 address\\ define wan_ip4 = 112.213.222.38 #definition of wan IPv4 address\\ define vpn = wg0 #definition of wireguard vpn device label\\ define vpn_ip = 192.168.6.0/24 #definition of wireguard IPv4 address range\\ define vpn_ip4 = 192.168.6.1 #definition of wireguard IPv4 server address\\ define vpn_port = 51914 #definition of wireguard server port\\ #define he_tunnel = he-ipv6 #\\ \\ # Clean out the current ruleset\\ flush ruleset ++ | \\ +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 [[https://www.netfilter.org/projects/nftables/manpage.html#lbAO|Ruleset]] for more information.++ \\ \\ table inet firewall { ++ | \\ +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 [[https://www.netfilter.org/projects/nftables/manpage.html#lbAP|Tables]] for more information. ++ \\ \\ # The set controllist is used to dynamically add IP address(es) to a timed drop list for LAN access control ++ | \\ + This has been separately described in detail at the following link: [[linux_router:nftables_control]], also see wiki.nftables on [[https://wiki.nftables.org/wiki-nftables/index.php/Sets#Named_sets| Names Sets]] & nft man page [[https://www.netfilter.org/projects/nftables/manpage.html#lbAS|Sets]]. ++ \\ set controllist {\\ type ipv4_addr\\ flags interval\\ flags timeout\\ }\\ \\ # Remember that the input and output chains only refer to this machine.\\ # The forward and nat chains refer to routing.\\ # open ports refers to ports to open for input to this machine.\\ # ssh is port 22 for SSH on both TCP and UDP, but we only use TCP!\\ # bootps, port 67 and bootpc, port 68 are for DHCP on both TCP and UDP, but we only use UDP!\\ # domain is port 53 for DNS requests on TCP & UDP.\\ # openvpn is port 1194 on both TCP and UDP, but we will not be using this antiquated protocol.\\ \\ set tcp_open_ports { ++ | \\ + 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 [[https://wiki.nftables.org/wiki-nftables/index.php/Sets#Named_sets| Names Sets]] & nft man page [[https://www.netfilter.org/projects/nftables/manpage.html#lbAS|Sets]]. ++ \\ type inet_service; flags interval;\\ elements = {\\ ssh,\\ domain\\ }\\ }\\ \\ set udp_open_ports { ++ | \\ + 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 [[https://wiki.nftables.org/wiki-nftables/index.php/Sets#Named_sets| Names Sets]] & nft man page [[https://www.netfilter.org/projects/nftables/manpage.html#lbAS|Sets]]. ++ \\ type inet_service; flags interval;\\ elements = {\\ domain,\\ $vpn_port, # Wireguard port\\ 60000-61000 # mosh port interval\\ }\\ }\\ \\ chain tcp_cek { ++ | \\ + 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 [[https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains| Configuring Chains]] & nft man page [[https://www.netfilter.org/projects/nftables/manpage.html#lbAQ|Chains]]for more information++ \\ # bad tcp -> avoid network scanning: \\ tcp flags & (fin|syn) == (fin|syn) counter drop\\ tcp flags & (syn|rst) == (syn|rst) counter drop\\ tcp flags & (fin|syn|rst|psh|ack|urg) < (fin) counter drop\\ tcp flags & (fin|syn|rst|psh|ack|urg) == (fin|psh|urg) counter drop\\ } \\ \\ chain icmp_cek {++ | \\ + 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 [[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.++ \\ # no ping floods\\ ip protocol icmp counter\\ ip protocol icmp limit rate over 2/second burst 5 packets counter drop ++ | + This limits the incoming rate of icmp packets handled ++\\ ip6 nexthdr ipv6-icmp counter\\ ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 2/second burst 5 packets counter drop ++ | + This limits the incoming rate of icmpv6 packets handled ++\\ \\ # allow icmp \\ ip protocol icmp icmp type { ++ | + This allows the icmp packets to be handled ++\\ echo-request, echo-reply, time-exceeded,\\ parameter-problem, destination-unreachable\\ } counter accept\\ \\ ip6 nexthdr icmpv6 icmpv6 type { ++ | + This allows the icmpv6 packets to be handled ++ \\ echo-request, echo-reply, time-exceeded,\\ parameter-problem, destination-unreachable,\\ packet-too-big, nd-router-advert, nd-router-solicit,\\ nd-neighbor-solicit, nd-neighbor-advert,\\ mld-listener-query\\ } counter accept\\ }\\ \\ chain input {++ | \\ + See wiki.nftables [[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.++ \\ type filter hook input priority 0; policy drop; ++ | \\ + 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 [[https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains| Configuring Chains]] for more information.++ \\ \\ # drop connections from blacklisted addresses \\ # ip saddr @blacklist counter drop \\ \\ # Cek valid tcp packet\\ ip protocol tcp jump tcp_cek ++ | + We use the jump statement as we want to return and continue to process chains after this statement.++\\ \\ # Cek valid icmp communications\\ jump icmp_cek ++ | + We use the jump statement as we want to return and continue to process chains after this statement.++\\ \\ #count all the wan SSH attemps\\ #iifname $wan tcp dport ssh counter \\ \\ # avoid brute force on ssh: (if using external ssh) \\ #tcp dport ssh limit rate over 8/minute counter drop ++ | \\ + 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.)++ \\ \\ # In general input filter drops and limits list should be placed above here here ++ | \\ + Position of rules is important in chains. Once a chain rule accepts, drops or rejects a packet subsequent chain rules are no longer processed!)++ \\ \\ # accept established connections ++ | \\ + 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. ++ \\ ct state established, related counter accept \\ \\ # drop invalid state packets ++ | \\ + 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.++ \\ ct state invalid counter drop\\ \\ # accept input from loopback interface ++ | \\ + It is generally accepted practice to accept all packets from the loopback interface.++ \\ iif lo counter accept\\ \\ # accept input from LAN ++ | \\ + 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.++ \\ iifname $lan counter accept # By port is a pain in the arse!\\ # iifname $lan tcp dport { ssh, domain, 667, 953 } counter accept ++ | + This is used to accept specific LAN tcp ports, required if not accepting all.++ \\ # iifname $lan udp dport { domain, bootps, bootpc, 667, 953} counter accept ++ | + This is used to accept specific LAN tcp ports, required if not accepting all.++ \\ \\ # accept input from VPN ++ | \\ + 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.++ \\ iifname $vpn counter accept # By port is a pain in the arse!\\ ip daddr $vpn_ip4 udp dport $vpn_port counter accept ++ | \\ + 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.++ \\ # iifname $wan udp dport $vpn_port ct state new counter accept\\ \\ # everything else reject port unreachable \\ ct state new counter reject ++ | \\ + 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 [[https://wiki.nftables.org/wiki-nftables/index.php/Rejecting_traffic| Rejecting traffic]] for more information. ++\\ }\\ \\ chain forward {++ | \\ + See wiki.nftables [[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.++ \\ type filter hook forward priority 0; policy accept; ++ | \\ + 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 [[https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains| Configuring Chains]] for more information.++ \\ \\ #drop all IP addresses in the control list both source and destination ++ | \\ + This has been separately described in detail at the following link: [[linux_router:nftables_control]], also see wiki.nftables on [[https://wiki.nftables.org/wiki-nftables/index.php/Sets#Named_sets| Names Sets]] & nft man page [[https://www.netfilter.org/projects/nftables/manpage.html#lbAS|Sets]]. ++ \\ ip daddr @controllist counter drop\\ ip saddr @controllist counter drop\\ \\ #limit the IP address range bandwidth using WAN ++ | \\ + 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]] ++ \\ iifname $wan ip daddr { 192.168.1.100-192.168.1.200 } limit rate over 1300kbytes/second burst 3000kbytes counter drop\\ oifname $wan ip saddr { 192.168.1.100-192.168.1.200 } limit rate over 750kbytes/second burst 1200kbytes counter drop\\ \\ oifname $wan tcp flags syn tcp option maxseg size set rt mtu # This automatically set the tcp mtu\\ #oifname $wan tcp flags syn tcp option maxseg size set 1452 counter # This set the tcp mtu directly to a maximum value\\ \\ # In general forward filter drops and limits list should be placed above here here ++ | \\ + Position of rules is important in chains. Once a chain rule accepts, drops or rejects a packet subsequent chain rules are no longer processed!)++ \\ \\ # accept established connections ++ | \\ + 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. ++ \\ ct state established, related counter accept\\ \\ # drop invalid state packets ++ | \\ + 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.++ \\ ct state invalid counter drop\\ \\ #All traffic from lan is accepted\\ iifname $lan counter accept # accept anything from the LAN, keep it simple.\\ \\ # Allow traffic coming from the vpn \\ iifname $vpn counter accept # accept anything from the VPN, keep it simple.\\ \\ # Allow new connections to internal servers, but only those that have had dnat performed. \\ # The server names can be used where host DNS resolves to IP address\\ ip daddr $http_server ct status dnat counter accept ++ | \\ + The incoming WAN packets that have been preroute dnat to the http server IP address to be accepted.++ \\ ip daddr $mail_server ct status dnat counter accept ++ | \\ + The incoming WAN packets that have been preroute dnat to the mail server IP address to be accepted.++ \\ #ip daddr $vpn_ip4 ct status dnat counter accept # required if VPN service is not on main router IP\\ # other option is perhaps more verbose, e.g.\\ #ip daddr $http_server tcp dport {http, https} counter accept\\ #ip daddr $mail_server tcp dport {http, https, pop3s, imaps, smtp} counter accept\\ #ip daddr $vpn_ip4 udp dport $vpn_port counter accept\\ \\ #Hurricane Electric IPv6 tunnel\\ #iifname $he_tunnel counter jump base_checks # not required as we do this for all connections above\\ #iifname $lan oifname $he_tunnel counter accept\\ \\ # drop and count everything else not accounted for,\\ # probably does not have much use on forward\\ counter drop\\ }\\ \\ chain output {\\ # We allow everything out \\ type filter hook output priority 0; policy accept;\\ \\ #counters for different devices, ip address, port, etc. place first\\ ip saddr $vpn_ip4 counter\\ \\ # In general output filter drops and limits list should be placed above here here ++ | \\ + Position of rules is important in chains. Once a chain rule accepts, drops or rejects a packet subsequent chain rules are no longer processed!)++ \\ \\ # Allow packets with existing state & count\\ ct state established,related counter accept\\ \\ # Allow all new packets on Router output & count\\ ct state new counter accept\\ \\ # Drop all invalid packet and count\\ ct state invalid counter drop\\ }\\ }\\ \\ table ip nat {\\ \\ #map tcp_nat_map { \\ # type inet_service : ipv4_addr \\ # elements = { http : 192.168.1.16, https : 192.168.1.16} \\ #} \\ \\ #map udp_nat_map { \\ # type inet_service : ipv4_addr \\ #} \\ \\ #chain wan_in { \\ # tcp dport { http, https} counter dnat to $http_server\\ # tcp dport { pop3s, imaps, smtp} counter dnat to $mail_server\\ # udp dport $vpn_port counter dnat to 192.168.6.1\\ # dnat tcp dport map @tcp_nat_map \\ # dnat udp dport map @udp_nat_map \\ #}\\ \\ chain prerouting { ++ | \\ + If we decalre postrouting nat we must also decalare prerouting nat, even if we do not need any rules in the prerouting chain.++\\ type nat hook prerouting priority 0; policy accept;\\ #iifname $wan jump wan_in\\ # dnat - direct allowed by port number wan incoming services to correct lan server ip.\\ ip daddr $wan_ip4 tcp dport {http, https} counter dnat to $http_server ++ | \\ + Incoming WAN packets to the http or https ports are preroute dnat to the webserver IP address.++\\ ip daddr $wan_ip4 tcp dport { pop3s, imaps, imap2, smtp, submission, submissions } counter dnat to $mail_server ++ | \\ + Incoming WAN packets to the mail ports, pop3s, imaps or smtp ports are preroute dnat to the mail server IP address.++\\ ip daddr $wan_ip4 udp dport $vpn_port counter dnat to $vpn_ip4 ++ | \\ + Incoming WAN packets to the VPN port are preroute dnat to the VPN IP address.++\\ }\\ \\ chain postrouting {\\ type nat hook postrouting priority 0; policy accept;\\ \\ #Allow internal clients to correctly see external address "hairpin dnat" ++ | \\ + Hairpin nat is dicussed in greater death at [[https://wiki.kptree.net/doku.php?id=linux_router:nftables#hairpin_nat|hairpin nat]].++\\ ip saddr $lan_ip4 ip daddr $http_server tcp dport { http, https } counter snat $router_ip4\\ ip saddr $lan_ip4 ip daddr $mail_server tcp dport { http, https, pop3s, imap2, imaps, smtp, submission, submissions } counter snat $router_ip4\\ \\ #Standard postrouting nat ++ | \\ + The examples below show different levels of granularity in control.++\\ #oifname $modem counter masquerade #needed with dynamic wan ip address\\ ip saddr $lan_ip4 ip daddr $modem_ip4 counter snat $modem_ip\\ ip saddr $vpn_ip ip daddr $modem_ip4 counter snat $modem_ip\\ #oifname $wan counter masquerade #needed with dynamic wan ip address\\ ip saddr $lan_ip4 oifname $wan counter snat $wan_ip4\\ ip saddr $vpn_ip oifname $wan counter snat $wan_ip4\\ #ip saddr { $lan_ip4, $vpn_ip4 }oifname $wan counter snat $wan_ip4 #with ip set\\ #oifname { $wan, $modem } counter masquerade #with device set \\ \\ }\\ }\\ ++++ ++++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. \\ \\ Diagram 1 \\ DNAT, Simple, Hairpin, not working and Hairpin working \\ (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 ->