用于访问远程服务器上的数据库的Python3 Fabric ssh隧道[paramiko.ssh_exception.NoValidConnectionsError]

问题描述

我的网络跳是A(本地)-> B-> C-> D(端口3306上的数据库已为本地主机打开)。

我需要从A访问D上的数据库

我正在使用fabric进行ssh连接

我的代码如下:

import socket
from contextlib import closing
from fabric import Connection
import MysqL.connector

def _connect_to_middleware_database(self,port):
    return MysqL.connector.connect(
        user="DB-USER",password="MYPASS",host="127.0.0.1",port=port,database="tasks",connection_timeout=10)


def get_free_port():
    with closing(socket.socket(socket.AF_INET,socket.soCK_STREAM)) as s:
        s.bind(("localhost",0))
        s.setsockopt(socket.soL_SOCKET,socket.so_REUSEADDR,1)
        return s.getsockname()[1]

def _get_connection(self,ip,port,user,pw):
    return Connection(
        host=ip,user=user,connect_kwargs={
            "password": pw,"allow_agent": False,"look_for_keys": False,},)


res = {}
local_port = get_free_port()
local_port_1 = get_free_port()
local_port_2 = get_free_port()

c1 = self._get_connection(
    "IP-B",22,"USER-B","PASS-B",)

c2 = self._get_connection(
    "127.0.0.1",local_port,"USER-C","PASS-C",)

c3 = self._get_connection(
    "127.0.0.1",local_port_1,"USER-D","PASS-D",)



with c1.forward_local(
    local_port=local_port,remote_port=22,local_host="127.0.0.1",remote_host="IP-C",):
    with c2.forward_local(
        local_port=local_port_1,remote_host="IP-D",):
        with c3.forward_local(
            local_port=local_port_2,remote_port=3306,remote_host="127.0.0.1",):

            cnx = _connect_to_middleware_database(
                local_port_2
            )
            if cnx:
                cursor = cnx.cursor()
                cursor.execute("show tables")
                sql_result = cursor.fetchall()
                print(str(sql_result))

问题在于它只能真正不一致。经过一些尝试,它可以工作,但大多数情况下,我得到:

Traceback (most recent call last):
  File "tunnel_test.py",line 78,in <module>
    remote_host="IP-D",File "/usr/lib/python3.6/contextlib.py",line 159,in helper
    return _GeneratorContextManager(func,args,kwds)
  File "/usr/lib/python3.6/contextlib.py",line 60,in __init__
    self.gen = func(*args,**kwds)
  File "<decorator-gen-6>",line 2,in forward_local
  File "/usr/local/lib/python3.6/dist-packages/fabric/connection.py",line 29,in opens
    self.open()
  File "/usr/local/lib/python3.6/dist-packages/fabric/connection.py",line 634,in open
    self.client.connect(**kwargs)
  File "/usr/local/lib/python3.6/dist-packages/paramiko/client.py",line 368,in connect
    raise NovalidConnectionsError(errors)
paramiko.ssh_exception.NovalidConnectionsError: [Errno None] Unable to connect to port 59235 on 127.0.0.1

在某些情况下它可以正常工作,而且我使用的主机肯定可以访问。

我很感谢所有答案。

解决方法

使用sshtunnel代替fabric

import sshtunnel

with sshtunnel.open_tunnel(
    ssh_address_or_host=('IP_B',22),remote_bind_address=('IP_C',ssh_username='USER_B',ssh_password='PASS_B',block_on_close=False
) as tunnel1:
    print('Connection to B OK...')
    with sshtunnel.open_tunnel(
        ssh_address_or_host=('localhost',tunnel1.local_bind_port),remote_bind_address=('IP_D',ssh_username='USER_C',ssh_password='PASS_C',block_on_close=False
    ) as tunnel2:
        print('Connection to C OK...')
        with sshtunnel.open_tunnel(
            ssh_address_or_host=('localhost',tunnel2.local_bind_port),remote_bind_address=('127.0.0.1',3306),ssh_username='USER_D',ssh_password='PASS_D',block_on_close=False
        ) as tunnel3:
            print('Connection to D OK...')
            cnx = _connect_to_middleware_database(
                tunnel3.local_bind_port
            )
            if cnx:
                cursor = cnx.cursor()
                cursor.execute("show tables")
                sql_result = cursor.fetchall()
                print(str(sql_result))