Posted by emeitner on Mon 2 Jul 2007 at 16:37
This article describes how to use the new tunneling features of OpenSSH V 4.3 to establish a VPN between two Debian or Debian-like systems. Note that by tunneling I am referring to layer-3 IP-in-SSH tunneling, not the TCP connection forwarding that most people refer to as tunneling.
When operational this VPN will allow you to route traffic from one computer to another network via an SSH connection.
This is a brief recipe rather than a "HOW-TO". It it assumed you are familiar with all of the basic concepts.
SSH V 4.3 introduced true layer-2 and layer-3 tunneling allowing easy to configure VPNs that can be built upon existing SSH authentication mechanisms. The VPN configuration described below allows a client(or if you prefer the stupid term: road warrior) to connect to a firewall/server and access the entire private network that is behind it.
Previously I never allowed root login via SSH to any machines because I always logged in under a personal account and then used sudo. It made sense to turn off root logins via SSH(PermitRootLogin=no). With the advent of the new tunneling features there seems to be a need to have a limited root login for the purposes of establishing the SSH VPN. This is required because the user that connects to the sshd server must have the permissions to set up a tunnnel(tun) interface. Until OpenSSH allows non-root users to do so (such as: http://marc.info/?l=openssh-unix-dev&m=115651728700190&w=2) we will have to do it this way.
OpenSSH also has a few features to allow for easily tearing down an SSH connection without having to track all sorts of PIDs and such. I use the control connection feature to do so. See the SSH man page for these switches: -M -O -S. This allows one to use the ifup and ifdown commands to easily control the SSH VPN.
In this recipe two machines will be configured:
-------- /\_/-\/\/-\ -----------------
| Client |~~~~~~~/ Internet /~~~~~~| Server/Firewall |~~~[ private net ]
-------- \_/-\/\_/\/ / ----------------- \
||\ \ ||\ \
|| {tun0} {eth0} || {tun0} {eth1}
|| ||
\-================= tunnel ==============-/
For this recipe lets number things like this:
If you do not already have them, generate an SSH keypair for root:
$ sudo ssh-keygen -t rsa
/etc/network/interfaces: Add this stanza to the file:
iface tun0 inet static
pre-up ssh -S /var/run/ssh-myvpn-tunnel-control -M -f -w 0:0 5.6.7.8 true
pre-up sleep 5
address 10.254.254.2
pointopoint 10.254.254.1
netmask 255.255.255.252
up route add -net 10.99.99.0 netmask 255.255.255.0 gw 10.254.254.1 tun0
post-down ssh -S /var/run/ssh-myvpn-tunnel-control -O exit 5.6.7.8
The first time we connect to the server as root we may need to acknowledge saving the servers SSH key fingerprint:
$ sudo ssh 5.6.7.8 The authenticity of host '5.6.7.8 (5.6.7.8)' can't be established. RSA key fingerprint is aa:fe:a0:38:7d:11:78:60:01:b0:80:78:90:ab:6a:d2. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '5.6.7.8' (RSA) to the list of known hosts.
Don't bother logging in, just hit CTRL-C.
/etc/ssh/sshd_config: Add/modify the two keywords to have the same values as below.
PermitTunnel point-to-point PermitRootLogin forced-commands-only
The PermitRootLogin line is changed from the default of no. You do restrict root SSH login, right?
/root/.ssh/authorized_keys: Add the following line.
tunnel="0",command="/sbin/ifdown tun0;/sbin/ifup tun0" ssh-rsa AAAA ..snipped.. == root@server
Replace everything starting with "ssh-rsa" with the contents of root's public SSH key from the client(/root/.ssh/id_rsa.pub on the client).
/etc/network/interfaces: Add the following stanza.
iface tun0 inet static
address 10.254.254.1
netmask 255.255.255.252
pointopoint 10.254.254.2
/etc/sysctl.conf: Make sure net.ipv4.conf.default.forwarding is set to 1
net.ipv4.conf.default.forwarding=1
This will take effect upon the next reboot so make it active now:
$ sudo sysctl net.ipv4.conf.default.forwarding=1
user@client:~$ sudo ifup tun0 RTNETLINK answers: File exists run-parts: /etc/network/if-up.d/avahi-autoipd exited with return code 2 user@client:~$ ping -c 2 10.99.99.1 PING 10.99.99.1 (10.99.99.1) 56(84) bytes of data. 64 bytes from 10.99.99.1 icmp_seq=1 ttl=64 time=96.3 ms 64 bytes from 10.99.99.1 icmp_seq=2 ttl=64 time=94.9 ms --- 10.99.99.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 999ms rtt min/avg/max/mdev = 94.954/95.670/96.387/0.780 ms user@client:~$ sudo ifdown tun0 Exit request sent.
You may get the two errors after running ifup. No problem, they are harmless.
Once you have this running it is fairly easy to route traffic between two networks on each end of the VPN. See the first reference link below for details.
man sshman ssh_configman sshd_configman interfaces
iface tun-ssh- inet static
pre-up tunctl -u -t tun-ssh-
address 10.254.254.1
netmask 255.255.255.252
pointopoint 10.254.254.2
post-down tunctl -d tun-ssh-
iface tun0 inet static
pre-up tunctl -u -t tun0
address 10.254.254.1
netmask 255.255.255.252
pointopoint 10.254.254.2
post-down tunctl -d tun0
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
cat /proc/sys/net/ipv4/ip_forwardshould return 1. If not you need to make sure net.ipv4.conf.default.forwarding was set properly. Run
ip route liston your client. You should see an entry like:
192.168.1.0/24 via 10.254.254.1 dev tun0
[ Parent ]
[ Parent ]
-oServerAliveInterval=60To the ssh command in the pre-up stanza in /etc/network/interfaces on the client.
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ View Weblogs ]
[ Parent ]