关于用Python启动SSH隧道的问题

3 投票
3 回答
7415 浏览
提问于 2025-04-16 03:19

我在用Python写的HTTP RPC服务器上启动SSH隧道时遇到了麻烦。

这个HTTP RPC服务器是基于Python的BaseHTTPServer写的。作为这个服务的一部分,我想从RPC服务器启动一个SSH隧道到远程机器。我在RPC调用的Python脚本中使用os.system来启动SSH隧道。

os.system("ssh -f -n -N -L 127.0.0.1:%d:localhost:%d user@%s" % (6800, 9000, "remote.machine"))

乍一看,一切似乎都很好,因为隧道已经启动,我可以使用它,但我注意到了一件事。除了在6800端口监听外,SSH还在8001端口监听(这是HTTP RPC服务器运行的端口)。

这是关于RPC服务器和SSH的lsof输出:

rpc.py    27763   usern    5u  IPv4 102130428       TCP 127.0.0.1:8001 (LISTEN)
ssh        1951   usern   14u  IPv4 102149728       TCP 127.0.0.1:6800 (LISTEN)
ssh        1951   usern    5u  IPv4 102130428       TCP 127.0.0.1:8001 (LISTEN)

一切正常,直到RPC服务器重启。在重启过程中,RPC服务器被迫关闭与监听套接字的连接,但SSH的连接仍然保持打开状态,因此RPC服务器无法再次在同一个端口上启动。

看起来SSH隧道以某种方式与RPC服务器的监听套接字的文件描述符关联在一起。

有没有人能给点建议,如何在脚本中设置SSH隧道,让它只在指定的端口(在这个例子中是6800)上监听。

3 个回答

0

你有没有考虑过用 paramiko 来处理你的SSH连接?(这个库依赖于PyCrypto,但其他部分都是用纯Python写的)

这样可以让你管理连接变得更简单,而且根据你的服务器如何处理套接字,你可能只需要设置一个paramiko的通道,然后让服务器把它当作其他监听套接字来处理。

(在paramiko中,Transport对象代表基本的SSH2连接,对于每个Transport,你可以创建多个Channel对象,每个Channel都可以替代普通的Python套接字)

如果你想了解更多,这里有一些资源可以看看:

0

我觉得这可能跟“分叉”有关,也就是让ssh从调用的进程中分离出来(用“-f”这个选项)。

另外,我不确定这是否有帮助,但如果你的Python版本足够新,最好使用subprocess模块。

2

与其使用os.system(),不如试试subprocess.Popen(),并把close_fds设置为True(这个在Python 2.4及以上版本都有)。

import subprocess    
subprocess.Popen("ssh -f -n -N -L 127.0.0.1:%d:localhost:%d user@%s" % (6800, 9000, "remote.machine"), shell=True, close_fds=True)

理论上,我觉得你也可以用fcntl来设置监听套接字的文件描述符为在执行时关闭(FD_CLOEXEC),这样在调用os.system()之前就可以做到。

撰写回答