使用 Paramiko 在 Python 中设置 SSH 隧道

204 阅读2分钟
  • 需要在多台卫星服务器和一个集中式注册数据库之间建立 SSH 隧道。
    • 服务器之间已经设置了公钥认证,因此无需密码提示即可登录。
    • 尝试使用 Paramiko 创建隧道,但代码实现很复杂。
    • 尝试使用 Autossh,但在建立隧道后 2 分钟内就断开了连接。
    • 需要一个简单的代码片段,可以守护进程并使用 Supervisord 或 Monit 进行监视。
  1. 解决方案:

    • 使用 Paramiko 库来设置 SSH 隧道。
    • 使用 SocketServer 库来创建一个简单的服务器,用于转发流量。
    • 使用 select 模块来处理来自客户端和服务器的数据。
    • 使用 Paramiko 提供的 SSHTransport 类来建立 SSH 连接。
    • 使用 SSHTransport 的 open_channel() 方法来打开一个通道,用于转发流量。
    • 使用 SocketServer 的ThreadingTCPServer 类来创建一个多线程服务器,用于处理来自客户端的请求。
    • 使用 SocketServer 的BaseRequestHandler 类来创建一个处理程序,用于处理来自客户端的请求。
    • 在处理程序中,使用 SSHTransport 的 open_channel() 方法来打开一个通道,用于转发流量。
    • 使用 select.select() 方法来处理来自客户端和服务器的数据。
    • 使用守护进程管理器(如 Supervisord 或 Monit)来监视和管理服务器。

以下是一个使用 Paramiko 在 Python 中设置 SSH 隧道示例代码:

import select
import SocketServer
import sys
import paramiko

class ForwardServer(SocketServer.ThreadingTCPServer):
    daemon_threads = True
    allow_reuse_address = True

class Handler (SocketServer.BaseRequestHandler):
    def handle(self):
        try:
            chan = self.ssh_transport.open_channel('direct-tcpip', (self.chain_host, self.chain_port), self.request.getpeername())
        except Exception, e:
            print('Incoming request to %s:%d failed: %s' % (self.chain_host, self.chain_port, repr(e)))
            return
        if chan is None:
            print('Incoming request to %s:%d was rejected by the SSH server.' % (self.chain_host, self.chain_port))
            return

        print('Connected!  Tunnel open %r -> %r -> %r' % (self.request.getpeername(), chan.getpeername(), (self.chain_host, self.chain_port)))
        while True:
            r, w, x = select.select([self.request, chan], [], [])
            if self.request in r:
                data = self.request.recv(1024)
                if len(data) == 0:
                    break
                chan.send(data)
            if chan in r:
                data = chan.recv(1024)
                if len(data) == 0:
                    break
                self.request.send(data)
        chan.close()
        self.request.close()
        print('Tunnel closed from %r' % (self.request.getpeername(),))

def main():
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.WarningPolicy())
    client.connect("192.168.0.8")

    class SubHandler(Handler):
        chain_host = "192.168.0.6"
        chain_port = 3389
        ssh_transport = client.get_transport()

    try:
        ForwardServer(('', 3389), SubHandler).serve_forever()
    except KeyboardInterrupt:
        sys.exit(0)

if __name__ == '__main__':
    main()

这个脚本将创建一个服务器,用于转发来自 localhost:3389 的流量到 192.168.0.6:3389。要使用此脚本,请按照以下步骤操作:

  1. 将脚本保存到一个文件(例如,ssh_tunnel.py)。
  2. 在服务器上安装 Paramiko 库。
  3. 确保服务器之间已经设置了公钥认证。
  4. 在服务器上运行脚本。
  5. 在客户端上,使用以下命令建立 SSH 隧道:
ssh -L 3389:localhost:3389 user@192.168.0.8

现在,您就可以通过 localhost:3389 访问 192.168.0.6:3389 上的资源了。