Simple VPN Setup on Unix Systems by Tunneling IP Traffic over SSH

With the prevalence of VPN appliances on the market today, this is decidedly an old-school method of creating a VPN link between networks.  However, there are some distinct advantages to using IP/SSH Tunnels that aren’t always available from an appliance.

ssh-ip-tunnel-small.jpg

Most network VPN solutions require both ends of the VPN connection to have static Internet accessible IP addresses.  But for a variety of reasons, it may not be possible for the remote location to get a static IP from the ISP.  In some cases, acquiring an Internet accessible IP may also not be possible.  There may be firewalls or NAT’d (Network Address Translations / Masquerading) gateways between the remote office and the Internet.  Or, simply, Unix servers may already exist at both locations, negating the need to purchase more hardware.

This article describes how to setup and configure a VPN connection via SSH/IP Tunneling between two Unix servers.  The image at right, is a simplified map of an Tunneled VPN network. Click to enlarge.

To implement an IP/SSH Tunnel, a unix system must be available on both ends of the VPN connection.  In most cases, this doesn’t have to be a dedicated machine.  Unless you are planning on having a large number of Tunneled VPN connections, this process can be piggy-backed on a unix system already performing another function, such as file-sharing or the local email server.
ASSUMPTIONS

For the purposes of this document, I’ll make some assumptions about the networks and servers on both sides of the VPN connection.  Both tunneling systems will act as routers for traffic between the networks across the VPN tunnel.

The VPN machine at the Main Office will have the following configuration:
– Server name: mainoffice
– Internet IP: 123.45.67.89
– Local Network: 192.168.3.x
– LAN IP: 192.168.3.11
– Local Default Router 192.168.3.1
– VPN name: vpn-main
– VPN IP: 10.0.0.1

The VPN machine at the Remote Office will have the following configuration:
– Server name: remoteoffice1
– Internet IP is dynamically assigned by ISP
– Local Network: 192.168.5.x
– LAN IP: 192.168.5.11
– Local Default Router 192.168.5.11
– VPN name: vpn-remote1
– VPN IP: 10.0.0.2

We assume that the machines have already been configured, connected to their local networks, and that Internet connections for each network has already been established.  We also assume that all required software has been installed that ssh (Secure Shell) is running on both machines.

For this document, I’ll further assume that both systems being described are running NetBSD, although any version of *BSD or Linux, as well as many unicies, will work with varying degrees of effort.
SOFTWARE

Although the following software is required, it’s installation is beyond the scope of this article.

pppd
The Point to Point Protocol daemon is included as part of the base installation of NetBSD and many other unix operating systems.

OpenSSH
This is also included in the base installation of NetBSD and most other unicies.  If not included with your OS, check www.openssh.com/ for binary installations and/or source code.

pty-redir
This is a small application allocates allocates a pty (pseudo terminal) and redirects
stdio (standard out IO) to that pty.  It can be found in the NetBSD Package System.  It can also be found on the Internet at the following locations:
ftp.vein.hu/pub/ssa/contrib/mag/pty-redir-0.1.tar.gz
bleu.west.spy.net/~dustin/soft/pty-redir-0.1.tar.gz

ssh-ip-tunnel
This is the application that makes VPN over pppd work.  It can also be found in the NetBSD Package System as well as at the following Internet location:
bleu.west.spy.net/~dustin/soft/vpn-1.0.tar.gz

Once both systems, mainoffice and remoteoffice1, are connected to the Internet and all the pre-requisite software has been installed, we can begin configuring them.
CREATE VPN ACCOUNTS

For security’s sake, we have to create a user group and account to manage the VPN connections on each system.  This group and account should NOT have any special access or authority.  As root (Superuser/Administrator) on each system, we can create the group and account with the following commands:

# groupadd vpn
# useradd -m -c “VPN User” -G vpn vpnuser
# mkdir /home/vpnuser/.ssh

This creates the user group ‘vpn’ and a user account ‘vpnuser’ as a member of the ‘vpn’ group.

It’s very important that these are locked accounts.  On NetBSD, this is the default way accounts are created.  On another unix, ensure there is nothing but asterisks (”*”) are in the password field of /etc/shadow.

NetBSD:
vpnuser:*************:1002:1100::0:0:VPN User:/home/vpnuser:/bin/csh

Other unix:
vpnuser:*:11933::::::
ADD VPN IP

Add the IP addresses that will be used by pppd’s VPN interfaces to the /etc/hosts file on both systems:

10.0.0.1   vpn-main
10.0.0.2   vpn-remote1

On the remote server, remoteoffice1, add the Internet IP for the Main Office’s VPN machine to the /etc/hosts file:

123.45.67.89   mainoffice
CONFIGURE SSH

