From 0d460c0264f49b72f2e759b0cfb92486c0fdca61 Mon Sep 17 00:00:00 2001 From: Scott Kuhl Date: Wed, 10 Mar 2021 13:52:46 -0500 Subject: [PATCH 1/3] Check if client and server are running on same machine. This patch checks to see if the client and the server are running on the same machine. The client creates a temporary file that the server then looks for. If the server finds the file, it knows it is on the same machine as the client and it deletes the file. Next, if the client detects that the file has been deleted by the server, it assumes that it is running on the same machine as the server. This check is necessary since a user may specify a remote host with "-r" that actually points back to the same machine the client is on. This commit uses tempfile.mkstemp() which was added in Python 2.3. Functionally, this commit does nothing except add a debug message about if the client and server are on the same machine. It also sets a variable which contains this information for potential future use. Potential future uses include... 1) If the TTL hack is removed, this can be used to print a error message since running the client and server on the same machine would no longer be supported. 2) If the TTL hack is continued, we could disable the hack when the client and the server are on different machines. --- sshuttle/assembler.py | 2 +- sshuttle/client.py | 30 +++++++++++++++++++++++++++++- sshuttle/server.py | 14 +++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/sshuttle/assembler.py b/sshuttle/assembler.py index 2d09a50..2937335 100644 --- a/sshuttle/assembler.py +++ b/sshuttle/assembler.py @@ -42,4 +42,4 @@ import sshuttle.cmdline_options as options # noqa: E402 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.ttl) + options.auto_nets, options.ttl, options.localhost_detector) diff --git a/sshuttle/client.py b/sshuttle/client.py index c1cabdb..354c887 100644 --- a/sshuttle/client.py +++ b/sshuttle/client.py @@ -6,6 +6,7 @@ import subprocess as ssubprocess import os import sys import platform +import tempfile import psutil import sshuttle.helpers as helpers @@ -47,6 +48,10 @@ def got_signal(signum, frame): _pidname = None +# This variable is set to true if the client and the server appear to +# be running on the same host. +_client_server_samehost = False + def check_daemon(pidfile): global _pidname @@ -467,6 +472,16 @@ 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. The + # _client_server_samehost variable is set to true when they are on + # the same machine. + (_, localhost_detector) = tempfile.mkstemp(prefix="sshuttle-localhost-") + try: (serverproc, serversock) = ssh.connect( ssh_cmd, remotename, python, @@ -476,7 +491,8 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename, auto_hosts=auto_hosts, to_nameserver=to_nameserver, auto_nets=auto_nets, - ttl=ttl)) + ttl=ttl, + localhost_detector=localhost_detector)) except socket.error as e: if e.args[0] == errno.EPIPE: raise Fatal("failed to establish ssh session (1)") @@ -586,6 +602,18 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename, % (expected, initstring)) 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. + global _client_server_samehost + if os.path.exists(localhost_detector): + debug2("Client and server appear to be running on different machines.") + _client_server_samehost = False + os.remove(localhost_detector) # cleanup + else: + debug1("Client and server are running on the same machine.") + _client_server_samehost = True + if daemon: daemonize() log('daemonizing (%s).' % _pidname) diff --git a/sshuttle/server.py b/sshuttle/server.py index 645252b..1d443e8 100644 --- a/sshuttle/server.py +++ b/sshuttle/server.py @@ -16,6 +16,8 @@ from sshuttle.ssnet import Handler, Proxy, Mux, MuxWrapper from sshuttle.helpers import b, log, debug1, debug2, debug3, Fatal, \ resolvconf_random_nameserver, which, get_env +_client_server_samehost = False + def _ipmatch(ipstr): # FIXME: IPv4 only @@ -273,7 +275,7 @@ class UdpProxy(Handler): def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver, - auto_nets, ttl): + auto_nets, ttl, localhost_detector): try: helpers.logprefix = ' s: ' debug1('Starting server with Python version %s' @@ -284,6 +286,16 @@ 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. + global _client_server_samehost + if os.path.exists(localhost_detector): + debug3("Deleted the localhost_detector created by the client.\n") + os.remove(localhost_detector) + _client_server_samehost = True + # synchronization header sys.stdout.write('\0\0SSHUTTLE0001') sys.stdout.flush() From 7fd0a776e54d91f9f8bba725ebb90bcc184481d3 Mon Sep 17 00:00:00 2001 From: Scott Kuhl Date: Fri, 16 Jul 2021 14:19:18 -0400 Subject: [PATCH 2/3] Exit if host specified with -r is actually localhost. --- sshuttle/client.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/sshuttle/client.py b/sshuttle/client.py index 4b2cc3a..e96b899 100644 --- a/sshuttle/client.py +++ b/sshuttle/client.py @@ -48,10 +48,6 @@ def got_signal(signum, frame): # Filename of the pidfile created by the sshuttle client. _pidname = None -# This variable is set to true if the client and the server appear to -# be running on the same host. -_client_server_samehost = False - def check_daemon(pidfile): global _pidname @@ -477,9 +473,7 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename, # 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. The - # _client_server_samehost variable is set to true when they are on - # the same machine. + # that it is running on the same host as the server. (_, localhost_detector) = tempfile.mkstemp(prefix="sshuttle-localhost-") try: @@ -604,14 +598,22 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename, # If the server couldn't delete our localhost_detector file, then # the server is running on a different machine. - global _client_server_samehost if os.path.exists(localhost_detector): - debug2("Client and server appear to be running on different machines.") - _client_server_samehost = False + debug3("Client and server appear to be running on different machines.") os.remove(localhost_detector) # cleanup else: - debug1("Client and server are running on the same machine.") - _client_server_samehost = True + # 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() From 56bc247ab8a151aadcf5d9a393e4f6877a3c25ed Mon Sep 17 00:00:00 2001 From: Scott Kuhl Date: Mon, 19 Jul 2021 12:13:16 -0400 Subject: [PATCH 3/3] Remove unused variable --- sshuttle/server.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sshuttle/server.py b/sshuttle/server.py index 379495d..407c7db 100644 --- a/sshuttle/server.py +++ b/sshuttle/server.py @@ -16,8 +16,6 @@ from sshuttle.ssnet import Handler, Proxy, Mux, MuxWrapper from sshuttle.helpers import b, log, debug1, debug2, debug3, Fatal, \ resolvconf_random_nameserver, which, get_env -_client_server_samehost = False - def _ipmatch(ipstr): # FIXME: IPv4 only @@ -286,11 +284,9 @@ def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver, # 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. - global _client_server_samehost if os.path.exists(localhost_detector): debug3("Deleted the localhost_detector created by the client.\n") os.remove(localhost_detector) - _client_server_samehost = True # synchronization header sys.stdout.write('\0\0SSHUTTLE0001')