Weekend Project time. Here's how to build a VPN using just SSH and two Ubuntu machines. At least one of these machines should have a static IP or DNS, and they should both be the default gateway for their respective sites.

MachineA is the originator and has a local network on NetworkA/24. MachineB has a static name on the Internet and a local network called NetworkB/24. Our point-to-point network is, with MachineA at and MachineB a I will assume for brevity that you have already established an SSH trust relationship between root@MachineA and root@MachineB.

auto tun0
iface tun0 inet static
      pre-up ssh -f -w 0:0 MachineB "ifdown tun0 ; ifup tun0"
      pre-up sleep 5
      up ip route add NetworkB/24 via
      down ip route del NetworkB/24 via
iface tun0 inet static
      up ip route add NetworkA/24 via
      down ip route del NetworkA/24 via

(Note that MachineA's interface is set to auto, but MachineB's is not.)

Since these are the gateways, we're probably running a netfilter wrapper such as Shorewall. In that case, we'll have a few more things to add. They'll be the same on both ends.

  • In /etc/shorewall/zones, add: vpn ipv4
  • In /etc/shorewall/interfaces, add: vpn tun0 detect
  • In /etc/shorewall/rules, add:
    ACCEPT vpn loc all
    ACCEPT loc vpn all
  • And finally, in /etc/modules, add tun

After restarting Shorewal and loading the tun device (modprobe tun && invoke-rc.d shorewall restart), you should now be able to run ifup tun0 on MachineA. Ta-da! You can now connect to your remote networks as if they were a single hop away.

This solution shares some problems with all TCP-only VPNs. Each relayed packet has a reduced payload size, so you'll see more fragmentation. There are also problems related to running Nagle on top of Nagle. Depending on your expected traffic, you might have some luck enabling compression. See ssh_config(5). There are also some problems more or less unique to SSH VPNs. If the MachineB's host key changes, MachineA won't be able to establish a connection until the old key is removed from MachineA:/root/.ssh/known_keys. This also makes failover scenarios pretty ugly. Good luck!