Since the account ‘vpnuser’ is locked, we have to enable Public Key authentication in order for it to login from remoteoffice1 to mainoffice.  On the Main Office machine, mainoffice, modify the sshd configuration file (usually /etc/ssh/sshd_config) to allow Public Key authentication, by removing the comment character from the line reading:

#PubkeyAuthentication yes

So that it reads as:

PubkeyAuthentication yes
CREATE & EXCHANGE SSH KEYS

Now that Public Key authentication is allowed, we must generate a public/private key pair for the ‘vpnuser’ account on remoteoffice1.  As root, enter the following commands:

# ssh-keygen -t dsa -f /home/vpnuser/.ssh/id_dsa -C “remoteoffice1″ -N ‘’
# ssh-keygen -t rsa -f /home/vpnuser/.ssh/id_rsa -C “remoteoffice1″ -N ‘’

Note that we use the “-N ‘’” parameter to generate these keys with empty pass-phrases because we want to be able to manage the VPN connections via scripts.  Requiring the pass-phrase be entered each time VPN is started would be cumbersome at best and make automating the process virtually impossible.
ASSEMBLE & DISTRIBUTE AUTHORIZED KEYS

Once the key-pairs have been generated, you’ll need to assemble the public-keys into a file and transfer it to the server at Main Office.

As root, concatentate the public keys into one file:
# cd /home/vpnuser/.ssh
# cat id_*.pub > public_keys.remoteoffice1

Copy the public_keys.remoteoffice1 file to the mainoffice server.  You can do this either via sneaker-net or by using another account on mainoffice.  Once you have them on mainoffice, add them to the authorized_keys files:

# cd /home/vpnuser/.ssh
# cat public_keys.remoteoffice1 > authorized_keys
# cat public_keys.remoteoffice1 > authorized_keys2

Now, on both systems, mainoffice and remoteoffice1, set the correct ownership and permissions of the vpnuser files:

# chown -R vpnuser /home/vpnuser/.ssh
# chmod 600 /home/vpnuser/.ssh/*
# chmod 644 /home/vpnuser/.ssh/*.pub
CONFIGURE TUNNEL

The package ’ssh-ip-tunnel’ was previously named ‘vpn.’  But since there are numerous methods of setting up a VPN, the developers renamed it to specifically indicate it’s particular VPN methodology.  The name of the files inside the package remain unchanged.  To read the man page for ssh-ip-tunnel, it is still ‘man vpn’.

The configuration for ssh-ip-tunnel is contained in peer files.  On NetBSD, these will be located in /usr/pkg/etc/vpn/peers, but on other unicies, they may be located in/usr/local/etc/vpn/peers or /etc/vpn/peers.

On the Main Office server, mainoffice, create a peer file remoteoffice1 — since mainoffice doesn’t initiate the VPN connection, we only need the most basic configuration options:

#/usr/pkg/etc/vpn/peers/remoteoffice1
SSHUSER=vpnuser

On the Remote Office server, remoteoffice1, create a peer file name mainoffice.  Since remoteoffice1 initiates the VPN connection, this file is a bit more extensive:

#/usr/pkg/etc/vpn/peers/mainoffice
SSH=”/usr/bin/ssh -2″
PEER=server1
SSHUSER=vpnuser
RSAKEY=/home/vpnuser/.ssh/id_rsa
LOCALPPP=/usr/sbin/pppd
LPPPOPTIONS=”call vpn-main”
REMOTEPPP=/usr/sbin/pppd
RPPPOPTIONS=”call vpn-remote1″

Note: the paths listed in these files are correct for NetBSD.  Your paths may differ, depending on the OS you are using.
CONFIGURE PPP

On the Main Office machine, mainoffice, create a PPP peer file:

#/etc/ppp/peers/vpn-remote1
#debug debug debug debug debug
mtu 1500
mru 1500
noauth
noipv6
10.0.0.1:10.0.0.2
netmask 255.255.255.0
linkname vpn-remote1
ipparam 192.168.5.0 # Network on other side of vpn-remote1

On remoteoffice1, at the Remote Office, create a PPP peer file:

#/etc/ppp/peers/vpn-main
#debug debug debug debug debug
mtu 1500
mru 1500
noauth
noipv6
netmask 255.255.255.0
linkname vpn-remote1
ipparam 192.168.3.0 # Network on other side of vpn-main
silent

Notice that the VPN link IP addresses are specified in the PPP peer file on mainoffice, the first file above.
TESTING

To test the configuration, login as root on the Remote Office machine, remoteoffice1, and enter the following commands:

# su vpnuser
{1} ssh -2 vpnuser@mainoffice

You will have to answer “yes” to the “continue connecting” question, but you should then be presented with a shell on mainoffice — the “continue connection” prompt only occurs the first time you log onto a particular system using ssh.

Assuming the above test worked correctly, test the vpn connection too.  Logon to mainoffice as root and enter the following command:

