An ignorant man ages like an ox. His flesh may increase, but not his understanding.
This page is an extension of my blog post HERE . I will cover the commands in a bit more detail here as it is too long for a blog ;) .
This how-to is intended to cover the details of how to establish a VPN (Virtual Private Network) over a SSH connection. Starting with open-ssh 4.3, you can now use a ssh connection to set up a VPN.
This is technically termed "layer-3 IP-in-SSH tunnelling" and is not using ssh to port forward (ssh -L ) or create a dynamic "application level" forwarding (SOCKS) (ssh -D ). Rather a VPN is established using a SSH connection to create a virtual interface, tun0.
- IMO, this technique is easier to set up then openvpn, especially if you are using a single client.
- Works with most Linux distributions without the need to install any additional software on the clients. The server only needs openssh-server.
- This protocol uses udp to transmit tunneled tcp connections resulting in a more stable connection compared with port forwarding (using ssh with the -L or -D options).
- As of yet I do not know of a windows client which will use this protocol.
- If you are needing to set up a VPN with numerous clients I would use openvpn.
Although there are several "how-to's" on the web, most of them assume you know something about networking and routing. This page attempts to explain some of the "missing details".
Understanding VNC over SSH requires an understanding of ssh, "basic" networking concepts, and the "route" command.
Commands you may wish to review :
- ssh - in particular the options -M and -S .
- visudo / configuration of the sudoers file
In particular, taking the 5-10 minutes required to review the route man page can help enormously.
At the risk of oversimplification, the "route" command instructs the kernel where to send (route) network packets (traffic). The default gateway (gw) is where packets are sent if no route has been specified (for the destination host or network). Basic syntax for the route command is "send packets to host w.x.y.z through eth0".
Another potential stumbling blocks are the concepts of an IP Address, the difference between private / public networks, and port forwarding. Here are some links discussing networks and net masks :
- What is an IP address?
- Wikipedia Classful Network
- Wikipedia Private Network
- Assigning Private Network IP Addresses to the Internal LAN
A brief overview of public / private ip addresses and port forwarding :
Public - This is the ipaddress assigned to you by your internet provider. You can see your public ip address with this link
Private - This is the ipaddress assigned by your router. Generally 10.0.0.0 , 126.96.36.199 , and 192.168.0.0 are reserved for private networks. Wikipedia Private Networks
Port forwarding - You can not connect to a private ip address (private network) unless you have allowed (enabled) the network traffic connection from your router via a process known as port forwarding. From outside your LAN, you connect to your server useing the public ip address and the router forwards the connection to the server. The Port Forwarding Progression
Establishing a tunnel as outlined in this how to requires root access on both server and client. Most of the tutorials I reviewed spent minimal time reviewing security and, in my opinion, have some concerning security liabilities.
Before explaining the details of configuring the connection I would like to digress to discuss security.
1. Restrict root access by using the "PermitRootLogin without-password" (rather then "PermitRootLogin yes") in /etc/ssh/sshd_config . The syntax is a bit misleading, this does not allow root to log in without a password, rather it forces root to use a ssh key (logging in as root with a password is disabled). With this option you can leave the root account locked on Ubuntu.
2. Use ssh keys. You can configure a ssh key to force a command to be run at log in. In this example we will use -
tunnel="0",command="/sbin/ifdown tun0; /sbin/ifup tun0"
Basically you "force" a command by editing the authorized_keys file (~/.ssh/authorized_keys) on the server and adding command="command to be executed".
For this tutorial I will add the following to the key :
tunnel="0",command="/sbin/ifdown tun0; /sbin/ifup tun0",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
3. For a review of how to secure your ssh server see AdvancedOpenSSH
1. Configure sudo (see Sudoers Manual for additional information) to allow users to establish the VPN tunnel, without a password, without giving full root access.
Edit /etc/sudoers with
export EDITOR=gedit && sudo -E visudo
Add this line (near the bottom, in the group section, under the line which begins %admin ... ) to allow users in the group "vpn" to run ifup and ifdown, as root, using sudo, and without entering a password:
%vpn ALL=NOPASSWD: /sbin/ifup,/sbin/ifdown
2. If you wish, you can configure sudo to use a password (user "ALL" in place of "NOPASSWD".
3. I will also be generating a ssh key without a password, which is probably sufficient as the key will use a forced command and thus does not give full shell access. If you prefer, you can increase security by using a password with the key.
Server - This is the computer you will be logging in to for VPN access.
Public IP address 188.8.131.52 - The ssh port (22 by default) is forwarded (routed) by the router from the public ip address (184.108.40.206) to the private address (220.127.116.11).
Private Network 18.104.22.168/24
Private IP address 22.214.171.124
default gw 126.96.36.199
This is the computer you wish to connect to the server via VPN over SSH.
Public IP address - does not matter
Private network 192.168.0.0/24
Private IP address 192.168.1.10
Default gw 192.168.0.1
VPN Tunnel ( tun0 )
A virtual interface, called "tun0", is created and configured on both the client and server and is then used to establish a VPN connection. This virtual interface, tun0, is created by ssh using the -w flag (see below) and must be separate from the server or client networks.
In this example, I will use 10.0.0.0/24 as a network for the virtual interfaces.
Server tun0 IP address = 10.0.0.1
Client tun0 IP address = 10.0.0.2
STOP - If you do not understand private / public networks and that 188.8.131.52/24 , 192.168.1.0/24 , 10.0.0.0/24 , are 3 DIFFERENT NETWORKS, you may wish to review the previous links or you may have difficulty following the rest of this how-to .
Ok, enough background, let's set it up already.
1. Using any editor, open /etc/ssh/sshd_config and change the "PermitRootLogin" line and add the "PermitTunnel" line :
2. Allow NAT. These commands will enable NAT without the need to reboot (NAT will be persistent).
# enable now:
sudo sysctl -w net.ipv4.ip_forward=1
To set as default, using any editor, open /etc/sysctl.conf and add :
# Needed to add for forwarding
net.ipv4.ip_forward = 1
Next, configure iptables to allow masquerade (NAT)
sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
Your iptables settings will be lost when you reboot unless you configure a way of saving your settings. This can be done several ways, here I will use "iptables-save" and "iptables-restore" to setting masquerade as default.
sudo bash -c "iptables-save > /etc/iptables.rules"
Using any editor, open /etc/rc.local and add this line (above the exit 0 line)
iptables-restore < /etc/iptables.rules
3. Configure the tunnel (tun0). Using any editor, edit /etc/network/interfaces and add these lines (works on Debian/Ubuntu).
iface tun0 inet static
pointopoint 10.0.0.2 # Client tunnel address
The interface, tun0, will be brought up and down automatically when the client establishes a ssh connection, using forced commands on the ssh key (see below).
1. Make a ssh key , call it "VPN".
ssh-keygen -f VPN -b 1024
When generating the key, simply hit enter when asked for a password. This creates a "passwordless key". Alternately, if you wish to increase security you can enter a password.
Note: You can use a stronger key if you wish (increase the bits -b 4096)
2. On the client, put the private key (VPN) in /root/.ssh and set permissions.
#If needed, make the directory /root/.ssh first.
sudo mkdir /root/.ssh
sudo cp VPN /root/.ssh/VPN
sudo chown root:root /root/.ssh/VPN
sudo chmod 400 /root/.ssh/VPN
3. Configure the key on the server.
After transferring the public key (VPN.pub) to the server, first put it in /root/.ssh/authorized_keys.
sudo bash -c "cat VPN.pub >> /root/.ssh/authorized_keys"
Next, edit /root/.ssh/authorized_keys adding a "forced command" (just before the ssh-rsa AAA ...):
tunnel="0",command="/sbin/ifdown tun0; /sbin/ifup tun0",no-port-forwarding,no-ptty,no-X11-forwarding,no-agent-forwarding ssh-rsa AAAA .... (long sequence of random numbers / letters )
Note: The options "no-port-forwarding,no-ptty,no-X11-forwarding,no-agent-forwarding" further enhance security. Keep the forced command and the rest of the key all on a single line.
4. Add the servers nameserver in the client /etc/resolv.conf
On the server , show your nameserver with :
grep nameserver /etc/resolv.conf
On the client, use any editor and add the server's nameserver to /etc/resolv.conf.
5. Configure tun0 on the client.
Using any editor, add the following lines to /etc/network/interfaces :
You will need to change "184.108.40.206" to the IP address of your server and "192.168.0.1" to the default gateway of your client.
pre-up ssh -i /root/.ssh/VPN -S /var/run/ssh-vpn-tunnel-control -M -f -w 0:0 220.127.116.11 true
pre-up sleep 5
up route add -host 18.104.22.168 dev eth0
up route add default gw 10.0.0.1 dev tun0
up route del default gw 192.168.0.1 dev eth0
down route add default gw 192.168.0.1 dev eth0
down route del default gw 10.0.0.1 dev tun0
down route del -host 22.214.171.124 dev eth0
post-down ssh -i /root/.ssh/VPN -S /var/run/ssh-vpn-tunnel-control -O exit 126.96.36.199
Note : "pre-up ... 188.8.131.52 true" and"post-down ... exit 184.108.40.206" are all on one line.
Let us examine this configuration in some detail.
"pre-up" commands are run before the interface (tun0) is enabled.
The -M flag allows the client to control the tunnel (see man ssh).
The -S flag specifies the path for the control file
pre-up sleep 5 -> allows time for ssh to establish the tunnel before proceeding with configuration.
Optional: add the -C flag to the ssh command to use compression. Compression may enhance the performance of the tunnel.
address 10.0.0.2 # sets the tunnel ipaddress
pointtopoint 10.0.0.1 # establishes the point - to - point connection (VPN). netmask = 255.255.255.0 #netmask for tunnel
"up" commands are run once the tun0 is configured and are used to change the default route on the client to use the tunnel as default, thus establishing the VPN.
up route add -host # Specifies the route for ssh traffic to the public ip address of the server. This is necessary as we are next going to change the default gateway to the VPN connection.
up route add default gw 10.0.0.1 dev tun0
up route del default gw 192.168.0.1 dev eth0
Those lines change the default gateway from eth0 to the tunnel. Once the gateway is changed, the VPN connection is complete and the client now behaves as if it is directly connected to the server's private network, 220.127.116.11/24 in this example. The client can now ping or connect to other computers on the private network.
The down commands reverse the routing when the tunnel is brought down.
post-down ssh ... exit 18.104.22.168 brings the ssh connection and thus the tunnel, tun0, down.
6. Make a new group, "vpn", and add users you wish to use the tunnel to the vpn group.
Change the user "bodhi" to your actual user name.
sudo addgroup vpn
sudo adduser bodhi vpn
7. Allow members of the vpn group to establish the ssh-vpn tunnel without a password.
Add this line (near the bottom, in the group section, under the line which begins %admin ... )
Save the file and exit gedit.
Users in the vpn group can establish and disconnect the VPN.
Bring the connection up:
sudo ifup tun0
Bring the connection down:
sudo ifdown tun0
One way to test the connection is with tracepath.
Use tracepath both before and after establishing the VPN tunnel. Before you establish the tunnel you will see the network traffic going through eth0 to your LAN, after it will go through tun0 to the remote LAN.
If you are having trouble establishing the tunnel:
- First be sure you have configured ssh server side and port 22 is forwarded properly.
- Then work through /etc/network/interfaces on both client and server and make sure the ip address and routes (gateway) are set properly.
- Don't forget to set up NAT on the server.
- Did you add the server nameserver to the guest ?
I has a a problem when the ssh tunnel failed or is terminated unexpectedly. In that case the ifup and ifdown commands may not work and you may see error messages. In this event I have had to manually set the route on the client including the default gateway. Delete the server as a host if the tunnel is down.