NAT to a local server

Hi all,

I’m just setting up a development server at home. I’m using Apache, and for starters I want to access its welcome page from internet.

My network diagram looks like this:
diagram.jpg
— An ONT router set as a bridge.
— The routing is done via a Mikrotik hAP ac². The web interface is accesible from internet via its public IP,193.248.32.7 —not the real one, of course—.
— Two clients with dynamic IPs
— One local server —with firewall disabled for testing purposes— and this IP settings:

    network:
      ethernets:
        eno1:
          dhcp4: no
          addresses: [192.168.88.246/24]
          gateway4: 192.168.88.1
          nameservers:
          addresses: [8.8.8.8,8.8.4.4]

Now, I enter my local server within my local network with ssh:

    $ ssh myuser@192.168.88.246
    $ curl http:localhost:80
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    etc.

Success, it returns the whole HTML of the Apache welcome page

Now I want to do the same thing from outside my network, so I set up a NAT in the Router for any ethernet interface and port 4000, to 192.168.88.246 and port 80

    /ip firewall nat add chain=dstnat action=dst-nat protocol=tcp in-interface=all-ethernet dst-port=4000 to-addresses=192.168.88.246 to-ports=80 comment="NAT HTTP"

Then I try to retrieve the Apache welcome page:

    \$ curl 193.248.32.7:4000
    curl: (7) Failed to connect to 193.248.32.7 port 4000: Connection refused

As you see the call is not reaching the server. If I use any service as https://www.yougetsignal.com/tools/open-ports I can see that the port 193.248.32.7:4000 is open. But with Telnet is not that evident:
Pointing to the router IP reaches the router itself, asking for password:

    \$ telnet 193.248.32.7
    Connected to 193.248.32.7
    Escape character is '^]'.
    Password:

But if I point to the port 4000 it fails:

    \$ telnet 193.248.32.7 4000
    Trying 193.248.32.7...
    telnet: connect to address 193.248.32.7: Connection refused
    telnet: Unable to connect to remote host

I have been trying to set this up for a while, any idea will be welcome!

/ip firewall nat add chain=dstnat action=dst-nat protocol=tcp in-interface=WAN-INTERFACE dst-address=WAN-IP dst-port=4000 to-addresses=192.168.88.246 to-ports=80 comment="NAT HTTP"

The point is, as @baragoon hinted, that from the point of view of the IP stack, in-interface of packets coming from your hAP from the internet is none of the etherX (all-ethernet) but pppoe-out1 or how have you named the /interface pppoe-client you probably use as uplink.

But it’s all guessing as you haven’t posted the actual configuration of the hAP. See my automatic signature below for a hint.

Thanks @baragoon and @sindy.

I didn’t set any pppoe-client:

 /interface pppoe-client print
Flags: X - disabled, I - invalid, R - running

Here is the full configuration of the router (as before, 193.248.32.7 is not the authentic IP):

# jul/30/2019 xx:xx:xx by RouterOS 6.45.2
# software id = MC01-GCC9
#
# model = RBD52G-5HacD2HnD
# serial number = XXXXXXXX
/interface bridge
add admin-mac=xx:xx:xx:xx:xx:xx auto-mac=no comment=defconf name=bridge
/interface wireless
set [ find default-name=wlan1 ] band=2ghz-b/g/n channel-width=20/40mhz-XX distance=indoors frequency=auto mode=ap-bridge ssid=MikroTik-602B32 \
    wireless-protocol=802.11
/interface list
add comment=defconf name=WAN
add comment=defconf name=LAN
/interface wireless security-profiles
set [ find default=yes ] supplicant-identity=MikroTik
add authentication-types=wpa2-psk eap-methods="" management-protection=allowed mode=dynamic-keys name=Security_WiFi supplicant-identity=""
/interface wireless
set [ find default-name=wlan2 ] band=5ghz-a/n/ac channel-width=20/40/80mhz-XXXX default-authentication=no disabled=no distance=indoors frequency=\
    auto mode=ap-bridge security-profile=Security_WiFi ssid=h73_29gqXk wireless-protocol=802.11
