tunnel.py
123 lines
| 3.3 KiB
| text/x-python
|
PythonLexer
|
r3571 | |||
#----------------------------------------- | ||||
# 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'] | ||||