Compare commits

...

6 Commits

Author SHA1 Message Date
f1b33dab29 Add a --exclude option for excluding subnets from routing.
Also, add 127.0.0.0/8 to the default list of excludes.  If you want to route
0/0, you almost certainly *don't* want to route localhost to the remote ssh
server's localhost!

Thanks to Edward for the suggestion.
2010-07-15 14:13:33 -04:00
3a25f709e5 log(): don't abort if we fail to write to stderr.
Failing to write to the log sucks, but not as much as failing to clean up
just because stderr disappeared.  So let's catch any IOError exception from
log() and just ignore it.

This should fix a problem reported by Camille Moncelier, which is that
sshuttle firewall entries stick around if your tty dies strangely (eg. your
X server aborts for some reason).
2010-05-16 17:57:18 -04:00
a8b3d69856 ssh.py: try harder to find required *.py files.
Search the entire python sys.path, not just the directory that argv[0] is
in.  That way if you symlink the sshuttle binary into (for example) ~/bin,
it'll be able to work correctly.
2010-05-12 13:53:14 -04:00
2d4f6a4308 client: add a debug1() message for connecting/connected.
If the server is going to delay us, we'd at least like to know that.
2010-05-11 19:04:44 -04:00
d435ed837d Created a googlegroups.com mailing list for sshuttle. 2010-05-11 15:30:53 -04:00
2d77403a0b Don't use try/except/finally so that python 2.4 works.
Use try/(try/except)/finally instead.  There was only once case of this.

Thanks to Wayne Scott and nisc for pointing this out.
2010-05-10 13:58:52 -04:00
7 changed files with 77 additions and 36 deletions

View File

@ -161,3 +161,6 @@ later. You're welcome.
-- --
Avery Pennarun <apenwarr@gmail.com> Avery Pennarun <apenwarr@gmail.com>
Mailing list:
Subscribe by sending a message to <sshuttle+subscribe@googlegroups.com>
List archives are at: http://groups.google.com/group/sshuttle

View File