/ip hotspot profile
set [ find default=yes ] html-directory=flash/hotspot
/ip pool
add name=default-dhcp ranges=192.168.88.10-192.168.88.254
/ip dhcp-server
add address-pool=default-dhcp disabled=no interface=bridge name=defconf
/interface bridge port
add bridge=bridge comment=defconf interface=ether2
add bridge=bridge comment=defconf interface=ether3
add bridge=bridge comment=defconf interface=ether4
add bridge=bridge comment=defconf interface=ether5
add bridge=bridge comment=defconf interface=wlan1
add bridge=bridge comment=defconf interface=wlan2
/ip neighbor discovery-settings
set discover-interface-list=LAN
/interface list member
add comment=defconf interface=bridge list=LAN
add comment=defconf interface=ether1 list=WAN
/interface wireless access-list
add comment=xxxxxxxxx mac-address=xx:xx:xx:xx:xx:xx vlan-mode=no-tag
add comment=xxxxxxxxx interface=wlan2 mac-address=xx:xx:xx:xx:xx:xx vlan-mode=no-tag
add comment=xxxxxxxxx interface=wlan2 mac-address=xx:xx:xx:xx:xx:xx vlan-mode=no-tag
add comment=xxxxxxxxx interface=wlan2 mac-address=xx:xx:xx:xx:xx:xx vlan-mode=no-tag
add comment=xxxxxxxxx interface=wlan2 mac-address=xx:xx:xx:xx:xx:xx vlan-mode=no-tag
add comment=xxxxxxxxx interface=wlan2 mac-address=xx:xx:xx:xx:xx:xx vlan-mode=no-tag
add comment=xxxxxxxxx interface=wlan2 mac-address=xx:xx:xx:xx:xx:xx vlan-mode=no-tag
add comment=xxxxxxxxx interface=wlan2 mac-address=xx:xx:xx:xx:xx:xx vlan-mode=no-tag
add comment=xxxxxxxxx interface=wlan2 mac-address=xx:xx:xx:xx:xx:xx vlan-mode=no-tag
/ip address
add address=192.168.88.1/24 comment=defconf interface=bridge network=192.168.88.0
/ip dhcp-client
add comment=defconf dhcp-options=hostname,clientid disabled=no interface=ether1
/ip dhcp-server network
add address=192.168.88.0/24 comment=defconf gateway=192.168.88.1
/ip dns
set allow-remote-requests=yes
/ip dns static
add address=192.168.88.1 name=router.lan
/ip firewall filter
add action=accept chain=input comment="defconf: accept established,related,untracked" connection-state=established,related,untracked
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=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=dst-nat chain=dstnat comment="NAT HTTP" dst-address=193.248.32.7 dst-port=4000 in-interface=all-ppp protocol=tcp to-addresses=\
    192.168.88.246 to-ports=80
add action=masquerade chain=srcnat comment="defconf: masquerade" ipsec-policy=out,none out-interface-list=WAN
/ip ssh
set allow-none-crypto=yes forwarding-enabled=remote
/system clock
set time-zone-name=xxxxx
/tool mac-server
set allowed-interface-list=LAN
/tool mac-server mac-winbox
set allowed-interface-list=LAN

And from where exactly do you try that “curl 193.248.32.7:4000”? Is it really from outside (another device in internet), not from same LAN, right? Because from same LAN you’d also need what’s nicely explained here:

https://wiki.mikrotik.com/wiki/Hairpin_NAT

And you’d need to get rid of “in-interface=all-ppp” in dstnat rule (you can do it anyway if you have dst-address there).

Thanks.

Currently I am trying from a device outside the LAN. I will address the Hairpin NAT later, but for now I would like to understand how to connect from outside.

Ok, then I omit in-interface. But I would like to clarify which address should I set in dst-address: the public IP of my router, the private IP of my router, or the IP of my server.

/ip firewall nat add chain=dstnat action=dst-nat protocol=tcp dst-address=[WAN_IP] dst-port=4000 to-addresses=192.168.88.246 to-ports=80 comment="NAT HTTP"

A.

The dst-address is the one on which the rule matches, i.e. the public one (in the dst-nat chain); to-addresses is the new one to be set.

Thanks @sindy:

The router has a dynamic IP, so I can not set this IP to dst-address.

A.

Then dst-address-type=local will do the trick. It’s not perfect, because the rule will match for any address assigned to router, but with a completely non-standard port like 4000 it shouldn’t be a problem. If it would be other port which you might want to use also for something on router (e.g. 80 for WebFig), it’s possible to use both dst-address-type=local dst-address=!192.168.88.1 and this address would be excluded from dstnat.

It’s also possible to use specific address and update it when PPPoE connects, e.g.:

/ip firewall nat
add action=jump chain=dstnat comment=wan-port-forward disabled=yes jump-target=port-forward
add action=dst-nat chain=port-forward dst-port=80 protocol=tcp to-addresses=192.168.88.10
add action=dst-nat chain=port-forward dst-port=25 protocol=tcp to-addresses=192.168.88.20
/ppp profile
add <other options> \
    on-down="/ip firewall nat set [/ip firewall nat find comment=\"wan-port-forward\"] disabled=yes;" \
    on-up="/ip firewall nat set [/ip firewall nat find comment=\"wan-port-forward\"] dst-address=\$\"local-address\" disabled=no;"

In a way it’s better than previous solution, because dstnat rule applies only to the one correct address, same way as with static config. But I don’t really like the script being the “moving part”. It should be reliable, but I just trust static things better.

Tons of thanks, finally it is working with dst-address-type=local. For example, here I can access the web server, or the ssh.

 
add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-port=4000 to-addresses=192.168.88.246 to-ports=80 comment="NAT HTTP"
add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-port=4001 to-addresses=192.168.88.246 to-ports=22 comment="NAT SSH"

Now I will try to set up the Hairpin NAT following https://wiki.mikrotik.com/wiki/Hairpin_NAT

Anyway, I’m curious about the update when PPPoE connects:

