如何使用Python和Paramiko创建SSH隧道?
我需要建立一个隧道来读取数据库中的信息。我使用的是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 进行端口转发的示例。