4. Secure solution: piercing using ssh

4.1. Principle

Let's assume that your firewall administrator allows transparent TCP connections to some port on some server machine on the other side of the firewall (be it the standard SSH port 22, or an alternate destination port, like the HTTP port 80 or whatever), or that you somehow managed to get some port in one side of the firewall to get redirected to a port on the other side (using httptunnel, mailtunnel, some tunnel over telnet, or whatelse).

Then, you can run an sshd on the server side port, and connect to it with an ssh on the client side port. On both sides of the ssh connection, you run IP emulators ( pppd), and there you have your VPN, Virtual Public Network, that circumvents the stupid firewall limitations, with the added bonus of being encrypted for privacy (beware: the firewall administrator still knows the other end of the tunnel, and whatever authentication information you might have sent before to run ssh).

The exact same technology can be used to build a VPN, Virtual Private Network, whereby you securely join physical sites into a one logical network without sacrificing security with respect to the transport network between the sites.

4.2. A sample session

Below is a sample script for you to adapt to your needs. It uses the array feature of zsh, but you may easily adapt it to your favorite shell. Use option -p for ssh to try another port than port 22 (but then, be sure to run sshd on same port).

Note that the script supposes that ssh can login without your having to interactively type your password (indeed, it's controlling tty will be connected to pppd, so if it asks for a password, you lose). This can be done either by ssh keys in your ˜/.ssh/authorized_keys that either do not require a password, or that you unlock using ssh-agent or ssh-askpass. See your SSH documentation. Actually, you might also use a chat script to enter your password, but this is definitely not the Right Thing.

If you are not root on the server end, or simply if want to screen your client's network from outbound connections, you can use slirp instead of pppd as the server's PPP emulator. Just uncomment the relevant line.

#!/bin/zsh -f
SERVER_ACCOUNT=root@server.fqdn.tld
SERVER_PPPD="pppd ipcp-accept-local ipcp-accept-remote"
#SERVER_PPPD="pppd" ### This usually suffices if it's in /usr/sbin/
#SERVER_PPPD="/home/joekluser/bin/slirp ppp"
CLIENT_PPPD=( pppd
	silent
	10.0.2.15:10.0.2.2
	### For debugging purposes, you may uncomment the following:
	# updetach debug
	### Another potentially useful option (see section on Routing):
	# defaultroute
)
$CLIENT_PPPD pty "ssh -t $SERVER_ACCOUNT $SERVER_PPPD"

Note that default options from your /etc/ppp/options or ˜/.slirprc may break this script, so remove any unwanted option from there.

Also note that 10.0.2.2 is the default setting for slirp, which might or not fit your specific setup. In any case, you should most likely be using some address in one of the ranges reserved by RFC 1918 for private networks: 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16. The firewall-protected LAN might already be using some of them, and avoiding clashes is your responsibility. For more customization, please read the appropriate documentation.

If your client's pppd is old or non-linux (e.g. BSD) and hasn't got the pty option, use
cotty -d -- $CLIENT_PPPD -- ssh -t $SERVER_ACCOUNT $SERVER_PPPD
Catches: don't put quotes around commands given to cotty, as they are just exec()'d as is, and don't forget to specify the full path for the server's pppd if it's not in the standard path setup by ssh.

Automatic reconnection is left as an exercise to the reader (hint: the nodetach option from pppd might help for that).