summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAvery Pennarun <apenwarr@gmail.com>2010-05-04 23:21:16 -0400
committerAvery Pennarun <apenwarr@gmail.com>2010-05-04 23:42:36 -0400
commit8fe3592be3fafb04d12f056a264c73cb846c1a0e (patch)
treebf2bb55632fba90c22318ba7706c37b4ef64ac6c
parentba19d9c72d86436a405d4f544f75c4d3dc76ab05 (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.md15
-rw-r--r--assembler.py26
-rw-r--r--server.py16
-rw-r--r--ssh.py64
-rw-r--r--ssnet.py3
5 files changed, 87 insertions, 37 deletions
diff --git a/README.md b/README.md
index 92a1d1d..b86c4ff 100644
--- a/README.md
+++ b/README.md
@@ -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()
diff --git a/server.py b/server.py
index ceb33b2..1da22a1 100644
--- a/server.py
+++ b/server.py
@@ -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),
diff --git a/ssh.py b/ssh.py
index 3c83e2b..458720c 100644
--- a/ssh.py
+++ b/ssh.py
@@ -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
diff --git a/ssnet.py b/ssnet.py
index c7d78f9..9db8c40 100644
--- a/ssnet.py
+++ b/ssnet.py
@@ -1,5 +1,6 @@
import struct, socket, errno, select
-from helpers import *
+if not globals().get('skip_imports'):
+ from helpers import *
HDR_LEN = 8