OpenSSH 4.3 VPN Example

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
Replies
Thank you for this article! I've been trying to figure this out from: http://gentoo-wiki.com/HOWTO_VPN_over_SSH_and_tun
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
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.
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.
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.
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
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
I needed to do two things to make this work:
-
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"
-
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!
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 """