Update (Nov 22, 2003).

I've let this slide ever since I went back to graduate school. My free time (what little there is of it) has been spent banging on busybox and putting together my own distibution, I sort of let this project slide. (I do have new code to post, about 80% done. I should do that.)

I suspect I haven't gotten much mail about it because the trommello.org account died almost a year ago now. :)

Somebody named Mark Hewitt has requested this domain be transferred to him, although when I tried to reply sourceforge said the email account he mailed me from doesn't exist. So I guess I'll reply here, and say "I don't have enough information, could you tell me more about the new project"? (He wants to make a dynamic VPN gateway. I have no idea what that MEANS, but might I suggest "dvpng" as the Real Easy Way (tm) of getting a site up?)

If sourceforge transfers the domain without asking me, I probably won't fight it though. I have my own domain now (www.landley.net, and the corresponding new email address rob at landley.net), and could easily put my code under there. Ever since sourceforge's download system developed brain damage (download a url ending in .zip, and it gives you an html file asking you to pick a mirror. This broke many scripts, and they never gave any warning. Stupid...) I've largely moved my projects off sourceforge anyway. I'm going to continue to call this project dvpn, though.

By the way, here's some quick and dirty sample code of how I get my email from a machine behind a firewall (linksys router, actually) that has one externally-accessible port. (port 22 is forwarded to a machine behind the firewall that has netcat installed on it. Netcat is part of busybox, by the way, although there it's called nc.)

On my laptop, I fire off this program from /etc/rc3.d/S99local:

/home/root/hack.py 25 &
And the whole of that program is here. Then I just tell my laptop my pop server lives on, and life is good. Notice that there's some commented out code showing out how to call the "get original destination of redirected connection" call in there, which is the only hard part of doing the whole dvpn setup in python.

Dynamic Virtual Private Networking

The basic idea here is to combine ip masquerading and transparent proxying to transparently wrap each tcp/ip connection in an ssh session, which is spawned when the connection is opened and which exits automatically when the connection is closed. This can be done with a suprisingly small amount of code, is quite robust, and amazingly scalable. (A VPN with 65,000 gateways is easily possible.) Scalability is based on the number of active connections at each node, not on the total number of nodes in the VPN.

The implementation we use involves a couple dozen lines of firewall rules (iptables) and a small daemon (about 100 lines of C code, or python, or perl) that accepts forwarded connections and forks off an ssh process for each one. At the far end, ssh runs netcat to complete the connection.

The trick here is the call to look up the original destination of a connection that has been port forwarded to the vpn daemon, and you do so with the getsockopt(connection_fd,SOL_IP,SO_ORIGINAL_DST, &addr, &i) command.

Here's some example code showing that call in use:


Knowing which public IP address you need to ssh to to reach each VPN address range is something the daemon needs to look up in a config file. I put the info in /etc/hosts, using lines that look like this:	thursday	#!
That way, hosts in the vpn that are using the gateway as their nameserver can use "thursday" as a name and get the VPN address, which will be transparently routed. (Setting up a caching nameserver on the VPN gateways is also currently left as an exercise to the reader, but give me time to flesh the site out more...)

The firewall rules look something like this. Twiddle to match your setup. You can substitute the dummy0 interface for eth1 if your box is a leaf node rather than a router. (You probably want a smaller eth1 netmask in that case). Yes, it'll work fine in the 192.168.X.X address range, editing the firewall rules to make that happen is (currently) left as another exercise to the reader.

I need to write up a HOWTO and put up example code (I have all this running just fine here, but permission to GPL the implementation has been "real soon now" for about a year).

You need to run the daemon as a user account that's set up to ssh from box to box without a password, preferably one that goes into a chroot gaol that contains nothing but a staticaly linked copy of netcat. Setting up ssh to do this will eventually be part of the howto as well, if and when I get the thing written. In the meantime, "man sshd" has the info you need, although when cryptographers write man pages the result tends to look like it's encrypted anyway. (Hint: "ssh-keygen -d -f ~dvpn/.ssh/id_dsa", and then lookup "authorized_keys2" and "known_hosts2". The "2" may not be there if you've disabled ssh protocol version 1 in /etc/sshd, which is a good idea.)

Any daemons you run on the server itself you'll want to bind to eth1 so boxes within your subnet or from other subnets in the VPN can access it, but nobody outside the firewall can. (The only port you should expose to the outside world is sshd, and you can obfuscate that a little if you try.) The firewall rules help here (blocking non-sshd incoming connections), but it's still a good idea not to bind to anything you don't need to.

Fun daemons to run: dhcpd and a local copy of bind or djbdns to act as a cacheing name server. I've got configs and scripts to make that work together (and export the names in "/etc/hosts" through bind to the subnet... As I said, bug me to put more stuff up...

One advantage of this firewall method is that it can scale extremely well; it scales on the number of active connections in use, not on the number of subnets being bridged. In the 10.x address space you could bridge 65536 firewalls with subnets easily. (Try THAT with ppp encapsulation.) One downside is that it only works for TCP (not UDP), and that you always get a connection even to a nonexistent box or port (it's closed immediately if it can't go through, but the forwarder will always accept). Works fine for http, ftp, samba (although W2K is a bit broken and needs port 445 blocked in the firewall rules to connect to hosts that don't support encryption, which is mostly redundant through a VPN anyway so I just block it in my firwall rules).

UDP through this vpn could be made to work (I just haven't bothered: encrypting causes latency, you use UDP when low latency is more important than reliability. If somebody really WANTS to play doom through a VPN...) The connect always succeeds thing would be harder to change.

There's a ton of stuff I need to write up. If anybody is actually interested in USING this, email rob at landley.net.