Python3 用于访问远程服务器上的数据库的 Fabric ssh 隧道 [paramiko.ssh_exception.NoValidConnectionsError]
Python3 Fabric ssh tunnel to access database on remote server [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,
port=port,
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_port=22,
local_host="127.0.0.1",
remote_host="IP-D",
):
with c3.forward_local(
local_port=local_port_2,
remote_port=3306,
local_host="127.0.0.1",
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', 22),
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', 22),
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))
我的网络跃点是 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,
port=port,
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_port=22,
local_host="127.0.0.1",
remote_host="IP-D",
):
with c3.forward_local(
local_port=local_port_2,
remote_port=3306,
local_host="127.0.0.1",
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', 22),
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', 22),
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))