# vpn mainoffice authtest
MONITORING SCRIPT

Now that you can establish a VPN session from the Remote Office to the Main Office, you’ll need a method of monitoring the connection and restarting it as needed.  The following script can be installed on remoteoffice1 and, once started, will monitor the VPN connection and re-establish it if it goes down:

#!/bin/sh
#
# vpnchk -- Monitor VPN Connection and restart as necessary.
#
# A single parameter is required:  vpnchk

#
# Ping REMOTE_VPN_HOST approximately every 10 seconds. Keep track of
# failed pings by incrementing COUNT.  If pings are good, always reset
# COUNT back to zero.  Only take corrective action when the number of
# failed pings reaches THRESH(hold).  Notify root by mail whenever the
# status of the vpn connection has changed.
#
# Eric Fox
# http://fox.phoenix.az.us/
#
##########################################################################
#
REMOTE_VPN_HOST=${1}
MAILTO=root@localhost
#
##########################################################################
if [ “${REMOTE_VPN_HOST}” = “” ]; then
  echo “Syntax: vpnchk
”
  exit
fi
##########################################################################

CHK_TEXT=”call ${REMOTE_VPN_HOST}”

THRESH=3
COUNT=0

while [ : ]; do  # loop forever
  if ping -c 5 ${REMOTE_VPN_HOST} 1>/dev/null 2>/dev/null ; then
    COUNT=0
    if [ -f /tmp/.vpn-down ]; then
      rm -f /tmp/.vpn-down
      MSG=”VPN Connection is -UP-: `date “+%H:%M on %m/%d/%Y”`”
      echo ${MSG} | mailx -s”${MSG}” ${MAILTO}
    fi
  else
    COUNT=`expr ${COUNT} + 1`
    if [ ${COUNT} -ge ${THRESH} ]; then
      if [ ! -f /tmp/.vpn-down ]; then
        touch /tmp/.vpn-down
        MSG=”VPN Connection is DOWN: `date “+%H:%M on %m/%d/%Y”`”
        echo ${MSG} | mailx -s”${MSG}” ${MAILTO}
      fi
      PID=`ps -awwjx | grep -v grep | grep “${CHK_TEXT}” | awk ‘{print $2}’`
      if [ ! “${PID}” = “” ]; then
        for xPID in ${PID} ; do kill -KILL ${PID} ; done
        COUNT=0
        sleep 60
      fi
      nohup /usr/pkg/sbin/vpn fire start &
      sleep 150
    fi
  fi
  sleep 10
done

# end

Assuming the ‘vpnchk‘ script was placed in /usr/local/sbin, simply run the following command as root on remoteoffice2:

/usr/local/sbin/vpnchk mainoffice
NETWORK ROUTING

In order to perform routing, the systems must be able to perform IP forwarding.  On some unices this is on by default, and on others it must be specifically activated.

NetBSD falls into the later category and must have IP Forwarding activated.  This can be done either by recompiling the kernel, or by use of the ‘sysctl‘ command.  For simplicity, the use of ‘sysctl‘ is documented here.

On both systems, mainoffice and remoteoffice1, enter the following command:

# /sbin/sysctl -w net.inet.ip.forwarding=1

Also add this command to the bottom of the /etc/rc.local file on both systems so it will be issued each time the system is rebooted.

On both systems, create ‘ip-up‘ and ‘ip-down‘ scripts to add/delete network routes when the VPN connection starts or has dropped:

#!/bin/sh
# /etc/ppp/ip-up
# Add route for REMOTE_NETWORK
##########################################################################
REMOTE_IP="${5}"
REMOTE_NETWORK="${6}"
if [ ! "${REMOTE_NETWORK}" = "" ]; then
  /sbin/route add -net ${REMOTE_NETWORK} ${REMOTE_IP}
fi
#!/bin/sh
# /etc/ppp/ip-down
# Delete route for REMOTE_NETWORK
##########################################################################
REMOTE_IP="${5}"
REMOTE_NETWORK="${6}"
if [ ! "${REMOTE_NETWORK}" = "" ]; then
  /sbin/route delete -net ${REMOTE_NETWORK} ${REMOTE_IP}
fi

Finally, if these systems are not the default routers for their respective networks, a route must be added to these default routers to route network traffic for each remote site to the local network’s VPN server.

Assuming the default router for Main Office is another NetBSD systems, this can be accomplished by adding the following to the default router’s /etc/netstart.local file:
/sbin/route add -net 192.168.5.0 192.168.3.14
CONCLUSION

Now, with the VPN in place you should be able to ping any machine on the 192.168.3.x network from any machine on the 192.168.5.x network, and visa versa, even though they may be in different offices, cities, or states, or even different countries.  The resources of both offices are now available to everyone as if they were all in the same building.

Leave a Reply

Your email address will not be published. Required fields are marked *

*