@ -20,10 +20,11 @@ def original_dst(sock):
class FirewallClient: class FirewallClient:
def __init__(self, port, subnets): def __init__(self, port, subnets_include, subnets_exclude):
self.port = port self.port = port
self.auto_nets = [] self.auto_nets = []
self.subnets = subnets self.subnets_include = subnets_include
self.subnets_exclude = subnets_exclude
argvbase = ([sys.argv[0]] + argvbase = ([sys.argv[0]] +
['-v'] * (helpers.verbose or 0) + ['-v'] * (helpers.verbose or 0) +
['--firewall', str(port)]) ['--firewall', str(port)])
@ -67,8 +68,10 @@ class FirewallClient:
def start(self): def start(self):
self.pfile.write('ROUTES\n') self.pfile.write('ROUTES\n')
for (ip,width) in self.subnets+self.auto_nets: for (ip,width) in self.subnets_include+self.auto_nets:
self.pfile.write('%s,%d\n' % (ip, width)) self.pfile.write('%d,0,%s\n' % (width, ip))
for (ip,width) in self.subnets_exclude:
self.pfile.write('%d,1,%s\n' % (width, ip))
self.pfile.write('GO\n') self.pfile.write('GO\n')
self.pfile.flush() self.pfile.flush()
line = self.pfile.readline() line = self.pfile.readline()
@ -96,6 +99,7 @@ def _main(listener, fw, use_server, remotename, seed_hosts, auto_nets):
helpers.logprefix = 'c : ' helpers.logprefix = 'c : '
else: else:
helpers.logprefix = 'client: ' helpers.logprefix = 'client: '
debug1('connecting to server...\n')
(serverproc, serversock) = ssh.connect(remotename) (serverproc, serversock) = ssh.connect(remotename)
mux = Mux(serversock, serversock) mux = Mux(serversock, serversock)
handlers.append(mux) handlers.append(mux)
@ -110,6 +114,7 @@ def _main(listener, fw, use_server, remotename, seed_hosts, auto_nets):
if initstring != expected: if initstring != expected:
raise Fatal('expected server init string %r; got %r' raise Fatal('expected server init string %r; got %r'
% (expected, initstring)) % (expected, initstring))
debug1('connected.\n')
def onroutes(routestr): def onroutes(routestr):
if auto_nets: if auto_nets:
@ -183,7 +188,8 @@ def _main(listener, fw, use_server, remotename, seed_hosts, auto_nets):
mux.check_fullness() mux.check_fullness()
def main(listenip, use_server, remotename, seed_hosts, auto_nets, subnets): def main(listenip, use_server, remotename, seed_hosts, auto_nets,
subnets_include, subnets_exclude):
debug1('Starting sshuttle proxy.\n') debug1('Starting sshuttle proxy.\n')
listener = socket.socket() listener = socket.socket()
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@ -210,7 +216,7 @@ def main(listenip, use_server, remotename, seed_hosts, auto_nets, subnets):
listenip = listener.getsockname() listenip = listener.getsockname()
debug1('Listening on %r.\n' % (listenip,)) debug1('Listening on %r.\n' % (listenip,))
fw = FirewallClient(listenip[1], subnets) fw = FirewallClient(listenip[1], subnets_include, subnets_exclude)
try: try:
return _main(listener, fw, use_server, remotename, return _main(listener, fw, use_server, remotename,

View File

@ -43,14 +43,23 @@ def do_iptables(port, subnets):
ipt('-I', 'OUTPUT', '1', '-j', chain) ipt('-I', 'OUTPUT', '1', '-j', chain)
ipt('-I', 'PREROUTING', '1', '-j', chain) ipt('-I', 'PREROUTING', '1', '-j', chain)
# create new subnet entries # create new subnet entries. Note that we're sorting in a very
for snet,swidth in subnets: # particular order: we need to go from most-specific (largest swidth)
ipt('-A', chain, '-j', 'REDIRECT', # to least-specific, and at any given level of specificity, we want
'--dest', '%s/%s' % (snet,swidth), # excludes to come first. That's why the columns are in such a non-
'-p', 'tcp', # intuitive order.
'--to-ports', str(port), for swidth,sexclude,snet in sorted(subnets, reverse=True):
'-m', 'ttl', '!', '--ttl', '42' # to prevent infinite loops if sexclude:
) ipt('-A', chain, '-j', 'RETURN',
'--dest', '%s/%s' % (snet,swidth),
'-p', 'tcp')
else:
ipt('-A', chain, '-j', 'REDIRECT',
'--dest', '%s/%s' % (snet,swidth),
'-p', 'tcp',
'--to-ports', str(port),
'-m', 'ttl', '!', '--ttl', '42' # to prevent infinite loops
)
def ipfw_rule_exists(n): def ipfw_rule_exists(n):
@ -103,6 +112,7 @@ def ipfw(*args):
def do_ipfw(port, subnets): def do_ipfw(port, subnets):
sport = str(port) sport = str(port)
xsport = str(port+1)
# cleanup any existing rules # cleanup any existing rules
if ipfw_rule_exists(port): if ipfw_rule_exists(port):
@ -120,11 +130,16 @@ def do_ipfw(port, subnets):
'from', 'any', 'to', 'any', 'established') 'from', 'any', 'to', 'any', 'established')
# create new subnet entries # create new subnet entries
for snet,swidth in subnets: for swidth,sexclude,snet in sorted(subnets, reverse=True):
ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port, if sexclude:
'log', 'tcp', ipfw('add', sport, 'skipto', xsport,
'from', 'any', 'to', '%s/%s' % (snet,swidth), 'log', 'tcp',
'not', 'ipttl', '42') 'from', 'any', 'to', '%s/%s' % (snet,swidth))
else:
ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
'log', 'tcp',
'from', 'any', 'to', '%s/%s' % (snet,swidth),
'not', 'ipttl', '42')
def program_exists(name): def program_exists(name):
@ -228,10 +243,11 @@ def main(port):
elif line == 'GO\n': elif line == 'GO\n':
break break
try: try:
(ip,width) = line.strip().split(',', 1) (width,exclude,ip) = line.strip().split(',', 2)
except: except:
raise Fatal('firewall: expected route or GO but got %r' % line) raise Fatal('firewall: expected route or GO but got %r' % line)
subnets.append((ip, int(width))) subnets.append((int(width), bool(int(exclude)), ip))
try: try:
if line: if line:
debug1('firewall manager: starting transproxy.\n') debug1('firewall manager: starting transproxy.\n')
@ -258,7 +274,6 @@ def main(port):
raise Fatal('expected EOF, got %r' % line) raise Fatal('expected EOF, got %r' % line)
else: else:
break break
finally: finally:
try: try:
debug1('firewall manager: undoing changes.\n') debug1('firewall manager: undoing changes.\n')

