diff --git a/sshuttle/assembler.py b/sshuttle/assembler.py index e944280..ccc672b 100644 --- a/sshuttle/assembler.py +++ b/sshuttle/assembler.py @@ -50,4 +50,4 @@ from sshuttle.server import main # noqa: E402 main(options.latency_control, options.latency_buffer_size, options.auto_hosts, options.to_nameserver, - options.auto_nets) + options.auto_nets, options.localhost_detector) diff --git a/sshuttle/client.py b/sshuttle/client.py index 08df678..03e53bc 100644 --- a/sshuttle/client.py +++ b/sshuttle/client.py @@ -7,6 +7,7 @@ import os import sys import base64 import platform +import tempfile import sshuttle.helpers as helpers import sshuttle.ssnet as ssnet @@ -602,6 +603,14 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename, handlers = [] debug1('Connecting to server...') + # The client creates this file. If the server sees the file, it + # will delete it before writing SSHUTTLE0001 back to the client. + # If the server sees the file, then the server can deduce that it + # is running on the same host as the client. If the client sees + # that the server deleted the file, then the client can deduce + # that it is running on the same host as the server. + (_, localhost_detector) = tempfile.mkstemp(prefix="sshuttle-localhost-") + try: (serverproc, rfile, wfile) = ssh.connect( ssh_cmd, remotename, python, @@ -612,7 +621,8 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename, latency_buffer_size=latency_buffer_size, auto_hosts=auto_hosts, to_nameserver=to_nameserver, - auto_nets=auto_nets)) + auto_nets=auto_nets, + localhost_detector=localhost_detector)) except socket.error as e: if e.args[0] == errno.EPIPE: debug3('Error: EPIPE: ' + repr(e)) @@ -724,6 +734,25 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename, log('Connected to server.') sys.stdout.flush() + # If the server couldn't delete our localhost_detector file, then + # the server is running on a different machine. + if os.path.exists(localhost_detector): + debug3("Client and server appear to be running on different machines.") + os.remove(localhost_detector) # cleanup + else: + # The client and server can't run on the same machine because + # the firewall rules can't distinguish between data the + # sshuttle server sends (which shouldn't be redirected through + # sshuttle) and the different connections applications make + # (which perhaps should be redirected through sshuttle). + # Previously we set the TTL of the packets the server sent to + # distinguish between the two, but this feature was removed + # since running the client and server on the same machine is + # only useful for debugging. + raise Fatal("Exiting because sshuttle client and server are " + "running on the same machine. The host specified " + "with the -r option must be a remote host.") + if daemon: daemonize() log('daemonizing (%s).' % _pidname) diff --git a/sshuttle/server.py b/sshuttle/server.py index 4350dfb..85d17a8 100644 --- a/sshuttle/server.py +++ b/sshuttle/server.py @@ -285,7 +285,7 @@ class UdpProxy(Handler): def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver, - auto_nets): + auto_nets, localhost_detector): try: helpers.logprefix = ' s: ' @@ -294,6 +294,14 @@ def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver, import sshuttle.ssnet as ssnet ssnet.LATENCY_BUFFER_SIZE = latency_buffer_size + # The client writes this file to the local machine. If we can see + # it, we delete it prior to send the synchronization header. The + # client can then determine if the server and client are running + # on the same machine by checking for the presence of the file. + if os.path.exists(localhost_detector): + debug3("Deleted the localhost_detector created by the client.\n") + os.remove(localhost_detector) + # synchronization header sys.stdout.write('\0\0SSHUTTLE0001') sys.stdout.flush()