Je travaille sur un script python pour interroger quelques bases de données distantes sur un tunnel ssh établi de temps en temps. Je connais assez bien la bibliothèque paramiko, donc c'était mon choix de route. Je préférerais garder ceci en python complet afin que je puisse utiliser paramiko pour gérer les problèmes clés, et utiliser python pour démarrer, contrôler et arrêter les tunnels SSH.Arrêt du tunnel SSH Paramiko Problème
Il y a eu quelques questions connexes à propos de ce sujet, mais la plupart semblaient incomplètes dans les réponses. Ma solution ci-dessous est un ensemble piraté des solutions que j'ai trouvées jusqu'ici.
Maintenant pour le problème: je suis capable de créer le premier tunnel assez facilement (dans un thread séparé) et de faire mes trucs DB/python, mais lorsque je tente de fermer le tunnel, localhost ne libère pas le port local Je me suis lié à. Ci-dessous, j'ai inclus ma source et les données Netstat pertinentes à chaque étape du processus.
#!/usr/bin/python
import select
import SocketServer
import sys
import paramiko
from threading import Thread
import time
class ForwardServer(SocketServer.ThreadingTCPServer):
daemon_threads = True
allow_reuse_address = True
class Handler (SocketServer.BaseRequestHandler):
def handle(self):
try:
chan = self.ssh_transport.open_channel('direct-tcpip', (self.chain_host, self.chain_port), self.request.getpeername())
except Exception, e:
print('Incoming request to %s:%d failed: %s' % (self.chain_host, self.chain_port, repr(e)))
return
if chan is None:
print('Incoming request to %s:%d was rejected by the SSH server.' % (self.chain_host, self.chain_port))
return
print('Connected! Tunnel open %r -> %r -> %r' % (self.request.getpeername(), chan.getpeername(), (self.chain_host, self.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)
chan.close()
self.request.close()
print('Tunnel closed from %r' % (self.request.getpeername(),))
class DBTunnel():
def __init__(self,ip):
self.c = paramiko.SSHClient()
self.c.load_system_host_keys()
self.c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.c.connect(ip, username='someuser')
self.trans = self.c.get_transport()
def startTunnel(self):
class SubHandler(Handler):
chain_host = '127.0.0.1'
chain_port = 5432
ssh_transport = self.c.get_transport()
def ThreadTunnel():
global t
t = ForwardServer(('', 3333), SubHandler)
t.serve_forever()
Thread(target=ThreadTunnel).start()
def stopTunnel(self):
t.shutdown()
self.trans.close()
self.c.close()
Bien que je terminerai en utilisant une méthode de type stopTunnel(), j'ai conscience que le code n'est pas tout à fait correct, mais plus encore une expérimentation d'essayer d'obtenir le tunnel à l'arrêt correctement et tester mes résultats .
Quand j'appelle créer l'objet DBTunnel et appeler startTunnel(), les rendements netstat les éléments suivants:
tcp4 0 0 *.3333 *.* LISTEN
tcp4 0 0 MYIP.36316 REMOTE_HOST.22 ESTABLISHED
tcp4 0 0 127.0.0.1.5432 *.* LISTEN
Une fois que j'appelle stopTunnel(), ou même supprimer l'objet DBTunnel itself..I'm gauche avec cette connexion jusqu'à python sortie I tous ensemble, et ce que je suppose être le garbage collector prend soin:
tcp4 0 0 *.3333 *.* LISTEN
il serait bien de comprendre pourquoi cette prise ouverte est suspendue autour indépendante de l'objet DBConnect , et comment le fermer correctement de l'intérieur mon script. Si j'essaye de lier une connexion différente à une IP différente en utilisant le même port local avant de quitter complètement python (time_wait n'est pas le problème), alors j'obtiens l'adresse tristement célèbre err 48 utilisée. Merci d'avance :)
Le code ci-dessus montre un bug dans l'origina l paramiko exemple de code forward, qui est que le self.request.getpeername() provoque une mauvaise exception de descripteur de fichier car il est appelé après la fermeture de la requête. Le correctif est ici: https://github.com/paramiko/paramiko/pull/36 –