Skip to content

Instantly share code, notes, and snippets.

@mluis
Created August 10, 2017 02:37
Show Gist options
  • Save mluis/c227acbe4d991d97d7a325c617746f12 to your computer and use it in GitHub Desktop.
Save mluis/c227acbe4d991d97d7a325c617746f12 to your computer and use it in GitHub Desktop.
Wrapper that simplifies SSH tunnels

ssh-from

ssh-from simplifies common usage of SSH tunnels, and makes double (or triple) tunnels a piece of cake.
Here is an example, where I SSH to a computer that is behind two NATs, tunneling through each router:

Double tunnel example

Details coming to the blog soon.

Installation

Install dependencies (tsocks is optional):

sudo apt-get install python lsof tsocks

Then download the script somewhere in your PATH and make it executable:

sudo wget https://gist.github.com/jmendeth/346f2233310d8292efe7595d60aa3659/raw/ssh-from.py -O /usr/local/bin/ssh-from
sudo chmod a+rx /usr/local/bin/ssh-from

I highly recommend you install this helper program as well.

#!/usr/bin/env python
# Dependencies: lsof
import sys, os, signal, subprocess, socket, random, exceptions, atexit, tempfile
addr = "127.0.0.1"
ssh_args = sys.argv[1:]
# Find free port
tsock = socket.socket()
while True:
port = random.randint(8192, 65535)
try:
tsock.bind((addr, port))
break
except socket.error, e:
pass
# Start SSH proxy
tsock.close()
ssh = subprocess.Popen(["ssh", "-fND", "%s:%d" % (addr, port)] + ssh_args)
while True:
try:
r = ssh.wait()
break
except KeyboardInterrupt, e:
ssh.send_signal(signal.SIGINT)
if r != 0: exit(r)
print "\nConnected (%s)." % " ".join(ssh_args)
# Modify env
proxy = "socks5://%s:%d" % (addr, port)
for var in ["http_proxy", "https_proxy", "ftp_proxy", "rsync_proxy", "ssh_proxy", "all_proxy"]:
os.putenv(var, proxy)
os.putenv(var.upper(), proxy)
os.putenv("no_proxy", "")
os.putenv("ssh_from_orig", " ".join(ssh_args))
# Create tsocks config file, just in case
tsconf = tempfile.NamedTemporaryFile(suffix=".conf", delete=False)
tsconf.write("server = %s\nserver_port = %d\nserver_type = 5\n" % (addr, port))
tsconf.close()
os.putenv("TSOCKS_CONF_FILE", tsconf.name)
# Terminate ssh proxy at exit
def cleanup():
out = subprocess.check_output(["lsof", "-iTCP@%s:%d" % (addr, port), "-sTCP:LISTEN", "-Fp"])
pid = int({ o[0]: o[1:] for o in out.splitlines() }["p"])
os.kill(pid, signal.SIGTERM)
orig = os.getenv("ssh_from_orig")
if orig: print "Back at (%s)." % orig
else: print "SSH proxy terminated."
os.remove(tsconf.name)
atexit.register(cleanup)
# Run shell
sh = subprocess.Popen([os.environ["SHELL"] or "sh"])
while True:
try:
exit(sh.wait())
except KeyboardInterrupt, e:
sh.send_signal(signal.SIGINT)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment