通过 ssh 隧道访问远程数据库(Python 3)

Posted

技术标签:

【中文标题】通过 ssh 隧道访问远程数据库(Python 3)【英文标题】:Access remote DB via ssh tunnel (Python 3) 【发布时间】:2017-12-26 02:09:15 【问题描述】:

我无法理解(即使在阅读了几篇专门讨论 ssh Tunelling 的文章之后)CLI ssh 命令的哪些参数在此脚本中的位置。 基本上我必须连接到某个服务器(我称之为 'ssh_tunnel_host:22'),而不是使用此隧道连接到 db_host。

with SSHTunnelForwarder(
    ('ssh_tunnel_host', 22),
    ssh_username="ssh_username",
    ssh_pkey="/somepath/id_rsa",
    local_bind_address=('0.0.0.0', 1234),
    remote_bind_address=('127.0.0.1', 3306)
) as tunnel:
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect('127.0.0.1', 1234)
    db_connection = pymysql.connect(host=db_host, port=3306, db='mysql', user='user',
                                    password='password', charset='utf8mb4')

谁能给我解释一下:

    什么是 local_bind_address - 是我的本地地址还是 ssh_tunnel_host 本地地址?我在哪里可以学习 IP 和端口? 取决于前面的问题 - 什么是 remote_bind_address - 它是 ssh_tunnel_host 还是 db_host 的地址?我在哪里可以了解这些 IP 和端口? 我应该在哪里连接 client.connect()?本地绑定还是远程绑定?

(我确实尝试阅读文档,但仍然一团糟)

【问题讨论】:

【参考方案1】:

把它想象成一个代理连接。你连接到ssh_tunnel_host:22 并告诉它从它的<db host>:3306 代理连接,这意味着db_host 上的端口3306 由ssh_tunnel_host 访问给你,客户端。

您可以指定您希望代理连接可用的本地(对您而言)ip:port,或者让客户端选择一个免费的。省略local_bind_address 是后者。

然后您连接到您的本地端口,该端口实际上是remote_bind_address:3306 的代理。

local_bind_address ssh_tunnel_host remote_bind_address

代码应该是:

db_host = '<address reachable only by ssh_tunnel_host>'
with SSHTunnelForwarder(
    ('ssh_tunnel_host', 22),
    ssh_username="ssh_username",
    ssh_pkey="/somepath/id_rsa",
    remote_bind_address=(db_host, 3306)
) as tunnel:
    port = tunnel.local_bind_port
    db_connection = pymysql.connect(
        host='127.0.0.1', port=port, db='mysql', user='user',
        password='password', charset='utf8mb4')

    本地到客户端地址。要么设置一个,要么让客户端从tunnel.local_bind_port中选择并找到它的端口。

    您希望ssh_tunnel_host 代理回您的地址。如果它是服务器服务的本地,IP 将是127.0.0.1 和服务的端口。它很可能是任何其他 IP 或 ssh_tunnel_host 的网络中在 SSH 隧道主机之外不可见的 IP。

    无处可去。隧道提供代理远程连接的本地 ip:port。一旦隧道启动,就不需要其他客户端了。只需连接到本地 ip:port。

【讨论】:

目前无法尝试您的建议。但是为什么db host不参与连接链呢? ssh_tunnel_host 看到的db host 127.0.0.1:3306 不是吗?否则它是如何被隧道化的。根据 (2),如果 DB 主机不是隧道主机本地,则需要将其作为远程绑定地址,即 remote_bind_address=(db_host, 3306) 谢谢,最后我连接到我的数据库,将 db_host 设置为 remote_bind_address 主机 - 我认为我没有明确表示我的数据库不在 ssh_tunnel_host 上,在我只能通过 ssh_tunnel_host 访问的另一台服务器上: remote_bind_address=(db_host, 3306) 感谢您澄清并更新答案以反映这一点。【参考方案2】:

我们的情况类似,我厌倦了在尝试连接到 MS Sqlserver 数据库之前确保 SSH 隧道已启动并运行。所以我们有一个远程服务器(ip1),我们在上面安装了一个带有 MSSqlserver 的 docker。自然,当您登录该服务器时,您可以将 docker ip 转发到该机器上的 localhost。因此,如果您在“内部”ip1 中,您可以作为 localhost 访问服务器。

所有版本的 MS Sqlserver 强制将端口 1433 作为访问端口。

所以你有 ip1->localhost:1433->MSSqlserver

所以 SSH 使用简单的 SSH 用户@ip1 创建了一个访问 ip1 的隧道,但是您需要在远程服务器上转发 localhost:1433 才能像本地一样操作。

from sshtunnel import SSHTunnelForwarder
import values

server = SSHTunnelForwarder(
    (values.remote_server_ip, values.remote_server_port),
    ssh_username=values.remote_server_username,
    ssh_pkey=values.rsa_path+values.rsa_file,
    ssh_private_key_password=values.rsa_passphrase,
    remote_bind_address=(values.private_server_ip, values.private_server_port),
    local_bind_address=(values.db_server_ip, values.db_server_port) )
try:
    server.start()
except:
    print("trouble connecting to the tunnel. we will asume it is already up")
else:
    print("server is started and on port ",server.local_bind_port)  # show assigned local port

在这里做一些工作 那么

try:
    server.stop()
except:
    print("could not stop the server. assume it is already down")
else:
    print("stopped the server successfully")

而 values.py 包含的值

remote_server_ip = "....remote ip...."
remote_server_port = 22
private_server_ip = "localhost"
private_server_port = 1433
db_server_ip = "localhost"
db_server_port = 1433
remote_server_username = "...."
rsa_passphrase = "...."
rsa_path = "˜/.ssh/"
rsa_file = "id_rsa"

【讨论】:

以上是关于通过 ssh 隧道访问远程数据库(Python 3)的主要内容,如果未能解决你的问题,请参考以下文章

使用 Python 通过 SSH 隧道连接到远程 PostgreSQL 数据库

SSH 通过隧道访问远程客户端

jmx/jstatd 通过 ssh 隧道访问远程机器

通过ssh隧道安全访问(如防火墙过滤)远程服务

通过 SSH 隧道访问 SQL Server

SSH 隧道远程访问 MySQL 数据库