diff options
author | Avery Pennarun <apenwarr@gmail.com> | 2010-05-04 23:21:16 -0400 |
---|---|---|
committer | Avery Pennarun <apenwarr@gmail.com> | 2010-05-04 23:42:36 -0400 |
commit | 8fe3592be3fafb04d12f056a264c73cb846c1a0e (patch) | |
tree | bf2bb55632fba90c22318ba7706c37b4ef64ac6c | |
parent | ba19d9c72d86436a405d4f544f75c4d3dc76ab05 (diff) |
Don't require the remote server to have sshuttle installed.sshuttle-0.20
Instead, grab our source code, send it over the link, and have python eval
it and then start the server.py main() function.
Strangely, there's now *less* horrible stuff in ssh.py, because we no longer
have to munge around with the PATH environment variable. And this
significantly reduces the setup required to get sshuttle going.
Based on a suggestion from Wayne Scott.
-rw-r--r-- | README.md | 15 | ||||
-rw-r--r-- | assembler.py | 26 | ||||
-rw-r--r-- | server.py | 16 | ||||
-rw-r--r-- | ssh.py | 64 | ||||
-rw-r--r-- | ssnet.py | 3 |
5 files changed, 87 insertions, 37 deletions
@@ -36,11 +36,17 @@ Prerequisites - sudo, su, or logged in as root on your client machine. (The server doesn't need admin access.) - - Linux+iptables on your client machine, including at + - If you use Linux on your client machine: + iptables installed on the client, including at least the iptables DNAT, REDIRECT, and ttl modules. - This is available by default on most Linux distributions. + These are installed by default on most Linux distributions. (The server doesn't need iptables and doesn't need to be Linux.) + + - If you use MacOS or BSD on your client machine: + Your kernel needs to be compiled with IPFIREWALL_FORWARD + (MacOS has this by default) and you need to have ipfw + available. (The server doesn't need to be MacOS or BSD.) This is how you use it: @@ -58,6 +64,11 @@ That's it! Now your local machine can access the remote network as if you were right there! And if your "client" machine is a router, everyone on your local network can make connections to your remote network. +You don't need to install sshuttle on the remote server; +the remote server just needs to have python available. +sshuttle will automatically upload and run its source code +to the remote python interpreter. + This creates a transparent proxy server on your local machine for all IP addresses that match 0.0.0.0/0. (You can use more specific IP addresses if you want; use any number of IP addresses or subnets to change which diff --git a/assembler.py b/assembler.py new file mode 100644 index 0000000..1dce7c2 --- /dev/null +++ b/assembler.py @@ -0,0 +1,26 @@ +import sys, zlib + +z = zlib.decompressobj() +mainmod = sys.modules[__name__] +while 1: + name = sys.stdin.readline().strip() + if name: + nbytes = int(sys.stdin.readline()) + if verbosity >= 2: + sys.stderr.write('remote assembling %r (%d bytes)\n' + % (name, nbytes)) + content = z.decompress(sys.stdin.read(nbytes)) + exec compile(content, name, "exec") + + # FIXME: this crushes everything into a single module namespace, + # then makes each of the module names point at this one. Gross. + assert(name.endswith('.py')) + modname = name[:-3] + mainmod.__dict__[modname] = mainmod + else: + break + +verbose = verbosity +sys.stderr.flush() +sys.stdout.flush() +main() @@ -1,18 +1,20 @@ import struct, socket, select -import ssnet, helpers -from ssnet import SockWrapper, Handler, Proxy, Mux, MuxWrapper -from helpers import * +if not globals().get('skip_imports'): + import ssnet, helpers + from ssnet import SockWrapper, Handler, Proxy, Mux, MuxWrapper + from helpers import * def main(): - # synchronization header - sys.stdout.write('SSHUTTLE0001') - sys.stdout.flush() - if helpers.verbose >= 1: helpers.logprefix = ' s: ' else: helpers.logprefix = 'server: ' + + # synchronization header + sys.stdout.write('SSHUTTLE0001') + sys.stdout.flush() + handlers = [] mux = Mux(socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM), @@ -1,7 +1,20 @@ -import sys, os, re, subprocess, socket +import sys, os, re, subprocess, socket, zlib import helpers from helpers import * + +def readfile(name): + basedir = os.path.dirname(os.path.abspath(sys.argv[0])) + fullname = os.path.join(basedir, name) + return open(fullname, 'rb').read() + + +def empackage(z, filename): + content = z.compress(readfile(filename)) + content += z.flush(zlib.Z_SYNC_FLUSH) + return '%s\n%d\n%s' % (filename,len(content), content) + + def connect(rhostport): main_exe = sys.argv[0] l = (rhostport or '').split(':', 1) @@ -9,45 +22,42 @@ def connect(rhostport): portl = [] if len(l) > 1: portl = ['-p', str(int(l[1]))] - nicedir = os.path.split(os.path.abspath(main_exe))[0] - nicedir = re.sub(r':', "_", nicedir) - myhome = os.path.expanduser('~') + '/' - if nicedir.startswith(myhome): - nicedir2 = nicedir[len(myhome):] - else: - nicedir2 = nicedir + if rhost == '-': rhost = None + + z = zlib.compressobj(1) + content = readfile('assembler.py') + content2 = (empackage(z, 'helpers.py') + + empackage(z, 'ssnet.py') + + empackage(z, 'server.py') + + "\n") + + pyscript = r""" + import sys; + skip_imports=1; + verbosity=%d; + exec compile(sys.stdin.read(%d), "assembler.py", "exec") + """ % (helpers.verbose or 0, len(content)) + pyscript = re.sub(r'\s+', ' ', pyscript.strip()) + + if not rhost: - argv = ['sshuttle', '--server'] + ['-v']*(helpers.verbose or 0) + argv = ['python', '-c', pyscript] else: - # WARNING: shell quoting security holes are possible here, so we - # have to be super careful. We have to use 'sh -c' because - # csh-derived shells can't handle PATH= notation. We can't - # set PATH in advance, because ssh probably replaces it. We - # can't exec *safely* using argv, because *both* ssh and 'sh -c' - # allow shellquoting. So we end up having to double-shellquote - # stuff here. - escapedir = re.sub(r'([^\w/])', r'\\\\\\\1', nicedir) - escapedir2 = re.sub(r'([^\w/])', r'\\\\\\\1', nicedir2) - cmd = r""" - sh -c PATH=%s:'$HOME'/%s:'$PATH exec sshuttle --server%s' - """ % (escapedir, escapedir2, - ' -v' * (helpers.verbose or 0)) - argv = ['ssh'] + portl + [rhost, '--', cmd.strip()] - debug2('executing: %r\n' % argv) + argv = ['ssh'] + portl + [rhost, '--', "python -c '%s'" % pyscript] (s1,s2) = socket.socketpair() def setup(): # runs in the child process s2.close() - if not rhost: - os.environ['PATH'] = ':'.join([nicedir, - os.environ.get('PATH', '')]) os.setsid() s1a,s1b = os.dup(s1.fileno()), os.dup(s1.fileno()) s1.close() + debug2('executing: %r\n' % argv) p = subprocess.Popen(argv, stdin=s1a, stdout=s1b, preexec_fn=setup, close_fds=True) os.close(s1a) os.close(s1b) + s2.sendall(content) + s2.sendall(content2) return p, s2 @@ -1,5 +1,6 @@ import struct, socket, errno, select -from helpers import * +if not globals().get('skip_imports'): + from helpers import * HDR_LEN = 8 |