Friday, January 24, 2020

Debian / OpenVPN Site-to-Site VPN Solution that works behind NAT

This post details how to set up an OpenVPN Site-to-Site VPN link which will route traffic between two sites, where only one site has one UDP port forwarded through it's NAT router.  The second (restrictive) site, can be behind multiple NAT routers, does not require any port forwarding, and can also be on a dynamic public IP address.

I make no guarantees about privacy, or security, or reliability.  However, I can tell you that this solution has been, and is currently deployed in several locations and has rarely let me down. 
YMMV.

The two sites will be called Site A and Site B.
Each site requires a linux machine (I will be using Debian, but any linux distro with OpenVPN will work) with a single private IP address assigned to it. 

Site A
- can be called the Head Office, or main Hub
- will require a static public IP address
- will require a single UDP port forward rule added in to the site's internet router
- Local subnet is 192.168.6.0/24

Site B
- can be called the Branch Office, or remote office
- can be on a dynamic public IP
- will require outbound UDP traffic to be permitted
- does not require any port forwarding rules to function
- Local subnet is 192.168.10.0/24

We will also require a dedicated site-to-site subnet that will be used only by OpenVPN.  We will use 10.99.x.1 and 10.99.x.2 where x is the ID of the tunnel (starting from 0).  This will make more sense when you see the config below.

We will start configuring Site A's Debian VM (logged in as Root already):

#Configure network interfaces as required (set your VM's IP to something static, or assign a static reservation in your DHCP server)  E.g. 192.168.6.200
nano /etc/network/interfaces

#Install OpenVPN and OpenSSH-Server
apt-get install openvpn openssh-server

#one time commands to enable tun
modprobe tun
echo 'tun' >> /etc/modules

#one time command to enable tunnels through firewall
iptables -A FORWARD -i tun+ -j ACCEPT

#generate tun0.key
Cd /etc/openvpn
Openvpn --genkey --secret tun0.key

#NOTE: tun0.key must be present on each site.  You can copy from the SiteA VM to the SiteB VM using WinSCP.  The files MUST be the same on each end per tunnel.  

#create a startup script which brings up the OpenVPN Server
nano /etc/init.d/S99startvpn

Copy the contents below in to your S99startvpn file:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          startvpn
# Required-Start:
# Required-Stop:
# Default-Start:      2
# Default-Stop:
# Short-Description: Host OpenVPN Servers
# Description:
### END INIT INFO
#Tun 0 - Site B will connect in to us here on UDP PORT 5000
openvpn --port 5000 --dev tun0 --ifconfig 10.99.0.1 10.99.0.2 --verb 1 --secret /etc/openvpn/tun0.key --fragment 1400 --mssfix 1400 --tun-mtu 1450 &
sleep 3
#set up a route to the remote LAN subnet for this tunnel
ip route add 192.168.10.0/24 via 10.99.0.1
#enable packet forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

Now Save and close the script

#When you have the script in place, make it executable and owned by root:
chmod +x /etc/init.d/S99startvpn
chown root:root /etc/init.d/S99startvpn

#Set it to start automatically on boot with:
update-rc.d S99startvpn defaults
update-rc.d S99startvpn enable

Your Site A VM is now set up and can be rebooted.  You shouldn't have to touch it again.
When it reboots, it will automatically start up an instance of OpenVPN that is listening for incoming connections on UDP port 5000.  It's also got an ip of 10.99.0.1 on it's side of the tunnel.

Let's configure Site B's Debian VM (logged in as Root already):

#Configure network interfaces as required (set your VM's IP to something static, or assign a static reservation in your DHCP server) E.g. 192.168.10.200
nano /etc/network/interfaces

#Install OpenVPN and OpenSSH-Server
apt-get install openvpn openssh-server

#one time commands to enable tun
modprobe tun
echo 'tun' >> /etc/modules

#one time command to enable tunnels through firewall
iptables -A FORWARD -i tun+ -j ACCEPT

#NOTE: tun0.key must be present on each site.  You can copy from the SiteA VM to the SiteB VM using WinSCP.    The files MUST be the same on each end per tunnel.  

#create a startup script which brings up the OpenVPN client
nano /etc/init.d/S99startvpn

#copy the contents below in to your S99startvpn file:
#! /bin/sh
### BEGIN INIT INFO
# Provides:          startvpn
# Required-Start:
# Required-Stop:
# Default-Start:      2
# Default-Stop:
# Short-Description: Connect to VPN Server
# Description:
### END INIT INFO
#Tun0 - Connect to SITE A
openvpn --remote [INSERT_PUBLIC_IP_FOR_REMOTE_SITE_HERE] --port 5000 --dev tun0 --ifconfig 10.99.0.2 10.99.0.1 --verb 1 --secret /etc/openvpn/tun0.key --fragment 1400 --mssfix 1400 --tun-mtu 1450 &
sleep 30
#set up a route to the remote LAN subnet for this tunnel
ip route add 192.168.6.0/24 via 10.99.0.2
#set up a ping to keep NAT alive, one ping sent once every 60 seconds, otherwise NAT translations get deleted and the tunnel won't pass traffic when needed
ping 192.168.6.200 -i 60 &
#enable packet forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

Save and close the script

#When you have the script in place, make it executable and owned by root:
chmod +x /etc/init.d/S99startvpn
chown root:root /etc/init.d/S99startvpn

#Set it to start automatically on boot with:
update-rc.d S99startvpn defaults
update-rc.d S99startvpn enable

Your Site B VM is now set up and can be rebooted.  You shouldn't have to touch it again.
When it reboots, it will automatically start up an instance of OpenVPN that will connect to the Site A OpenVPN Server on UDP Port 5000.  It's also got an ip of 10.99.0.2 on it's side of the tunnel.

For both VMs, you could run the commands from the S99startvpn script by hand to make sure everything works before you automate it via the startup script.

Finally, joining the sites together so that the traffic routes at layer 3:

There are several ways you can do this.  I prefer setting up a static route on each site's primary default gateway for the remote subnet, with the next hop being the IP of the hub at each site.  Not all routers let you do this. 

This means that when traffic bound for the remote subnet hits your default gateway, the gateway will see the static route and forward the packet on to your site's new hub and it will then be forwarded over the open vpn link to the remote site.
This must be done on both sites for the traffic to traverse the link in both directions.

However, if you do not control the router at your restrictive site for example, and therefore cannot add a static route to that router, you must use static routes on each machine in each site that need to communicate instead.

Here's an example for doing it on Windows on a host in Site B:
route -p add 192.168.6.0 mask 255.255.255.0 192.168.10.200

Here's what the route command would look like on a host in Site A:
route -p add 192.168.10.0 mask 255.255.255.0 192.168.6.200

All we're doing here is telling windows to send traffic destined to the remote subnet to the new hub, instead of to the default gateway.

Please note that I came up with all of this 15 years ago when I knew almost nothing about any of this stuff.  As a result, there is a very good chance that the traffic isn't encrypted, or the keys are insecure, or the connection may not work with the latest version of Debian. 

There's also no firewall configured whatsoever (other than to allow all), and therefore there's nothing stopping anyone on either side of the link talking across it to devices on the other side.

There's no logging either, so you won't know what traffic is moving across the link. 

Also, this solution does nothing for DNS.  You will need to come up with your own solution, or use the IP addresses for communications. 

At the end of all this, you should have a functioning site to site link - if not, let me know and I'll try to help where I can. 
But, I know this works because I've been using this solution for 15 years with excellent results.

Good luck.