OpenSSH 4.3 VPN Example  

ssh-vpn-diagram.png
OpenSSH 4.3 comes with TUN/TAP support. This means that you can establish an encrypted virtual tunnel between two computers. This tunnel can be used to establish a VPN between these two networks. In the sample network you can establish an SSH connection to 55.56.57.58 but not the other two machines because they're firewalled off. Using an SSH VPN tunnel you can gain access to that entire network (anything that 55.56.57.58 would have access to). To clarify this is not SSH port forwarding. This is full IP forwarding using a tunnel interface.

This is done by creating a tunnel between your home PC (1.2.3.4) and the network gateway PC (55.56.57.58). This is done with the -w command in SSH.

ssh -w0:0 55.56.57.58 This creates a tun0 interface on both ends of the SSH session. Once the tunnel is established you will need to put an IP on both sides of the tunnel using the following commands. Note: the PermitTunnel option must be turned on in your sshd_config file for this to work. # IP Address for your Home PC ifconfig tun0 10.0.2.1 netmask 255.255.255.252 # IP Address for the network gateway PC ifconfig tun0 10.0.2.2 netmask 255.255.255.252
At this point you should be able to ping both sides of the tunnel from both machines. Now a little Linux routing knowledge comes in handy. You'll need two route statements to do this. One to force access to the network gateway PC to go out eth0 (or whatever your output device is), and the other to tell it to use tun0 for access to the rest of that subnet.

route add -host 55.56.57.58 dev eth0 route add -net 55.56.57.58/24 dev tun0
Everything will route properly now, but the firewalled machines will not know how to get back to your home PC. A little NAT will fix that right up. You'll need to setup IP Forwarding and NAT on the network gateway PC to masquerade all requests from your home PC.

echo 1 > /proc/sys/net/ipv4/ip_forward /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Leave A Reply - 9 Replies
Replies
jimcooncat 2007-02-19 04:57am - No Email - Logged IP: 72.73.108.230

Thank you for this article! I've been trying to figure this out from: http://gentoo-wiki.com/HOWTO_VPN_over_SSH_and_tun

dirtbag 2008-03-25 06:56pm - No Email - Logged IP: 71.70.174.94

So, this is all fine and dandy, but how would you purpose to automate it? i.e. im trying to set up a lan-to-lan tunnel with this, something like 192.168.1.0/24---(router)-=-=-=-=innernets=-=-=-=-=(router)----192.168.2.0/24 and I have 2 linux boxes on each end where I wanna do openssh vpn between them and route like so route add -net 192.168.1.0/24 dev tun1 (on one side) route add -net 192.168.2.0/24 dev tun1 (on the other side)

but how to automate this so that it can be kicked off from just one of the servers.

-Dirtbag

Scott Baker 2008-03-25 07:55pm - No Email - Logged IP: 65.182.224.60

The easiest thing to do would be to setup OpenVPN. SSH VPNs are good, ONLY if it's your last option. They're a pain otherwise.

jyg 2008-07-21 03:29pm - No Email - Logged IP: 67.127.54.94

Actually this is FAR simpler than setting up OpenVPN. If you need a quick and dirty tunnel between networks, this is the simplest and quickest way to go.

kybur 2009-04-01 10:18am - No Email - Logged IP: 159.221.32.10

If you want to automate this, you could set up the network gateway to accept passwordless ssh connections from the home pc, using ~/.ssh/authorized_keys, and then your script can ssh over to make sure the tun/routing/iptables is set up correctly.

Juac 2010-03-23 11:14pm - No Email - Logged IP: 190.227.122.37

Actually i don't know which is more difficult, *swan/openvpn or this, but i'm sketching a script that only requires sudo in both machines. It isn't 100% complete yet, but i think tomorrow i'll finish it. Openssh 4 ever!!!

call this with sudo:

!/bin/bash

userhost='myuser@myremotehost' sshflags='-Ap myaltsshport -i /home/path/to/.ssh/private_key' vpn='10.0.0.0/24'

setup first free local tap device

