mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-01-18 20:08:14 +01:00
iptables: try launching with sudo, then su, then directly.
Previous versions depended on having 'sudo' in your PATH. Now that we can feel safe that --iptables will clean up properly when you exit, and it doesn't need to authenticate twice, the advantages of sudo aren't strictly needed. Good old 'su' is a reasonable fallback - and everybody has it, which is nice. Unfortunately su doesn't let you redirect stdin, so I had to play a stupid fd trick to make it work.
This commit is contained in:
parent
7d674e9e37
commit
2c2bea80bc
50
client.py
50
client.py
@ -18,13 +18,38 @@ class IPTables:
|
||||
self.port = port
|
||||
self.subnets = subnets
|
||||
subnets_str = ['%s/%d' % (ip,width) for ip,width in subnets]
|
||||
self.argv = (['sudo', sys.argv[0]] +
|
||||
['-v'] * (helpers.verbose or 0) +
|
||||
['--iptables', str(port)] + subnets_str)
|
||||
self.p = subprocess.Popen(self.argv,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
line = self.p.stdout.readline()
|
||||
argvbase = ([sys.argv[0]] +
|
||||
['-v'] * (helpers.verbose or 0) +
|
||||
['--iptables', str(port)] + subnets_str)
|
||||
argv_tries = [
|
||||
['sudo'] + argvbase,
|
||||
['su', '-c', ' '.join(argvbase)],
|
||||
argvbase
|
||||
]
|
||||
|
||||
# we can't use stdin/stdout=subprocess.PIPE here, as we normally would,
|
||||
# because stupid Linux 'su' requires that stdin be attached to a tty.
|
||||
# Instead, attach a *bidirectional* socket to its stdout, and use
|
||||
# that for talking in both directions.
|
||||
(s1,s2) = socket.socketpair()
|
||||
def setup():
|
||||
# run in the child process
|
||||
s2.close()
|
||||
e = None
|
||||
for argv in argv_tries:
|
||||
try:
|
||||
self.p = subprocess.Popen(argv, stdout=s1, preexec_fn=setup)
|
||||
e = None
|
||||
break
|
||||
except OSError, e:
|
||||
pass
|
||||
self.argv = argv
|
||||
s1.close()
|
||||
self.pfile = s2.makefile('wb+')
|
||||
if e:
|
||||
log('Spawning iptables: %r\n' % self.argv)
|
||||
raise Fatal(e)
|
||||
line = self.pfile.readline()
|
||||
self.check()
|
||||
if line != 'READY\n':
|
||||
raise Fatal('%r expected READY, got %r' % (self.argv, line))
|
||||
@ -35,12 +60,15 @@ class IPTables:
|
||||
raise Fatal('%r returned %d' % (self.argv, rv))
|
||||
|
||||
def start(self):
|
||||
self.p.stdin.write('GO\n')
|
||||
self.p.stdin.flush()
|
||||
self.pfile.write('GO\n')
|
||||
self.pfile.flush()
|
||||
line = self.pfile.readline()
|
||||
self.check()
|
||||
if line != 'STARTED\n':
|
||||
raise Fatal('%r expected STARTED, got %r' % (self.argv, line))
|
||||
|
||||
def done(self):
|
||||
self.p.stdin.close()
|
||||
self.p.stdout.close()
|
||||
self.pfile.close()
|
||||
rv = self.p.wait()
|
||||
if rv:
|
||||
raise Fatal('cleanup: %r returned %d' % (self.argv, rv))
|
||||
|
19
iptables.py
19
iptables.py
@ -11,7 +11,7 @@ def chain_exists(name):
|
||||
return True
|
||||
rv = p.wait()
|
||||
if rv:
|
||||
raise Exception('%r returned %d' % (argv, rv))
|
||||
raise Fatal('%r returned %d' % (argv, rv))
|
||||
|
||||
|
||||
def ipt(*args):
|
||||
@ -19,7 +19,7 @@ def ipt(*args):
|
||||
debug1('>> %s\n' % ' '.join(argv))
|
||||
rv = subprocess.call(argv)
|
||||
if rv:
|
||||
raise Exception('%r returned %d' % (argv, rv))
|
||||
raise Fatal('%r returned %d' % (argv, rv))
|
||||
|
||||
|
||||
def do_it(port, subnets):
|
||||
@ -66,6 +66,14 @@ def main(port, subnets):
|
||||
assert(port > 0)
|
||||
assert(port <= 65535)
|
||||
|
||||
if os.getuid() != 0:
|
||||
raise Fatal('you must be root (or enable su/sudo) to set up iptables')
|
||||
|
||||
# because of limitations of the 'su' command, the *real* stdin/stdout
|
||||
# are both attached to stdout initially. Clone stdout into stdin so we
|
||||
# can read from it.
|
||||
os.dup2(1, 0)
|
||||
|
||||
debug1('iptables manager ready.\n')
|
||||
sys.stdout.write('READY\n')
|
||||
sys.stdout.flush()
|
||||
@ -77,14 +85,17 @@ def main(port, subnets):
|
||||
# we wait until we get some input before creating the rules. That way,
|
||||
# sshuttle can launch us as early as possible (and get sudo password
|
||||
# authentication as early in the startup process as possible).
|
||||
sys.stdin.readline()
|
||||
sys.stdin.readline(128)
|
||||
try:
|
||||
do_it(port, subnets)
|
||||
|
||||
sys.stdout.write('STARTED\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
# Now we wait until EOF or any other kind of exception. We need
|
||||
# to stay running so that we don't need a *second* password
|
||||
# authentication at shutdown time - that cleanup is important!
|
||||
while sys.stdin.readline():
|
||||
while sys.stdin.readline(128):
|
||||
pass
|
||||
finally:
|
||||
do_it(port, [])
|
||||
|
Loading…
Reference in New Issue
Block a user