使 Python 通过 SSH 隧道连接到 MySQL
Posted
技术标签:
【中文标题】使 Python 通过 SSH 隧道连接到 MySQL【英文标题】:Enable Python to Connect to MySQL via SSH Tunnelling 【发布时间】:2014-03-21 03:16:44 【问题描述】:我在 Python 2.7 中使用 mysqldb
以允许 Python 连接到另一个 MySQL 服务器
import MySQLdb
db = MySQLdb.connect(host="sql.domain.com",
user="dev",
passwd="*******",
db="appdb")
除了像这样正常连接,如何使用 SSH 密钥对通过 SSH 隧道建立连接?
理想情况下,SSH 隧道应该由 Python 打开。 SSH隧道主机和MySQL服务器是同一台机器。
【问题讨论】:
你谷歌了吗?使用 python 打开一个 ssh 隧道:***.com/questions/4364355/…,通过该隧道连接到 MySql:***.com/questions/3577555/… 您可能有充分的理由使用 SSH,但如果这是与 MySQL 服务器的直接连接,请改用 SSL。减少可能出错的事情。 @geertjanvdk 这很有趣,为什么 SSL 会是更好的选择?我希望在客户端和服务器之间建立安全连接,首先想到的是 SSH 您不使用 SSH 连接到安全网站,是吗?这只会使事情复杂化。如果您的 MySQL 服务器可直接访问,则 SSL 是可行的方法。此外,SSL 适用于任何连接器或操作系统,如 Windows。使用 SSH 隧道时,您需要对其进行维护、监控等。 【参考方案1】:只有这个对我有用
import pymysql
import paramiko
import pandas as pd
from paramiko import SSHClient
from sshtunnel import SSHTunnelForwarder
from os.path import expanduser
home = expanduser('~')
mypkey = paramiko.RSAKey.from_private_key_file(home + pkeyfilepath)
# if you want to use ssh password use - ssh_password='your ssh password', bellow
sql_hostname = 'sql_hostname'
sql_username = 'sql_username'
sql_password = 'sql_password'
sql_main_database = 'db_name'
sql_port = 3306
ssh_host = 'ssh_hostname'
ssh_user = 'ssh_username'
ssh_port = 22
sql_ip = '1.1.1.1.1'
with SSHTunnelForwarder(
(ssh_host, ssh_port),
ssh_username=ssh_user,
ssh_pkey=mypkey,
remote_bind_address=(sql_hostname, sql_port)) as tunnel:
conn = pymysql.connect(host='127.0.0.1', user=sql_username,
passwd=sql_password, db=sql_main_database,
port=tunnel.local_bind_port)
query = '''SELECT VERSION();'''
data = pd.read_sql_query(query, conn)
conn.close()
【讨论】:
sql_ip是干什么用的? 工作就像一个魅力。谢谢! @kathanshah 如果中间有跳转主机,如何使用它? & 它是否允许使用默认的 .ssh/config 文件设置? @OneAdamTwelve,sql_ip
似乎是不必要且未使用的。但是这个例子有效! (我用的是 MySQLdb)
对于使用此解决方案遇到“xxx.com Host is not allowed to connect to this MySQL server”的任何人来说,只是一个小小的附加评论。然后需要在 SSHTunnelForwarder() 的第一部分添加 local_bind_address = ('127.0.0.1', sql_port),例如:with SSHTunnelForwarder((ssh_host, ssh_port), ssh_username=ssh_user, ssh_pkey=mypkey, remote_bind_address=('127.0 .0.1', sql_port), local_bind_address = ('127.0.0.1', sql_port) ) 作为隧道:为什么?默认本地地址解析为mysql无法解析的外部主机名。【参考方案2】:
我猜你需要端口转发。我推荐sshtunnel.SSHTunnelForwarder
import mysql.connector
import sshtunnel
with sshtunnel.SSHTunnelForwarder(
(_host, _ssh_port),
ssh_username=_username,
ssh_password=_password,
remote_bind_address=(_remote_bind_address, _remote_mysql_port),
local_bind_address=(_local_bind_address, _local_mysql_port)
) as tunnel:
connection = mysql.connector.connect(
user=_db_user,
password=_db_password,
host=_local_bind_address,
database=_db_name,
port=_local_mysql_port)
...
【讨论】:
我应该在 remote_bind_address 中放什么?我在哪里可以找到这些信息? 如果你在本地,你可以试试这样:_remote_bind_address = '127.0.0.1' _local_bind_address = '0.0.0.0'
这是什么类型的转发?远程转发?需要任何服务器设置吗?
它不需要任何服务器设置。实际上,它是一种端口转发设置,可以通过 SSH 隧道访问安全外壳 (SSH) 连接的服务器端的应用程序。【参考方案3】:
from sshtunnel import SSHTunnelForwarder
import pymysql
import pandas as pd
tunnel = SSHTunnelForwarder(('SSH_HOST', 22), ssh_password=SSH_PASS, ssh_username=SSH_UNAME,
remote_bind_address=(DB_HOST, 3306))
tunnel.start()
conn = pymysql.connect(host='127.0.0.1', user=DB_UNAME, passwd=DB_PASS, port=tunnel.local_bind_port)
data = pd.read_sql_query("SHOW DATABASES;", conn)
感谢https://www.reddit.com/r/learnpython/comments/53wph1/connecting_to_a_mysql_database_in_a_python_script/
【讨论】:
使用启动隧道的方法对我来说比使用'with'语句的方法更好。它以某种方式解决了我的问题:***.com/questions/63774543/… 我不知道为什么,但这里也只能使用这种方式。使用with
语句似乎对我不起作用。
使用 with 启动 tunnel.close(),由于某种原因挂起。使用 tunnel.stop() 然后使用 tunnel.close() 似乎可以绕过这个问题。【参考方案4】:
如果您的私钥文件已加密,这对我有用:
mypkey = paramiko.RSAKey.from_private_key_file(<<file location>>, password='password')
sql_hostname = 'sql_hostname'
sql_username = 'sql_username'
sql_password = 'sql_password'
sql_main_database = 'sql_main_database'
sql_port = 3306
ssh_host = 'ssh_host'
ssh_user = 'ssh_user'
ssh_port = 22
with SSHTunnelForwarder(
(ssh_host, ssh_port),
ssh_username=ssh_user,
ssh_pkey=mypkey,
ssh_password='ssh_password',
remote_bind_address=(sql_hostname, sql_port)) as tunnel:
conn = pymysql.connect(host='localhost', user=sql_username,
passwd=sql_password, db=sql_main_database,
port=tunnel.local_bind_port)
query = '''SELECT VERSION();'''
data = pd.read_sql_query(query, conn)
print(data)
conn.close()
【讨论】:
【参考方案5】:您只能写入私钥文件的路径:ssh_pkey='/home/userName/.ssh/id_ed25519'
(文档在此处:https://sshtunnel.readthedocs.io/en/latest/)。
如果您使用 Oracle 的 mysql.connector,您必须使用构造
cnx = mysql.connector.MySQLConnection(
...
重要:建筑
cnx = mysql.connector.connect(
...
不能通过 SSH 工作!这是一个错误。
(文档在这里:https://dev.mysql.com/doc/connector-python/en/connector-python-connectargs.html)。
此外,您的 SQL 语句必须是理想的。如果 SQL 服务器端出现错误,您不会收到来自 SQL 服务器的错误消息。
import sshtunnel
import numpy as np
with sshtunnel.SSHTunnelForwarder(ssh_address_or_host='ssh_host',
ssh_username="ssh_username",
ssh_pkey='/home/userName/.ssh/id_ed25519',
remote_bind_address=('localhost', 3306),
) as tunnel:
cnx = mysql.connector.MySQLConnection(user='sql_username',
password='sql_password',
host='127.0.0.1',
database='db_name',
port=tunnel.local_bind_port)
cursor = cnx.cursor()
cursor.execute('SELECT * FROM db_name.tableName;')
arr = np.array(cursor.fetchall())
cursor.close()
cnx.close()
【讨论】:
您能否提供有关该错误的支持参考?我有 .connect() 在早期版本中工作,但不再工作,所以这是一个有用的线索。【参考方案6】:Paramiko 是执行 ssh 隧道的最佳 python 模块。在此处查看代码: https://github.com/paramiko/paramiko/blob/master/demos/forward.py
正如在 cmets 中所说,这一款非常完美。 SSH Tunnel for Python MySQLdb connection
【讨论】:
【参考方案7】:最佳实践是参数化连接变量。 这是我的实施方式。像魅力一样工作!
import mysql.connector
import sshtunnel
import pandas as pd
import configparser
config = configparser.ConfigParser()
config.read('c:/work/tmf/data_model/tools/config.ini')
ssh_host = config['db_qa01']['SSH_HOST']
ssh_port = int(config['db_qa01']['SSH_PORT'])
ssh_username = config['db_qa01']['SSH_USER']
ssh_pkey = config['db_qa01']['SSH_PKEY']
sql_host = config['db_qa01']['HOST']
sql_port = int(config['db_qa01']['PORT'])
sql_username = config['db_qa01']['USER']
sql_password = config['db_qa01']['PASSWORD']
with sshtunnel.SSHTunnelForwarder(
(ssh_host,ssh_port),
ssh_username=ssh_username,
ssh_pkey=ssh_pkey,
remote_bind_address=(sql_host, sql_port)) as tunnel:
connection = mysql.connector.connect(
host='127.0.0.1',
port=tunnel.local_bind_port,
user=sql_username,
password=sql_password)
query = 'select version();'
data = pd.read_sql_query(query, connection)
print(data)
connection.close()
【讨论】:
【参考方案8】:这对我有用:
import mysql.connector
import sshtunnel
with sshtunnel.SSHTunnelForwarder(
('ip-of-ssh-server', 'port-in-number-format'),
ssh_username = 'ssh-username',
ssh_password = 'ssh-password',
remote_bind_address = ('127.0.0.1', 3306)
) as tunnel:
connection = mysql.connector.connect(
user = 'database-username',
password = 'database-password',
host = '127.0.0.1',
port = tunnel.local_bind_port,
database = 'databasename',
)
mycursor = connection.cursor()
query = "SELECT * FROM datos"
mycursor.execute(query)
【讨论】:
以上是关于使 Python 通过 SSH 隧道连接到 MySQL的主要内容,如果未能解决你的问题,请参考以下文章
使用 Python 通过 SSH 隧道连接到远程 PostgreSQL 数据库