Hairpin NAT not working

Hi,

i am using Traefik as my reverse proxy. I wanted to set up remote for acces my git and i have done it (more or less) but its only working from outside of my network. I found that solution should be Loopback NAT and tried to use it but no succes. I tried to do it simply for whole network but nothing. Any ideas?

1    chain=srcnat action=masquerade src-address=10.10.42.0/24 dst-address=10.10.42.0/24 log=no log-prefix=""

and just to be complete here are all my NAT rules to see if there is no conflict

 0    chain=srcnat action=masquerade out-interface=ether5[internet] log=no log-prefix="" 

 1    chain=srcnat action=masquerade src-address=10.10.42.0/24 dst-address=10.10.42.69 log=no log-prefix="" 

 2    ;;; traefik-proxy
      chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=80 protocol=tcp src-address=!10.10.42.69 
      in-interface-list=WAN dst-port=80 log=no log-prefix="" 

 3    ;;; traefik-proxy
      chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=443 protocol=tcp src-address=!10.10.42.69 
      in-interface-list=WAN dst-port=443 log=no log-prefix="" 

 4    ;;; traefik-proxy
      chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=8080 protocol=tcp src-address=!10.10.42.69 
      in-interface-list=WAN dst-port=8080 log=no log-prefix="" 

 5    ;;; traefik-proxy
      chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=8080 protocol=udp src-address=!10.10.42.69 
      in-interface-list=WAN dst-port=8080 log=no log-prefix="" 

 6    ;;; traefik-proxy
      chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=443 protocol=udp src-address=!10.10.42.69 
      in-interface-list=WAN dst-port=443 log=no log-prefix="" 

 7    ;;; traefik-proxy
      chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=80 protocol=udp src-address=!10.10.42.69 
      in-interface-list=WAN dst-port=80 log=no log-prefix="" 

 8    ;;; traefik-ssh-git
      chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=2224 protocol=tcp src-address=!10.10.42.69 
      in-interface-list=WAN dst-port=2224 log=yes log-prefix="ssh_nat" 

 9    ;;; traefik-ssh-git
      chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=2224 protocol=udp src-address=!10.10.42.69 
      in-interface-list=WAN dst-port=2224 log=yes log-prefix="ssh_nat"

Dont be too lazy,
Select the search in the upper right, type in hairpin nat.

TBH I’ve never seen hairpin NAT with masquerade, only with action=src-nat
But yeah, easily googleable

To be clear, thats a nonsensical statement.
The chain is srcnat the action is masquerade for the ‘normal’ hairpin nat rule

add chain=srcnat action=masquerade dst-address=subnetofServer src-address=subnetofServer

You don’t have any dst-nat rule which would act on traffic towards traefik which is originating from LAN. All your dst-nat rules include in-interface-list=WAN and depending on configuration not shown here router’s LAN interface is not member of WAN interface list … or so I hope.

And, BTW, I highly doubt you need all those protocol=udp dst-nat rules.

Well that escalated quickly… I did my research reading through a few posts and trying everything i found sensible to do, watched and followed the tutorial by Mikrotik on YT but nothing worked. So i thought that i will give up, but then idea came to mind. Comunity! They were helpfull before. Iam guilty of doing the mystake and buying the Mikrotik router as a hobbyist that want to learn some more in his homelab but iam not lazy to search for an answer before i post. Ok jokes aside. Thanks for the reaction anyway:)

So that means i should just remove from the rule in-interface completely so it doesnt make a difference if the trafic comes from lan or wan?

PS: Thanks for the tip i did disabled those UDP rules and it seem to make no difference.

Edit: I tried it and removed in interface completely from all the rules and ssh redirect started to working. But another problem appeared. SSL certificates for all websites (excluded my sites behind traefik) are giving errors now that the site is not safe…?

Yes. But you should replace it with some other matcher (as you already discovered). Let’s dissect one of DST-NAT rules (they all are pretty similar, so they are plagued with same problem), for example this one:

chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=80 protocol=tcp src-address=!10.10.42.69
in-interface-list=WAN dst-port=80 log=no log-prefix=“”

What it says:

  1. does the packet match protocol=tcp ?
  2. does this packet match src-address=!10.10.42.69 ?
  3. does this packet match in-interface-list=WAN ?
  4. does this packet match dst-port=80 ?
  5. if all the above check, then rewrite packet header values: dst-address with value of to-addresses (10.10.42.69) and dst-port with value of to-ports (80)

(a side note: if service port on LAN server is the same as public port (as it is in this case), then it’s possible to omit setting to-ports property … and NAT machinery won’t rewrite this header field and thus save a CPU cycle or two)

Now, the item #3 used to prevent your LAN hosts to work with LAN server when you tried to use WAN IP address … even after you installed appropriate SRC-NAT rule (the material part of hairpin NAT).

