VPN over SSH

How to setup a VPN over SSH in ubuntu.


I wrote this article a while ago on my old blog. Apparently some people found this article very interesting, so now, I bring it back online. Enjoy!

Part 1 - Fri, 2007-06-22 22:48

I work at the computer science department of a university that was lucky enough to receive a class B IP-range in the beginning of the internet age. Since the computer science department was probably the first department with an internet connection, it has a large enough part of that IP-range to have no IP-address shortage at all: every machine has a routable IP-address. Although we don't have the 10.* or 192.168.* addresses typical for private networks, we do have a private network. Everything that doesn't need to accessible from the internet is firewalled.

Some of the internal services, like the IMAP-server, the intranet webserver or the calendar server are also needed if you want to do some work from home or from a hotel during a conference... For one service a plain ssh-tunnel is often satisfactory, but if you want to use several services, you have to set up a separate ssh-tunnel for each. A vpn solution would be welcome.

Yesterday, I came across the ubuntu community documentation that describes how to set up a vpn using a new feature of openssh 4.3: TUN-devices. Openssh can now set up a virtual network device at the two machines that are connected via the ssh-connection. This allows to set up a point-to-point connection between the two machines. Everything that is sent over this connection is tunneled through the ssh-connection.

The community documentation describes how to integrate the remote machine completely in the private network. This is however not possible in my situation, because the dhcp-server on our network only provides IPs to machines with a MAC-address it knows. So, I did something similar, but I used NAT to masquerade the connections of the remote machine as if they were coming from my machine at work.

The network setup is as follows (the IPs are obfuscated):

    +---------------+            OpenSSH 4.3           +---------------+
    |  work         | tun0 -- Tunnel Interface -- tun0 |   home        |
    |               | <------------------------------->|               |
    |  (machine A)  |       |   (machine B) |
    +-------+-------+     point to point connection    +-------+-------+
       eth0 |                                                  | eth1 |                                                  |
            |                                                  |
            |                                                  |
            |                                                  |
    +-------+-------+          +-~-~-~-~-~-~-~-+       +-------+-------+
    |  Firewall     |          |               |       |   Internet    |
    |  filters for: |          |  The Internet |       |  NAT gateway  |
    |   |<-------->|               |<----->|  |
    |   |          |               |       |               |
    +---------------+          +-~-~-~-~-~-~-~-+       +---------------+

* OpenSSH 4.3 or greater
* Root access at both machines
* PermitRootLogin yes in /etc/ssh/sshd_config on the machine at work (default in ubuntu)

During the rest of this procedure I will indicate the host where a command has to be executed by inserting the hostname (work or home) before the prompt.

One issue the community documentation doesn't address is: how to log in to the machine at work as root? Since the default situation in ubuntu is to have the root password scrambled. One option is to set a password for root using:

sudo passwd

Personally, I prefer using ssh's public key authentication mechanism. First, generate a key:

home $ sudo ssh-keygen -t rsa -b 2048 -f /root/.ssh/id_vpn

This will ask for a passphrase, if you want to be able to automate the whole thing afterwards, leave the passphrase empty. Now, copy the key to the machine at work and add it to authorized_keys:

home $ sudo cp /root/.ssh/ /tmp
home $ scp /tmp/ user@work:/tmp
work $ sudo mkdir /root/.ssh
work $ sudo chmod 700 /root/.ssh
work $ cat /tmp/ | sudo tee -a /root/.ssh/authorized_keys
work $ sudo chmod go-rwx /root/.ssh/authorized_keys

It should now be possible to login to the machine at work as root. The connection must also be initiated by root to allow ssh to create a TUN-device on the machine at home. So we use sudo:

home $ sudo ssh -i /root/.ssh/id_vpn root@work

If asked for a password, this is sudo asking for your user password! If you entered a passphrase for the ssh-key, you will have to enter it.

If this login succeeds, you are ready for the next step. First make sure the ssh-configuration for the ssh-server at work allows to create TUN-devices. Open /etc/ssh/sshd_config in your favorite editor (as root or using sudo) and make sure it contains the following line:

PermitTunnel yes

And restart the ssh-server:

work $ sudo /etc/init.d/ssh restart

We are now ready to set up the vpn connection. The only command that is fundamentally different from the instructions in the community documentation is the line with iptables. This line sets up the NAT to masquerade the home machine. You must be root for all the commands in the code block below. We use sudo at the machine at home. At the machine at work, this is not needed, since the ssh-connection provides us with a root-shell:

home $ sudo ssh -w 0:0 -i /root/.ssh/id_vpn root@work
home $ sudo ifconfig tun0 pointopoint
work # ifconfig tun0 pointopoint
work # echo 1 > /proc/sys/net/ipv4/ip_forward
work # iptables -t nat -A POSTROUTING --source -j SNAT --to-source
home $ sudo ip route add via
home $ sudo ip route add dev tun0 src
home $ sudo ip route add dev tun0 src


Off course, this should be automated, otherwise we could as well set up a ssh-tunnel for each service. I didn't have time to do the automation yet, but if I do, I will keep you informed.

NOTE: if you have a firewall at your machine at work, you have to make sure the equivalent of the ip forwarding line and the iptables line is in your firewall config. For example for shorewall, insert the following lines in the indicated configuration files:

vpn tun0
vpn all ACCEPT

Part 2 - Sun, 2007-06-24 12:23

Friday, I blogged about how to access private services via a vpn over ssh. Today, I have found some time to automate the process of setting up the tunnel.

First of all, make sure the ifup/ifdown commands know the tun0 device. Put the following lines in /etc/network/interfaces (at the home machine):

iface tun0 inet static

For the machine at work: switch the IPs.

Now edit the scripts attached to this page and place them in the directories /etc/network/if-pre-up.d, /etc/network/if-up.d /etc/network/if-down.d and /etc/network/if-post-down.d/ at your home machine. Make sure they are executable.

In my setup, the NAT at the machine at work is done by shorewall and is set up at boot time. If you use the iptables line from my previous post, you can make sure it is executed at startup by placing it in /etc/rc.local. Thus, open /etc/rc.local in your favorite editor (as root or using sudo) and place the following lines in it (off course make sure the IP-address matches your situation):

echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING --source -j SNAT --to-source

You should now be able to start and stop the vpn by issuing 'sudo ifup tun0' and 'sudo ifdown tun0' respectively (at your home machine).

We now know which commands are executed at the work machine, which allows us to secure the ssh-server a bit. Change the PermitRootLogin line in /etc/ssh/sshd_config to look like this:

PermitRootLogin forced-commands-only

And edit /root/.ssh/authorized_keys as follows:

tunnel="0",command="/sbin/ifdown tun0;/sbin/ifup tun0" ssh-rsa ... root@home

(the ... represent the base64-encoded public key, do not modify it!)
Restart the ssh-server:

$ sudo /etc/init.d/ssh restart