/ip firewall nat
add action=jump chain=dstnat comment=wan-port-forward disabled=yes jump-target=port-forward
add action=dst-nat chain=port-forward dst-port=80 protocol=tcp to-addresses=192.168.88.10
add action=dst-nat chain=port-forward dst-port=25 protocol=tcp to-addresses=192.168.88.20
/ppp profile
add <other options> \
    on-down="/ip firewall nat set [/ip firewall nat find comment=\"wan-port-forward\"] disabled=yes;" \
    on-up="/ip firewall nat set [/ip firewall nat find comment=\"wan-port-forward\"] dst-address=\$\"local-address\" disabled=no;"

Any help to understand it will be welcome!

Just did the Hairpin NAT with this rule:

add action=masquerade chain=srcnat dst-address=192.168.88.246 src-address=192.168.88.0/24 comment="Hairpin NAT"

Works nicely

@Sob was reading it diagonally too so he took my assumption that you use pppoe as a fact.

Now for all ppp-based protocols you can define a script to be run when the interface goes up and another one to be run when it goes down as on-up and on-down items in /ppp profile.

For dhcp client, there is only a single script which is called at any event, and the script has access to some variables, see the related documentation.

But basically I’d recommend to use in-interface-list=WAN or in-interface=ether1 for this particular case. It is not clear to me why your original rule with in-interface=all-ethernet didn’t work given that you actually do use ether1 as uplink.

In fact my NAT rules works omiting in-interface or with in-interface=bridge

add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-port=4000 to-addresses=192.168.88.246 to-ports=80 comment=“NAT HTTP”

add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-port=4000 in-interface=bridge to-addresses=192.168.88.246 to-ports=80 comment=“NAT HTTP”

Any of these two will work. I dont know if with in-interface=bridge I am narrowing the scope of the NAT rule or not, but for starters both of them work

When setting in-interface=bridge NAT should stop working for connections from WAN …

Oops, no PPPoE. Well, someone who I usually trust did wrote that it’s there, so I didn’t really look closely. :slight_smile: But the idea was simple, to automatically update dst-address in dstnat rule. And the same could be done with DHCP client, if you tweak the example script from manual a little bit.

I don’t like in-interface(-list) for port forwarding, because a) it’s not compatible with hairpin NAT, b) on philosophical level it’s completely wrong, you don’t want to forward anything coming from internet, you want to forward packets from internet destined to your public address (but I do admit that for usual connection with one address, it will be the same thing in 99.999% cases).

In this case, I’d stick with just dst-address-type=local. It’s still kind of wrong, but it’s not that bad.

@sob I tried with a port 80 and dst-address=!192.168.88.1, but it worked only in local, not from outside the network.

       
add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-address=!192.168.88.1 dst-port=80 to-addresses=192.168.88.246 to-ports=80 comment="NAT HTTP Welcome"

Also, my setup works for all IPs, both in and outside the LAN, except when trying to point to the router itself: if I point to the HTTP interface of my router from outside the LAN —193.248.32.7:4876—, it won’t work.

This is the full set of NAT rules I have right now

add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-address=!192.168.88.1 dst-port=80 to-addresses=192.168.88.246 to-ports=80 comment="NAT HTTP Welcome"
add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-port=4874 to-addresses=192.168.88.246 to-ports=22 comment="NAT SSH Server"
add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-port=4875 to-addresses=192.168.88.1 to-ports=22 comment="NAT SSH Mikrotik"
add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-port=4876 to-addresses=192.168.88.1 to-ports=80 comment="NAT HTTP Mikrotik"
add action=masquerade chain=srcnat dst-address=192.168.88.246 src-address=192.168.88.0/24 comment="Hairpin NAT Server"

So my current issues are:

  • Can’t use default port 80 to redirect to the 80 to the server
  • Can’t access my router —192.168.88.1—, both from HTTP web interface and SSH, from outside the network

Dstnat to router itself won’t work, connection will be blocked by this rule:

/ip firewall filter
add action=drop chain=input comment="defconf: drop all not coming from LAN" in-interface-list=!LAN

Just returned from a long weekend and found your answer @sob. Disabling this firewall rule enables access from outside the network. Is it unsafe to allow it or should this rule be activated?

My other question: as you said previously, I cant use any standart port. For example, I’m trying to redirect from the default port 80 to the port 80 in my server, so the default apache html page is returned. But it fails to access it:

add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-port=80 to-addresses=192.168.88.246 to-ports=80 comment="NAT HTTP Welcome"

Using dst-address=!192.168.88.1 in this rule didn’t help with this:

add chain=dstnat action=dst-nat protocol=tcp dst-address-type=local dst-address=!192.168.88.1 dst-port=80 to-addresses=192.168.88.246 to-ports=80 comment="NAT HTTP Welcome"

Any help will be welcome!

No, that rule is good, you don’t want to disable it. If you want some exceptions, you should add them as other rules before this one.

I already forgot all details and I don’t want to study whole thread again, so sorry if something is a little inaccurate.

It doesn’t make much sense to do dstnat to router itself. In any case, you’ll be accessing router’s public address, so you can just allow access to ports 4875 and 4876 from anywhere and result will be the same. You can allow input for connection-nat-state=dstnat, but I think it will only make the config confusing.

Rules in your last post are standard port forwarding, they should work fine.