But what happens after you remove this matcher? Consider a packet targeting e.g. http://google.com/ (the plain HTTP protocol, which uses port 80)? Yup, DST-NAT rule matches and redirects the connection towards your LAN server. Which might even work for non-encrypted traffic if your LAN server runs some transparent proxy software (Traefik can be used as one). But doesn’t work with encrypted services (such as https or ssh) as servers use server-side certificates or keys and client can (in case of certificates which include “Subject Name” and “Subject Alt Names”) detect that there’s a MITM … and thus refuse to talk to the connection peer.

So … you have to add a matcher (instead of item #3 on the list above) which will restrict DST-NAT rule to only trigger on packets, destined to one of router’s addresses. The most straight-forward is this one

dst-address=<WAN IP address>

but this will only work in mid/long-term if your WAN IP address is truly static.

Another possibility would be to use

dst-address-type=local

which will match all packets targeting any of router’s addresses, also router’s LAN address. This may interfere with services, used for router management (WebFix, ssh) … you can move those services to non-standard ports to avoid this problem though.
If this is not an option and your WAN IP address is delivered to your router via DHCP client, then you could use the first option (with dst-address=) and use a DHCP client script to change the matcher on all relevant DST-NAT rules. Or, perhaps even better, create an address “list” (named e.g. WAN-IP), add WAN IP address to it and use DST-NAT rule matcher dst-address-list=WAN-IP. The role of DHCP client script would then be to remove old WAN IP address from WAN-IP list and add the new one.

Thanks for the explaination. It is for me a bit verbose so i had to find time to try to understand it. My understanding is that almost all rules i have are superfluous. I the end i need the masquerade if src and dst addresses are on my network (that is the Hairpin NAT rule right?). And second rule that has dst-addrss=“public IP” of my router that will have action dst-nat to IP of my Traefik and thats it because i use Traefik to do the rest in my home network. Is that right or did i missinterpreted something?
So in the end i will reduce it from those nine rules i started this post with

 0    chain=srcnat action=masquerade out-interface=ether5[internet] log=no log-prefix="" 
 1    chain=srcnat action=masquerade src-address=10.10.42.0/24 dst-address=10.10.42.0/24 log=no log-prefix="" 
 2    chain=dstnat action=dst-nat to-addresses=10.10.42.69 dst-address=CENSORED;) log=no log-prefix=""

Edit: Doesnt work so i obviously did not understand something

Of course its not going to work, what did you forward to the LAN server (answer → NOTHING ) no ports/protocol is delineated!!

Then:
0 chain=srcnat action=masquerade out-interface=ether5[internet] log=no log-prefix=“”
1 chain=srcnat action=masquerade src-address=10.10.42.0/24 dst-address=10.10.42.0/24 log=no log-prefix=“”
2 chain=dstnat action=dst-nat to-addresses=10.10.42.69 dst-address=CENSORED;) log=no log-prefix=“” WHERE IS DST PORT protocol ???

(a side note: if service port on LAN server is the same as public port (as it is in this case), then it’s possible to omit setting to-ports property … and NAT machinery won’t rewrite this header field and thus save a CPU cycle or two)



2 chain=dstnat action=dst-nat to-addresses=10.10.42.69 dst-address=CENSORED;) log=no log-prefix=""WHERE IS DST PORT protocol ???

As i understood if i go to http://publicIP.xxx that will have domain record with my public ip it will go to my router where it sees that dst-address was my public ip and dst nated trafic to address 10.10.42.69 where Traefik receives it with the port that doesnt have to be defined because it doesnt change - 80 in this case and will then redirect me to the destination serivce in my lan that i configured in Traefik.
Same for the ssh if i ssh user@http://publicIP.xxx -p 666 it will follow the same path if i have port 666 on traefik opened and ready to redirect me to destination ssh where the port is still the same - 666

Follow the bouncing ball…
https://gregsowell.com/?p=4242

In a nutshell, when the router attempts to send the response from the local LAN member, without the sourcenat rule in place, the router will try to shortcut the response directly from the server to the LAN user ( as if the LAN user used the actual LANIP of the server vice the Public IP ). The LAN user device, sees a response coming from another local LAN address where it didnt send any information/request to, and thus the the LAN user device drops the return traffic like a hot potatoe.

Thanks for your guidance. It works now.

 0    chain=srcnat action=masquerade out-interface=ether5[internet] log=no log-prefix="" 
 1    chain=srcnat action=masquerade src-address=10.10.42.0/24 dst-address=10.10.42.69 out-interface=local log=no log-prefix="" 
 2    chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=443 protocol=tcp dst-address=publicIP dst-port=443 
      log=no log-prefix="" 
 3    chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=80 protocol=tcp dst-address=publicIP dst-port=80 log=no 
      log-prefix="" 
 4    chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=2224 protocol=tcp dst-address=publicIP dst-port=2224 
      log=no log-prefix="" 
 5    chain=dstnat action=dst-nat to-addresses=10.10.42.69 to-ports=8080 protocol=tcp dst-address=publicIP dst-port=8080 
      log=no log-prefix=""