Port Forwarding to Reverse Proxy Not Working

I’m a new Mikrotik owner (Routerboard RB3011). I’m trying to set up port forwarding using this guide (https://help.mikrotik.com/docs/display/RKB/Port+forwarding) and am running into a few problems. The first and most obvious is that the forwarded ports seem to work, by and large, when I’m accessing from outside the network (WAN). But when I access the domain name (or public-facing IP directly) from a computer connected to the same subnet as the router, the login page for the Mikrotik is displayed, instead. I expected that it was a problem with the In. Interface list (since the guide has you specify “WAN”), but changing it to “All” or “LAN” didn’t seem to fix the problem.



In a separate (but possibly related issue), port 80 is forwarded to an NGINX reverse proxy listening on port 80 for several different subdomains. When I access (from outside the network) example.com, everything works correctly and the default web page is shown. However, when I access foundry.example.com, the request times out. I haven’t made any changes to the NGINX instance (other than changing the network’s old router out for the Mikrotik) and the underlying services are running fine.

Below is my setup info

# jan/17/2024 14:54:47 by RouterOS 6.49.11
# software id = **ELIDED**
#
# model = RB3011UiAS
# serial number = **ELIDED**
/ip firewall filter
add action=accept chain=input comment=\
"defconf: accept established,related,untracked" connection-state=\
established,related,untracked
add action=drop chain=input src-address=**ELIDED**
add action=drop chain=input comment="defconf: drop invalid" connection-state=\
invalid
add action=accept chain=input comment="defconf: accept ICMP" protocol=icmp
add action=accept chain=input comment=\
"defconf: accept to local loopback (for CAPsMAN)" dst-address=127.0.0.1
add action=drop chain=input comment="defconf: drop all not coming from LAN" \
in-interface-list=!LAN
add action=accept chain=forward comment="defconf: accept in ipsec policy" \
ipsec-policy=in,ipsec
add action=accept chain=forward comment="defconf: accept out ipsec policy" \
ipsec-policy=out,ipsec
add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \
connection-state=established,related
add action=accept chain=forward comment=\
"defconf: accept established,related, untracked" connection-state=\
established,related,untracked
add action=drop chain=forward comment="defconf: drop invalid" connection-state=\
invalid
add action=drop chain=forward comment="defconf: drop all from WAN not DSTNATed" \
connection-nat-state=!dstnat connection-state=new in-interface-list=WAN
/ip firewall nat
add action=masquerade chain=srcnat comment="defconf: masquerade" ipsec-policy=\
out,none out-interface=ether1 out-interface-list=WAN
add action=dst-nat chain=dstnat dst-port=80 in-interface-list=WAN protocol=tcp \
to-addresses=192.168.1.168 to-ports=80
add action=dst-nat chain=dstnat dst-port=32400 in-interface-list=all protocol=\
tcp to-addresses=192.168.1.168 to-ports=32400
add action=dst-nat chain=dstnat dst-port=3074 in-interface=ether1 protocol=tcp \
to-addresses=192.168.1.214
add action=dst-nat chain=dstnat dst-port=88 in-interface=ether1 protocol=udp \
to-addresses=192.168.1.214 to-ports=88
add action=dst-nat chain=dstnat dst-port=3074 in-interface=ether1 protocol=udp \
to-addresses=192.168.1.214 to-ports=3074
add action=dst-nat chain=dstnat dst-port=53 in-interface=ether1 protocol=tcp \
to-addresses=192.168.1.214 to-ports=53
add action=dst-nat chain=dstnat dst-port=500 in-interface=ether1 protocol=udp \
to-addresses=192.168.1.214 to-ports=500
add action=dst-nat chain=dstnat dst-port=3544 in-interface=ether1 protocol=udp \
to-addresses=192.168.1.214 to-ports=3544
add action=dst-nat chain=dstnat dst-port=4500 in-interface=ether1 protocol=udp \
to-addresses=192.168.1.214 to-ports=4500
add action=dst-nat chain=dstnat dst-port=53 in-interface=ether1 protocol=udp \
to-addresses=192.168.1.214 to-ports=53

Any ideas on what I should be checking/changing?

A general remark: you’re using in-interface in most rules, but there are remnants of default config which users in-interface-list. It is easier to make any change in WAN interface (if needed) if firewall rules use in-interface-list because in case of change it’s only necessary to make change in one place (interface list membership), firewall rules are then unchanged. And this rule shows the problem to its fullest:


add action=masquerade chain=srcnat comment=“defconf: masquerade” ipsec-policy=out,none > out-interface=ether1 out-interface-list=WAN

The matching criteria are ANDed together, so all of them have to match. Which means that either rule doesn’t work at all (if ether1 is not member of WAN interface lsit) or one of these properties is redundant (if you’ll keep using in-interface or out-interface instead of lists, then it’s the out-interface-list=WAN which should be removed).


As to the problem: you need to implement hairpin NAT. Without it, in your case, packets from client towards server go via router (who does port forwarding), but return packets go directly (because server sees client’s IP address from same subnet). Return traffic bypassing router means that connection tracking machinery can not properly update connection state and second TCP packet, traveling in direction from client towards server (the final packet of 3-step TCP handshake) will be deemed as invalid by connection tracking machinery and firewall will drop it. If hairpin NAT does its job, then RP will see router’s IP address as clients address, return packet will be sent towards router, router will undo the NAT and update connection tracking table (so it’l see the second packet of the 3-step TCP handshake, a.k.a. SYNACK packet) and everything will be dandy. Except for logs of RP, which won’t show actual clients’ IP addresses.
There are two ways out: 1) use DNS which will serve LAN IPs to LAN clients, so that LAN clients can connect to RP directly (bypassing router entirely, in both directions) or 2) make a proper DMZ for RP meaning that any traffic to/from RP will have to pass router in both directions. In both cases there’s no need for hairpin NAT so in both cases RP will see actual client IP addresses.