如何使用Python和Paramiko创建SSH隧道?

33 投票
5 回答
77305 浏览
提问于 2025-04-17 06:30

我需要建立一个隧道来读取数据库中的信息。我使用的是Paramiko这个工具,但我还没有用过隧道。请给我一个简单的代码示例,展示如何创建和关闭一个隧道。

5 个回答

12

虽然这个方法没有用到paramiko,但我觉得这是一个非常简单明了的解决方案(类似于@dario的回答,不过不需要在Python中管理线程)。

在openssh客户端中,有一个不太被提及的功能,可以让我们通过unix套接字来控制ssh进程,引用一下man ssh的内容:

-M      Places the ssh client into “master” mode for connection sharing.  Multiple -M options places ssh
         into “master” mode with confirmation required before slave connections are accepted.  Refer to the
         description of ControlMaster in ssh_config(5) for details.
-S ctl_path
         Specifies the location of a control socket for connection sharing, or the string “none” to disable
         connection sharing.  Refer to the description of ControlPath and ControlMaster in ssh_config(5)
         for details.

所以你可以启动一个后台的ssh进程(使用-Nf),然后通过另一个ssh命令来检查(或终止)它。

我在一个需要建立反向隧道的项目中使用这个功能。

from subprocess import call, STDOUT
import os
DEVNULL = open(os.devnull, 'wb')


CONFIG = dict(
    SSH_SERVER='ssh.server.com',
    SSH_PORT=2222,
    SSH_USER='myuser',
    SSH_KEY='/path/to/user.key',
    REMOTE_PORT=62222,
    UNIX_SOCKET='/tmp/ssh_tunnel.sock',
    KNOWN_HOSTS='/path/to/specific_known_host_to_conflicts',
)


def start():
    return call(
        [
            'ssh', CONFIG['SSH_SERVER'],
            '-Nfi', CONFIG['SSH_KEY'],
            '-MS', CONFIG['UNIX_SOCKET'],
            '-o', 'UserKnownHostsFile=%s' % CONFIG['KNOWN_HOSTS'],
            '-o', 'ExitOnForwardFailure=yes',
            '-p', str(CONFIG['SSH_PORT']),
            '-l', CONFIG['SSH_USER'],
            '-R', '%d:localhost:22' % CONFIG['REMOTE_PORT']
        ],
        stdout=DEVNULL,
        stderr=STDOUT
    ) == 0


def stop():
    return __control_ssh('exit') == 0


def status():
    return __control_ssh('check') == 0


def __control_ssh(command):
    return call(
        ['ssh', '-S', CONFIG['UNIX_SOCKET'], '-O', command, 'x'],
        stdout=DEVNULL,
        stderr=STDOUT
    )

-o ExitOnForwardFailure=yes这个选项确保如果隧道无法建立,ssh命令会失败并退出,否则它不会退出。

16

我在我的项目中使用了sshtunnel这个工具。下面是一个例子,展示了如何把远程的MySQL端口转发到本地的端口:

pip install sshtunnel
python -m sshtunnel -U root -P password -L :3306 -R 127.0.0.1:3306 -p 2222 localhost
16

在工作中,我们通常会创建 SSH 隧道来转发端口。我们这样做的方法是使用标准命令 ssh -L port:addr:port addr,并在一个单独的线程中运行子进程。我找到了一些有用的链接:https://github.com/paramiko/paramiko/blob/master/demos/forward.py,里面有一个使用 paramiko 进行端口转发的示例。

撰写回答