EVOLVE: Establish a VPN in One Line, Very Easily

Evolve is a silly acronym for something I figured out how to do one day. Other people have done this countless times, but I thought I'd document the things I learned doing it.

I established a Virtual Private Network (VPN) between two Linux hosts with ssh and pppd. This technique should work between any two Unix/POSIX systems, but the options available to pppd may vary.

'pppd' is a program that takes an TCP/IP stack and squeezes it through a sequential data stream, normally a modem. 'ssh' creates a network connection that is authenticated and encrypted. If you string these together, you can create a virtual network interface that connects any two computers that can communicate via ssh. This is useful for privacy reasons (traffic between the endpoints is encrypted), and for firewall-avoiding convenience (no ports are blocked on the virtual interface). If you can ssh to your target server, but can't run VNC because of the firewall, a quick-and-dirty VPN can solve that.

Let's describe the process in terms of connecting a 'client' (in this case my laptop at work) to a 'server' (in this case my workstation at home - www.risacher.org). The client machine is behind a firewall that does IP-masquerading (NAT). The firewall also only allows connections through on certain ports, in particular, the telnet port is open, but the ssh port is not. The server acts as its own firewall via iptables.

In order to get this to work...

  1. I needed to have 'pty_redir' on the client. As it turns out, this was part of the linuxconf package.
    'pty_redir' isn't well documented, if at all. What it appears to do is to allocate a pty (pseudo-terminal) and execute its arguments on that pty, while outputing the name of the pty device to its own stdout. As I note below, this is not needed for newer versions of pppd.
  2. I needed to install sudo and configure sudoers on the server. The critical line in sudoers was:
    magnus ALL= NOPASSWD: /usr/sbin/pppd,/usr/bin/pty-redir
    The NOPASSWD option is important because there is no way to type the password when running on the pty.
  3. I needed public key authentication for the ssh connection between client and server, for similar reasons - there's no way to type the password that ssh wants when running on the pty. This was accomplished by creating keys with 'ssh-keygen -d' and copying the resulting id_dsa.pub on the client to ~/.ssh/authorized_keys2 on the server. This file must have 644 permissions.
  4. The argument 'notty' for pppd was not in the VLAN mini-howto.
  5. For convenience, I also added lines to the hosts file for both the client and the server. I also have NAT enabled on the server, which allows me to use the encrypted channel as a primary network interface for the client by using the appropriate routing tables.

    So: originally the magic command, run as root on the client was:
    pppd `pty-redir /usr/bin/ssh -2 user@servername.org -p 23 sudo /usr/sbin/pppd notty passive` local 192.168.1.5:192.168.1.1

    Most people do not need to run SSH on port 23. I don't either, any more, but when I first tried this, I did because I didn't have control of the firewall.

    New versions of pppd have an option 'pty', which does the work of the pty-redir command in a much simpler fashion. If you are using a modern pppd, you can use this much simpler method. This would look like this:
    pppd pty '/usr/bin/ssh -2 user@servername.org sudo /usr/sbin/pppd notty passive' local 192.168.1.5:192.168.1.1

    I've seen some people writing that this is terrible, performance-wise, since you now have multiple TCP/IP stacks on top of one another, which can cause the exponential backoff algorithms in the protocols to do bad things. This is true, although I haven't tried to measure it. If you want a real VPN, I recommend that you use IPsec, but be prepared to spend some time, money, or both understanding it and setting it up. EVOLVE is great to put together a connection quickly. It's not suitable for production use.

    route add -net 216.175.0.0 netmask 255.255.0.0 gw 134.152.113.129
    route add -net 134.152.0.0 netmask 255.255.0.0 gw 134.152.113.129
    route add default gw risacher.vlan
    
    
    client:
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    192.168.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
    134.152.113.128 0.0.0.0         255.255.255.128 U     0      0        0 eth0
    134.152.0.0     134.152.113.129 255.255.0.0     UG    0      0        0 eth0
    216.175.0.0     134.152.113.129 255.255.0.0     UG    0      0        0 eth0
    127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        0 lo
    0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 ppp0
    

    Projects page, Dan Risacher