Created
January 6, 2015 23:06
-
-
Save wrouesnel/139b9610e9f5947b653d to your computer and use it in GitHub Desktop.
Creating an SSH tunnel that can be used in a larger program with Paramiko.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Modified by: William Rouesnel (w.rouesnel@gmail.com) | |
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> | |
# | |
# This file is part of paramiko. | |
# | |
# Paramiko is free software; you can redistribute it and/or modify it under the | |
# terms of the GNU Lesser General Public License as published by the Free | |
# Software Foundation; either version 2.1 of the License, or (at your option) | |
# any later version. | |
# | |
# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY | |
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |
# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | |
# details. | |
# | |
# You should have received a copy of the GNU Lesser General Public License | |
# along with Paramiko; if not, write to the Free Software Foundation, Inc., | |
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
# Modified for NetRegistry internal use by Will Rouesnel. | |
""" | |
Lightwieght port forwarding library over Paramiko. The key difference from the | |
original is we randomly select an unused port to forward, and return it to the | |
caller. We also don't establish SSH sessions, we expect someone else to handle | |
that for us. | |
""" | |
import os | |
import socket | |
import select | |
import threading | |
import time | |
import logging | |
logger = logging.getLogger(__name__) | |
try: | |
import SocketServer | |
except ImportError: | |
import socketserver as SocketServer | |
import sys | |
import paramiko | |
class ForwardServer (SocketServer.ThreadingTCPServer): | |
daemon_threads = True | |
allow_reuse_address = True | |
def __init__(self, server_address, RequestHandlerClass, | |
ssh, chain_host, chain_port, | |
bind_and_activate=True): | |
self.ssh = ssh | |
self.chain_host = chain_host | |
self.chain_port = chain_port | |
SocketServer.ThreadingTCPServer.__init__(self, server_address, | |
RequestHandlerClass, | |
bind_and_activate) | |
def server_close(self): | |
"""we don't seem to need to do anything here.""" | |
pass | |
class Handler (SocketServer.BaseRequestHandler): | |
finished = False | |
def handle(self): | |
peername = self.request.getpeername() | |
server = self.server | |
try: | |
chan = server.ssh.get_transport().open_channel('direct-tcpip', | |
(server.chain_host, server.chain_port), | |
self.request.getpeername()) | |
except Exception as e: | |
logger.warn('Incoming request to %s:%d failed: %s' % (server.chain_host, | |
server.chain_port, | |
repr(e))) | |
return | |
if chan is None: | |
logger.warn('Incoming request to %s:%d was rejected by the SSH server.' % | |
(server.chain_host, server.chain_port)) | |
return | |
# Add channel to server | |
#server.open_channels.append(chan) | |
#server.open_requests.append(self.request) | |
logger.debug('Connected! Tunnel open %r -> %r -> %r' % \ | |
(self.request.getpeername(), chan.getpeername(), | |
(server.chain_host, server.chain_port))) | |
while True: | |
r, w, x = select.select([self.request, chan], [], []) | |
if self.request in r: | |
data = self.request.recv(1024) | |
if len(data) == 0: | |
break | |
chan.send(data) | |
if chan in r: | |
data = chan.recv(1024) | |
if len(data) == 0: | |
break | |
self.request.send(data) | |
# Check if our server wants to shutdown | |
if server._BaseServer__is_shut_down.is_set(): | |
logger.debug('Closing tunnel due to shutdown request.') | |
break | |
time.sleep(0) # Let something else run. | |
chan.close() | |
self.request.close() | |
logger.debug('Tunnel closed from %r' % (peername,)) | |
def forward_tunnel(ssh, remote_host, remote_port): | |
# Bind to port 0 to get a free port | |
server = ForwardServer(('', 0), Handler, | |
ssh=ssh, | |
chain_host=remote_host, | |
chain_port=remote_port) | |
ip,port = server.server_address | |
server_thread = threading.Thread(target=server.serve_forever) | |
server_thread.daemon = True | |
server_thread.start() | |
return server, port |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment