Dave Eckhardt's Venti IP-address Restriction Page




The problem

You would like to restrict access to a Venti archival storage server so that only callers from a specific list of IP addresses are allowed.

(Why would you want to do that? "Authenticating" callers by IP address is very dubious, but (as of mid-2012) Venti implementations don't include authentication, access control, or encryption--which isn't completely crazy, since callers need to know SHA-1 hashes of data in order to retrieve anything. Restricting by IP address doesn't provide any real confidentiality, but it does make it pretty tough for random network vandals to call you up and ask you to store/serve somebody else's copyrighted content, since they would need to spoof a quite-a-few-packets TCP connection.)

A semi-constraint

I was looking to tread lightly, e.g., not:

A near miss

It turns out that a utility called trampoline already exists to shuffle data back and forth between two sockets. What's more, it even has an option to see if callers are permitted according to the Plan 9 network database. However, the admission-control code in trampoline restricts access according to MAC address, which doesn't meet my needs.

The solution

I thought about slightly expanding trampoline--it really wouldn't be a lot of code. But I would have felt bad about submitting it to the distribution because restricting by IP address seems even sketchier than restricting by MAC address. Plus the deal-breaker was that after looking around for a while I didn't see any pre-existing syntax for specifying a list of IP address and/or host names, so I would have had to define my own format and sell people on the concept, the code, and the UI.

Then laziness struck: maybe I could wrap something temporary and throw-away around trampoline, such as a shell script. One of the pleasant things about Plan 9 is that, since "everything is a file", things that on Unix inherently require page after page of "sockets" code can often be done with a small shell script on Plan 9.

The solution has three parts:

  1. Install your Venti and set it up to take connections only from the local machine (listen on tcp!127.1!17034)
  2. Add one line to your server's startup script:
    aux/listen1 tcp!*!17034 /cfg/venti/restrict &
  3. Install the script below as (e.g.) /cfg/venti/restrict
#!/bin/rc

# Dave Eckhardt, 2012-05-29

rfork ne

fn log { msg=`{date}^' '^$0^' '^$1; echo $msg >> /sys/log/listen }
fn die { log $1 ; echo $1; exit $1 }

cd $net || die $net

ifs='!' remote=`{read remote}
~ $#remote 2 || die 'bad word count '^$#remote
ipaddr=$remote(1)

ifs='.
' bytes=`{echo $ipaddr}
~ $#bytes 4 || die 'bad byte count '^$#bytes
~ $bytes(1) [0-9] [0-9][0-9] [0-9][0-9][0-9] || die 'invalid ipaddr '^$ipaddr
~ $bytes(2) [0-9] [0-9][0-9] [0-9][0-9][0-9] || die 'invalid ipaddr '^$ipaddr
~ $bytes(3) [0-9] [0-9][0-9] [0-9][0-9][0-9] || die 'invalid ipaddr '^$ipaddr
~ $bytes(4) [0-9] [0-9][0-9] [0-9][0-9][0-9] || die 'invalid ipaddr '^$ipaddr

switch ($ipaddr) {
case 128.2.xxx.yy 128.2.zzz.ww
	log 'taking call from '^$ipaddr
	exec aux/trampoline tcp!127.0.0.1!17034
	die 'could not start aux/trampoline'
}

log 'rejected '^$ipaddr

Enjoy!

Future Work

The "right way" to do this probably involves tlssrv, but this is harder than it sounds:

  1. While tlsclient has code to verify that the server's certificate is trusted, tlssrv has no corresponding way to specify a list of trusted client certificates
  2. As it turns out, this is not simple to fix, because the tlsServer() library call doesn't provide the server with a client certificate (it could treat the cert field as in/out).
  3. In turn, that's because the server side of the underlying connection-negotiation code doesn't ask the client for a certificate.

...and that's the point where I said "Gee, a shell script to do the wrong thing seems likely to take a lot less time than two-plus weeks of TLS hacking would".



Best viewed with any browser Proud Donor
davide+receptionist@cs.cmu.edu