ltap=0 while [ -n "$(ifconfig | grep tap$ltap)" ]; do ltap=$[ltap+1]; done tunctl -t tap$ltap ifconfig tap$ltap ${vpn%%?/}2/${vpn##/}

setup first remote local tap device

setuprnet="ssh $sshflags $userhost sudo 'bash -c \"rtap=0;while [ -n \\"\\$(ifconfig | grep tap\\$rtap)\\" ]; do rtap=\\$[rtap+1]; done; tunctl -b -t tap\\$rtap; ifconfig tap\\$rtap ${vpn%%?/}1/${vpn##/} up\"'"

the above ssh "preconect" must do more housekeeping: it must parse with sed sshd_config and allow root access (with pubkey only), then issue a sshd config reload

rtap=$(sh -c "$setuprnet") rtap=${rtap##tap}

bring vpn up

ssh $sshflags -w $ltap:$rtap -o Tunnel=ethernet $userhost

above command should reparse sshd_config, disable root access again and reload

also any remote routes, forwards and iptables required

the script also has to set up my own routes, and later disable everything remotely and locally. That would be a usable version but this works already though you have to do some housekeeping.

I tested this and could get access to my office network, use my sip extension to call my girl, drop my sucky ISP (it filters me badly) and go with my work as gateway, and give this access to all my local network :D :D :D

Please help me to improve this! And thanks 4 the article. It's amazing something as useful as this gets dismissed as a "class b" technique. I think i'll memorize the script and settings when i'm done. This is GOLD. It can really save you. Cheers

juac 2010-03-24 01:29pm - No Email - Logged IP: 190.136.29.17

final version:

!/bin/bash

prereqs:

remote host's sshd_config must have "PermitRootLogin=no", "AllowUsers user", and "PermitTunnel=yes"

"tunctl", in debians it is found in uml-utils, redhats another (dont remember but "yum provides tunctl" must tell)

remote user must be able to sudo-as-root

can opt by routing as in this case or soft bridge with brctl and you get full remote ethernet segment membership :D

that last i think i'll implement later as an option

other stuff to do is error checking, etcetc, this is just as came from the oven

userhost='user@host' sshflags='-Ap 2020 -i /path/to/some/authkey' vpn='10.0.0.0/24' rnet=192.168.40.0/24

START VPN

if [ "$1" == "start" ]; then echo setting up local tap ... ltap=$(tunctl -b) ifconfig $ltap ${vpn%%?/}2/${vpn##/} up

echo setting remote configuration and enabling root login ... rtap="ssh $sshflags $userhost sudo 'bash -c \"rtap=\\$(tunctl -b); echo \\$rtap; ifconfig \\$rtap ${vpn%%?/}1/${vpn##/} up; iptables -A FORWARD -i \\$rtap -j ACCEPT; iptables -A FORWARD -o \\$rtap -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -s ${vpn%%?/}2 -j SNAT --to \\$(ip r | grep $rnet | sed \\"s/^.src \(.\\$\)/\1/g\\"); sed -i -e \\"s/\(PermitRootLogin\).\\$/\1 without-password/g\\" -e \\"s/\(AllowUsers.*\)\\$/\1 root/g\\" /etc/ssh/sshd_config; /usr/sbin/sshd -t\"'" rtap=$(sh -c "$rtap")

echo setting up local routes ...

since my ISP sucks with transparent filters (i can't opt for another where i live), i'll just use my work net as gateway

ip r a $(ip r | grep default | sed "s/default/${userhost##@}/") ip r c default via ${vpn%%?/}1 dev $ltap

echo bringing up the tunnel and disabling root login ... ssh $sshflags -f -w ${ltap##tap}:${rtap##tap} -o Tunnel=ethernet -o ControlMaster=yes -o ControlPath=/root/.ssh/vpn-$userhost-l$ltap-r$rtap root@${userhost##@} bash -c "\"sed -i -e 's/(PermitRootLogin).\$/\1 no/g' -e 's/(AllowUsers.*) root\$/\1/g' /etc/ssh/sshd_config; /usr/sbin/sshd -t\""

echo connected.

STOP VPN

elif [ "$1" == "stop" ]; then echo searching control socket and determining configuration ... controlpath=$(echo /root/.ssh/vpn-$userhost)
ltap=${controlpath%%-rtap
} && ltap=tap${ltap##-ltap} rtap=${controlpath##rtap} && rtap=tap${rtap%%-*}

echo bringing the tunnel down ... ssh $sshflags -o ControlPath=$controlpath -O exit $userhost

echo restoring local routes ... ip r c default $(ip r | grep ${userhost##@} | sed "s/${userhost##@}(.$)/\1/g") ip r d ${userhost##@}

echo restoring remote configuration ... sh -c "ssh $sshflags $userhost sudo 'bash -c \"tunctl -d $rtap; iptables -D FORWARD -i $rtap -j ACCEPT; iptables -D FORWARD -o $rtap -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -s ${vpn%%?/}2 -j SNAT --to \$(ip r | grep $rnet | sed \"s/^.src (.*\$)/\1/g\")\"'"

echo deleting local tap ... tunctl -d $ltap

echo disconnected. fi

lglenn 2010-09-10 11:28am - No Email - Logged IP: 65.243.45.100

I needed to do two things to make this work:

  1. Be root on BOTH the client and server when making the ssh connection -- if you don't do that, you'll get a message that looks like this from ssh (assuming you ran it with the -v switch): "sys_tun_open: failed to open tunnel control interface: Permission denied"

  2. Change the route add -host command to: "route add -host dev tun0" (that is, I used tun0 instead of eth0). I don't know enough about routing to know why.

Once I did those, it worked like a charm!

Luca Invernizzi 2010-11-27 03:22pm - No Email - Logged IP: 128.111.48.6

Actually, you don't have to connect with a root account on the server, you just need to modify the sshd_config file on the server. """ To enable the tunnel device you need to add a setting to your sshd_config:

 PermitTunnel yes

"""

All content licensed under the Creative Commons License