I have dual WAN [LTE and ADLS]. The LTE is built into the Mikrotik router in the loft, so its best placed for the cellular aerial. The copper connection is at the bottom of the house and yup there is NO way to physically connect these devices. So a wireless 2.4ghz bridge is used and serves no other purpose. The Mikrotik device is the source of connection and access to everything, the copper router does nothing but route from the bridge there is no direct internal access to it provided.
Further down I have a diagram to show this. But now I will explain the problem. I am hosting a service which I need to access externally and internally via the same URL due to it being SSL secured.
I have seen people who appear [to my limited understanding] to be doing something similar and solving the problem by putting the target server on a different network. I could easily do this by spinning up another VLAN and placing the computer on that, but the target server will soon become physical and then it will not be so easy to do that. Hence this post.

So far I understand that:
- LTE is behind GEO NAT, so there is no way to host my service behind that service period. So I am stuck with the copper WAN.
- The ISP router on the ADSL line isn’t capable of doing HairPin NAT - (perhaps this is my only way out of this, but that’s for later).
- Currently external access works perfectly but internally we are broken since moving onto SSL recently which was expected.
The scope of this post is to explore whether HairPin Nat can even be employed given the setup we are using. If not what are the options.
The router is VERY carefully tuned to prevent excessive CPU utilisation as there are multiple internal networks connected through it as the diagram shows. Wide open masquerade rules [for instance] will not work since that will catch traffic unnecessarily and spike the CPU. Specifically there are 60 or so Virtual machines which are spread over the VLAN’s so they use the router to traverse between these networks.
Thanks for any insights.
First, if you control the DNS resolver used by most client devices in your network (you announce the DNS server via DHCP and the DNS server is hosted internally), then you should make that DNS server rewriting the DNS record of the domain to the local IP address of the service’s host. With that most of the devices in your network when accessing the service by URL will resolve to the local IP address and use that IP address to connect to the host directly, no NAT involved (and no hairpin NAT is required). That results in the best performance.
Of course, you’ll still need the existing DNSNAT configuration for devices connecting from the outside (from the internet).
Besides that, there might be devices in your network that ignore the internal DNS resolver (maybe they have DoH setup in the browser, or DoT setup as “Privacy” DNS). As fallback for those particular client devices, you’ll need to setup hairpin NAT (https://help.mikrotik.com/docs/spaces/ROS/pages/3211299/NAT#NAT-HairpinNAT) in the form of a masquerade rule. Follow the example from the doc and limit the rule with dst-address= src-address= out-interface= and the rule will only match in cases where it’s really needs to.
For the time the server and client PC infrastructure is “up” there is a DNS service, which serves them. But, they are not up all the time, due energy cost.
It is specifically the devices, the iPad’s phones etc which are on all the time and need to be accommodated that prompted my post. As I say, external access works perfectly at the moment, but the existing NAT rule will not work for hairpin NAT since it requires the source to be from ether5 and internal clients will arrive on ether1,2,3 or wlan1,2.
/ip firewall nat
add action=masquerade chain=srcnat comment="defconf: masquerade" ipsec-policy=out,none out-interface-list=WAN
add action=dst-nat chain=dstnat comment="Nat for my server" dst-port=5467 in-interface=ether5 protocol=tcp to-addresses=192.168.1.9 to-ports=5467
My question is [and I have tried already and failed] how do I set up a rule which accurately hairpins traffic back inside the router and also forwards traffic from the internet arriving via ether5? Perhaps I need more than one NAT rule.
Follow the example from the doc and limit the rule with dst-address= src-address= out-interface= and the rule will only match in cases where it’s really needs to.
The problem with this approach, and again this might be my lack of comprehension is that clients coming internally to access the service in question will be presenting a destination IP address of the WAN IP of the ADSL router; since this is what they will have found in public DNS. But clients arriving from the internet will arriving nated by the ADSL router and presenting a source IP of 192.168.254.254:5469. The existing rule looks at the target port and source interface to do it’s work. I am not sure how or if these two conditions can be accommodated in one rule?
I am not so familiar with this stuff, which is why I am posting here.
For hairpin NAT, your DSTNAT rule cannot use in-interface=ether5 anymore. You will need to replace this rule
/ip firewall nat
add action=dst-nat chain=dstnat comment="Nat for my server" dst-port=5467 in-interface=ether5 protocol=tcp to-addresses=192.168.1.9 to-ports=5467
with
/ip firewall nat
add action=dst-nat chain=dstnat comment="Nat for my server" dst-port=5467 dst-address-list=WAN_IP protocol=tcp to-addresses=192.168.1.9 to-ports=5467
As for WAN_IP, if on the router the domain (that you use in the URL to access the service) is resolved to the public IP address of the ADSL router, then you can create the address list like this:
/ip firewall address-list
add address=the.domain.name list=WAN_IP
You can then verify that a dynamic address list entry is also created for WAN_IP with the correct IP address.
Then another source NAT rule is needed for hairpin NAT (replace bridge with the bridge or vlan interface that has the subnet 192.168.1.0/24):
/ip firewall nat
add action=masquerade chain=srcnat comment="Hairpin NAT" src-address=192.168.1.0/24 dst-address=192.168.1.9 out-interface=bridge
Still, if possible, try to make most of the LAN devices have the.domain.name resolved to 192.168.1.9 for best performance, by having the local DNS server modifying the record for the.domain.name accordingly. BUT in that case the router still has to have the.domain.name resolved to the correct public IP address of the ADSL router, so that the dynamic address list entry above is still correct. Which means the router should use a separate DNS server as upstream DNS resolver, not the one that changes the record of the.domain.name.
Ok, I can make sense of this.
I’ll test it and report.
Many thanks.
So I have tested and some parts work and others are not ticking up bytes and packets when tested so, not hitting for some reason.
-
I left the rule for the external clients in place and moved it to the bottom of the Nat rule list, since all traffic for that rule will have an input interface of ether5 and no internal clients can match that. So it is effectively out of the picture. This rule still works fine.
-
The rule which catches internal clients trying to get to my ADSL WAN IP is working in terms of I see bytes and packets increasing when testing it. But I had a thought which was that https://myserver.com will by default connect to port 443, but my target service is on 5467. So the rule ended up with a source port of 443 and a target port of 5467. This rule is at position 2 in the Nat rule list.
-
The next rule at position 1 is the default internet masquerade rule.
This rule at position 0, is not being hit at all, I can’ really see why.
/ip firewall nat
add action=masquerade chain=srcnat comment="HairPin Nat" dst-address=192.168.1.9 out-interface-list=LAN src-address=192.168.1.0/24
To try to get more sense, I adjusted rule 2 to take 5467 and pass it to 5467.
Then I tested without the port in the browser URL so it would default to 443 and got “error 403” as expected.
But when I add :5467 it just times out and I see the packet counter increase on rule 2 but not on rule 0 so I guess this is why.
Finally, on rule 0 I tried to add the Out-Interface as ether1 - but since that interface matches the Src.Address in the rule the router would not accept that as valid . . .
It is such a shame that the router does not have a limited dns server capability.
FIXED!
I was thinking of another time when I had to do something like this for a split DNS solution and I re-used parts of that idea.
What I did is create a new mangle rule for prerouting.
- I look for packets coming from “connected-subnets”
- “connected-subnets” is an address list which matches all my internal network addresses.
- Match the destination IP address == WAN-IP which is another address list which resolves to the ip address assigned to my ADSL-WAN interface.
For connections state NEW only I mark the connection with a new connection mark “some-mark” and allow it to passthrough.
Then I pick that connection up in the masquerade NAT rule which was not firing before and that does now work and I can see the frames ticking up.
Here are these rules for clarity:
/ip firewall mangle
add action=mark-connection chain=prerouting comment="Server Marking" connection-state=new dst-address-list=ADSL-IP new-connection-mark=test-mark src-address-list=connected-subnets
/ip firewall address-list
add address=192.168.1.0/24 list=connected-subnets
add address=192.169.1.0/24 list=connected-subnets
add address=192.170.1.0/24 list=connected-subnets
add address=192.171.1.0/24 list=connected-subnets
add address=192.172.1.0/24 list=connected-subnets
add address=192.173.1.0/24 list=connected-subnets
add address=somewhere.com list=ADSL-IP
/ip firewall nat
add action=masquerade chain=srcnat comment="HairPin Nat" connection-mark=test-mark dst-address=192.168.1.9 out-interface-list=LAN src-address=192.168.1.0/24
add action=masquerade chain=srcnat comment="defconf: masquerade" ipsec-policy=out,none out-interface-list=WAN
add action=dst-nat chain=dstnat comment="Hairpin Rule 1" dst-address-list=ADSL-IP dst-port=443 protocol=tcp \
to-addresses=192.168.1.9 to-ports=5467
add action=dst-nat chain=dstnat comment="External Nat for sever1" dst-port=5467 in-interface=ether5 protocol=tcp to-addresses=\
192.168.1.9 to-ports=5467
Many thanks for assistance - I believe this solution will work and will not overload the CPU on the router.
That’s interesting, because
/ip firewall nat
add action=masquerade chain=srcnat comment="HairPin Nat" connection-mark=test-mark dst-address=192.168.1.9 out-interface-list=LAN src-address=192.168.1.0/24
Should actually not be able to catch anything that
/ip firewall nat
add action=masquerade chain=srcnat comment="HairPin Nat" dst-address=192.168.1.9 out-interface-list=LAN src-address=192.168.1.0/24
could not catch (we are adding one more condition).
Agreed, however in practice it does not catch. Hence the additional logic.