mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-01-19 04:18:23 +01:00
Don't require the remote server to have sshuttle installed.
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.
This commit is contained in:
parent
ba19d9c72d
commit
8fe3592be3
15
README.md
15
README.md
@ -36,12 +36,18 @@ 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
|
||||
|
26
assembler.py
Normal file
26
assembler.py
Normal file
@ -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()
|
16
server.py
16
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),
|
||||
|
64
ssh.py
64
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
|
||||
|
Loading…
Reference in New Issue
Block a user