Skip to content

Instantly share code, notes, and snippets.

@wrouesnel
Created January 6, 2015 23:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wrouesnel/139b9610e9f5947b653d to your computer and use it in GitHub Desktop.
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.
#!/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