#----------------------------------------- # Imports #----------------------------------------- from __future__ import print_function import os,sys from multiprocessing import Process from getpass import getpass, getuser try: import paramiko except ImportError: paramiko = None else: from forward import forward_tunnel from IPython.external import pexpect def launch_ssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, timeout=15): """Create an ssh tunnel using command-line ssh that connects port lport on this machine to localhost:rport on server. The tunnel will automatically close when not in use, remaining open for a minimum of timeout seconds for an initial connection. """ ssh="ssh " if keyfile: ssh += "-i " + keyfile cmd = ssh + " -f -L %i:127.0.0.1:%i %s sleep %i"%(lport, rport, server, timeout) tunnel = pexpect.spawn(cmd) failed = False while True: try: tunnel.expect('[Pp]assword:', timeout=.1) except pexpect.TIMEOUT: continue except pexpect.EOF: if tunnel.exitstatus: print (tunnel.exitstatus) print (tunnel.before) print (tunnel.after) raise RuntimeError("tunnel '%s' failed to start"%(cmd)) else: return tunnel.pid else: if failed: print("Password rejected, try again") tunnel.sendline(getpass()) failed = True def _split_server(server): if '@' in server: username,server = server.split('@', 1) else: username = getuser() if ':' in server: server, port = server.split(':') port = int(port) else: port = 22 return username, server, port def launch_paramiko_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None): """launch a tunner with paramiko in a subprocess""" if paramiko is None: raise ImportError("Paramiko not available") server = _split_server(server) if keyfile is None: passwd = getpass("%s@%s's password: "%(server[0], server[1])) else: passwd = None p = Process(target=_paramiko_tunnel, args=(lport, rport, server, remoteip), kwargs=dict(keyfile=keyfile, password=passwd)) p.daemon=False p.start() return p def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None): """function for actually starting a paramiko tunnel, to be passed to multiprocessing.Process(target=this). """ username, server, port = server client = paramiko.SSHClient() client.load_system_host_keys() client.set_missing_host_key_policy(paramiko.WarningPolicy()) try: client.connect(server, port, username=username, key_filename=keyfile, look_for_keys=True, password=password) except Exception as e: print ('*** Failed to connect to %s:%d: %r' % (server, port, e)) sys.exit(1) print ('Now forwarding port %d to %s:%d ...' % (lport, server, rport)) try: forward_tunnel(lport, remoteip, rport, client.get_transport()) except KeyboardInterrupt: print ('C-c: Port forwarding stopped.') sys.exit(0) __all__ = ['launch_ssh_tunnel', 'launch_paramiko_tunnel']