Page tree
Skip to end of metadata
Go to start of metadata

Overview

The goal is to be able to access a web server which is hosted behind a zeroshell NAT from within a network protected by the same Zeroshell router. The classical NAT loopback problem.

  • Site www.myweb.com is hosted on a server connected to a LAN protected by a NAT.
  • www.myweb.com resolves to the public IP address of the above mentioned NAT.
  • When i access www.myweb.com from outside the NATted LAN, all works fine.
  • When i access www.myweb.com from within the NATted LAN, i cannot access the webserver.

Details

Static IP Address

With a static public IP address, this is very simple and can be done using the Router > Virtual Servers table in Zeroshell: 

Input InterfaceUse ANY, or the interface of the local NATted LAN
IP AddressThe static public IP Address
ProtocolIn this example TCP
Local PortIn this example 80
Remote IPThe LANs local IP address of the web server
Remote PortIn this example 80

 

Dynamic IP Address

This is annoying, cuz we never know the IP address until we are connected. I chose an approach which makes use of a dynamic DNS setup, because i already had that setup. The main concept is the following:

  1. Zeroshell boots up
  2. Zeroshell brings up all services, internet connection and updates IP address on dynamic DNS system.
  3. Zeroshell then adds a rule to the firewall table and uses the FQDN of the dynamic DNS to obtain the public IP address to setup the rule.
  4. Every 10m Zeroshell checks that the IP address hasn't changed. If it has, delete the old rule, and add an updated one.

Using a DDNS lookup has the advantage of taking always the public IP address, regardless of which internet connection one is using (if the DDNS is setup properly). I chose this route also because i do have two outbound connections, so finding which IP address is currently the default route would have made this script more complicated.

Add "NAT and Virtual Servers" script

This goes in Setup > Scripts/Cron > "NAT and Virtual Servers"

# Loopback for scheme.brillig.org 
# This will work once the DYNDNS has been updated, since the iptables rule depends on the public IP address.
# At startup only: add rule. During uptime, loopback rule will be updated using the UpdateLoopback cron job.
LAST_PUBLIC_IP_FILE="/Database/LAST_PUBLIC_IP"
PUBLIC_IP=$(dig +short ciapee.no-ip.biz)
if [ -r "${LAST_PUBLIC_IP_FILE}" ]; then
	LAST_PUBLIC_IP=$(cat "${LAST_PUBLIC_IP_FILE}")
	# Remove rule, just in case there is on here for some reason. Don't log error messages.
	echo "Removing possibly stale loopback rule."
	iptables -t nat -D PREROUTING -d ${LAST_PUBLIC_IP} -s 10.1.16.0/24 -p tcp --dport 80 -j DNAT --to 10.1.19.100:80 2> /dev/null
fi
echo "Adding new loopback rule"
iptables -t nat -A PREROUTING -d ${PUBLIC_IP} -s 10.1.16.0/24 -p tcp --dport 80 -j DNAT --to 10.1.19.100:80
echo ${PUBLIC_IP} > "${LAST_PUBLIC_IP_FILE}"

Add new cronjob to run every 10 minutes

# Loopback for scheme.brillig.org 
# This will work once the DYNDNS has been updated, since the iptables rule depends on the public IP address.
LAST_PUBLIC_IP_FILE="/Database/LAST_PUBLIC_IP"
PUBLIC_IP=$(dig +short ciapee.no-ip.biz)
if [ -w "${LAST_PUBLIC_IP_FILE}" ]; then
	LAST_PUBLIC_IP=$(cat "${LAST_PUBLIC_IP_FILE}")
	if [ "${LAST_PUBLIC_IP}" != "${PUBLIC_IP}" ]; then
		echo "New Public IP address detected. Updating Loopback rules."
		iptables -t nat -D PREROUTING -d ${LAST_PUBLIC_IP} -s 10.1.16.0/24 -p tcp --dport 80 -j DNAT --to 10.1.19.100:80
		iptables -t nat -A PREROUTING -d ${PUBLIC_IP} -s 10.1.16.0/24 -p tcp --dport 80 -j DNAT --to 10.1.19.100:80
		echo ${PUBLIC_IP} > "${LAST_PUBLIC_IP_FILE}"
	fi
fi

The system will save the last IP address in a file in /Database/LAST_PUBLIC_IP.