View File

@ -4,9 +4,14 @@ logprefix = ''
verbose = 0 verbose = 0
def log(s): def log(s):
sys.stdout.flush() try:
sys.stderr.write(logprefix + s) sys.stdout.flush()
sys.stderr.flush() sys.stderr.write(logprefix + s)
sys.stderr.flush()
except IOError:
# this could happen if stderr gets forcibly disconnected, eg. because
# our tty closes. That sucks, but it's no reason to abort the program.
pass
def debug1(s): def debug1(s):
if verbose >= 1: if verbose >= 1:

View File

@ -53,6 +53,7 @@ l,listen= transproxy to this ip address and port number [default=0]
H,auto-hosts scan for remote hostnames and update local /etc/hosts H,auto-hosts scan for remote hostnames and update local /etc/hosts
N,auto-nets automatically determine subnets to route N,auto-nets automatically determine subnets to route
r,remote= ssh hostname (and optional username) of remote sshuttle server r,remote= ssh hostname (and optional username) of remote sshuttle server
x,exclude= exclude this subnet (can be used more than once)
v,verbose increase debug message verbosity v,verbose increase debug message verbosity
seed-hosts= with -H, use these hostnames for initial scan (comma-separated) seed-hosts= with -H, use these hostnames for initial scan (comma-separated)
noserver don't use a separate server process (mostly for debugging) noserver don't use a separate server process (mostly for debugging)
@ -79,6 +80,11 @@ try:
else: else:
if len(extra) < 1 and not opt.auto_nets: if len(extra) < 1 and not opt.auto_nets:
o.fatal('at least one subnet (or -N) expected') o.fatal('at least one subnet (or -N) expected')
includes = extra
excludes = ['127.0.0.0/8']
for k,v in flags:
if k in ('-x','--exclude'):
excludes.append(v)
remotename = opt.remote remotename = opt.remote
if remotename == '' or remotename == '-': if remotename == '' or remotename == '-':
remotename = None remotename = None
@ -95,7 +101,8 @@ try:
remotename, remotename,
sh, sh,
opt.auto_nets, opt.auto_nets,
parse_subnets(extra))) parse_subnets(includes),
parse_subnets(excludes)))
except Fatal, e: except Fatal, e:
log('fatal: %s\n' % e) log('fatal: %s\n' % e)
sys.exit(99) sys.exit(99)

View File

@ -79,14 +79,15 @@ def start_hostwatch(seed_hosts):
# child # child
rv = 99 rv = 99
try: try:
s2.close() try:
os.dup2(s1.fileno(), 1) s2.close()
os.dup2(s1.fileno(), 0) os.dup2(s1.fileno(), 1)
s1.close() os.dup2(s1.fileno(), 0)
rv = hostwatch.hw_main(seed_hosts) or 0 s1.close()
except Exception, e: rv = hostwatch.hw_main(seed_hosts) or 0
log('%s\n' % _exc_dump()) except Exception, e:
rv = 98 log('%s\n' % _exc_dump())
rv = 98
finally: finally:
os._exit(rv) os._exit(rv)
s1.close() s1.close()

8
ssh.py
View File

@ -5,8 +5,12 @@ from helpers import *
def readfile(name): def readfile(name):
basedir = os.path.dirname(os.path.abspath(sys.argv[0])) basedir = os.path.dirname(os.path.abspath(sys.argv[0]))
fullname = os.path.join(basedir, name) path = [basedir] + sys.path
return open(fullname, 'rb').read() for d in path:
fullname = os.path.join(d, name)
if os.path.exists(fullname):
return open(fullname, 'rb').read()
raise Exception("can't find file %r in any of %r" % (name, path))
def empackage(z, filename): def empackage(z, filename):