Simple network to network VPN with OpenSSH and tun device

For a long time, I used combination of proxy.pac and DynamicForward in .ssh/config to enable seamless surfing over ssh tunnels. However, this time, I wanted to access university network from home DSL.

I remembered all warnings about tunneling tcp over tcp and why it is bad idea, but I really liked simplicity of reusing ssh. So, I start looking for guides to implement minimal solution like this:

client.lan                      ssh gateway             private network
eth0 <--DSL-nat--> eth0
tun0 <-pointopoint-> tun0 
                                eth1 <----->
It's a bit more complicated, because LAN clients have to use NAT to access private network, but that's a single line in configuration. So, let's get started...


  1. Generate ssh key for VPN
    root@client:~# ssh-keygen -t rsa -f /root/.ssh/gw-tun0
    Generating public/private rsa key pair.
    Enter passphrase (empty for no passphrase): 
    Enter same passphrase again: 
    Your identification has been saved in /root/.ssh/gw-tun0.
    Your public key has been saved in /root/.ssh/
  2. Transfer it to server
    echo 'tunnel="0",command="/sbin/ifdown tun0;/sbin/ifup tun0"' `cat /root/.ssh/` | \
        ssh 'sudo sh -c "cat >> /root/.ssh/authorized_keys"'
    If you didn't have authorized_keys on you will also have to fix group with:
    root@gw:~# chown root:root /root/.ssh/authorized_keys
  3. /etc/network/interfaces
    iface tun0 inet static
        pre-up ssh -i /root/.ssh/gw-tun0 -S /var/run/gw-tun0 -M -f -w 0:0 true
        pre-up sleep 5
        up iptables -t nat -A POSTROUTING -s -o tun0 -j MASQUERADE
        post-down ssh -S /var/run/gw-tun0 -O exit
    iptables are used to NAT all hosts on home network, so they can see whole private network and not just IPs on
    I also added following route to my home network gateway so that all traffic for private network will go through VPN:
    # netstat -rn
    Kernel IP routing table
    Destination     Gateway         Genmask

  1. /etc/ssh/sshd_config
    #PermitRootLogin yes
    PermitTunnel point-to-point
    PermitRootLogin forced-commands-only
  2. /etc/network/interfaces
    iface tun0 inet static
        up arp -sD eth1 pub
    arp entry is used to make home IP address visible to all hosts in private network. That, together with rinetd on client.lan enabled me to tunnel connections to other hosts on my local network by creating port redirection (again, sigh!). I could have solved that with route on ssh gateway, but this solution leaves configuration of incomming connections on home side of link.
  3. /etc/cron.d/
    # monitor tun0
    */1     * * * * root    fping -q || ( ifdown tun0 ; ifup tun0 ) | logger -t tun0