【问题标题】:How to open an SSH tunnel using python?如何使用 python 打开 SSH 隧道?
【发布时间】:2011-05-20 20:16:58
【问题描述】:

我正在尝试使用 django 连接到远程 mysql 数据库。
文档指定需要先打开 SSH 隧道才能连接到数据库。
是否有 python 库可以在设置某些设置时打开 SSH 隧道?

【问题讨论】:

标签: python django ssh-tunnel


【解决方案1】:

您可以尝试paramikoforward 功能。有关 paramiko 的概述,请参阅 here

【讨论】:

  • forward.py 链接不可用。
  • 概述链接指向一篇似乎已被删除的文章。很遗憾,因为有 很多 人链接到那篇文章。您可以尝试搜索它的副本,但我运气不佳。提到了here,谷歌找到了this,但由于我工作的互联网设置,我无法确认任何一种选择。
【解决方案2】:

尝试使用sshtunnel package

这很简单:

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

披露:我是这个包的作者和维护者。

【讨论】:

    【解决方案3】:

    这是 Python3 的代码 sn-p(但您应该能够毫无困难地将其改造成 Python2)。它在单独的线程中运行 SSH 隧道;然后主线程通过 SSH 隧道获取网络流量。

    在本例中,ssh 隧道将本地端口 2222 转发到 localhost 上的端口 80。主要活动包括跑步

    curl http://localhost:2222
    

    即,从端口 2222 获取网页。

    类 SshTunnel 初始化有 4 个参数,本地和远程端口、远程用户和远程主机。它所做的就是通过以下方式启动 SSH:

    ssh -N -L localport:remotehost:remoteport remoteuser@remotehost
    

    为了完成这项工作,您需要为 remoteuser@remotehost 进行无密码登录(通过远程服务器上已知的 ~/.ssh/id_rsa.pub)。 这样运行的 ssh 隧道在一个线程上;主要任务必须在另一个。 ssh 隧道线程被标记为守护进程,因此一旦主要活动终止,它将自动停止。

    我没有提供完整的 MySQL 连接示例,因为它应该是不言自明的。一旦 SshTunnel 设置了本地 TCP 端口,您就可以连接到它——无论是通过您的 MySQL 客户端、curl 还是其他方式。

    import subprocess
    import time
    import threading
    
    class SshTunnel(threading.Thread):
        def __init__(self, localport, remoteport, remoteuser, remotehost):
            threading.Thread.__init__(self)
            self.localport = localport      # Local port to listen to
            self.remoteport = remoteport    # Remote port on remotehost
            self.remoteuser = remoteuser    # Remote user on remotehost
            self.remotehost = remotehost    # What host do we send traffic to
            self.daemon = True              # So that thread will exit when
                                            # main non-daemon thread finishes
    
        def run(self):
            if subprocess.call([
                'ssh', '-N',
                       '-L', str(self.localport) + ':' + self.remotehost + ':' + str(self.remoteport),
                       self.remoteuser + '@' + self.remotehost ]):
                raise Exception ('ssh tunnel setup failed')
    
    
    if __name__ == '__main__':
        tunnel = SshTunnel(2222, 80, 'karel', 'localhost')
        tunnel.start()
        time.sleep(1)
        subprocess.call(['curl', 'http://localhost:2222'])
    

    【讨论】:

      【解决方案4】:

      这是一个可以放入代码中的小类:

      import subprocess
      import random
      import tempfile
      
      class SSHTunnel:
      
          def __init__(self, host, user, port, key, remote_port):
              self.host = host
              self.user = user
              self.port = port
              self.key = key
              self.remote_port = remote_port
              # Get a temporary file name
              tmpfile = tempfile.NamedTemporaryFile()
              tmpfile.close()
              self.socket = tmpfile.name
              self.local_port = random.randint(10000, 65535)
              self.local_host = '127.0.0.1'
              self.open = False
      
          def start(self):
              exit_status = subprocess.call(['ssh', '-MfN',
                  '-S', self.socket,
                  '-i', self.key,
                  '-p', self.port,
                  '-l', self.user,
                  '-L', '{}:{}:{}'.format(self.local_port, self.local_host, self.remote_port),
                  '-o', 'ExitOnForwardFailure=True',
                  self.host
              ])
              if exit_status != 0:
                  raise Exception('SSH tunnel failed with status: {}'.format(exit_status))
              if self.send_control_command('check') != 0:
                  raise Exception('SSH tunnel failed to check')
              self.open = True
      
          def stop(self):
              if self.open:
                  if self.send_control_command('exit') != 0:
                      raise Exception('SSH tunnel failed to exit')
                  self.open = False
      
          def send_control_command(self, cmd):
              return subprocess.check_call(['ssh', '-S', self.socket, '-O', cmd, '-l', self.user, self.host])
      
          def __enter__(self):
              self.start()
              return self
      
          def __exit__(self, type, value, traceback):
              self.stop()
      

      下面是你可以如何使用它,例如 MySQL(通常是 3306 端口):

      with SSHTunnel('database.server.com', 'you', '22', '/path/to/private_key', '3306') as tunnel:
          print "Connected on port {} at {}".format(tunnel.local_port, tunnel.local_host)
      

      【讨论】:

      • 我不得不使用-o ExitOnForwardFailure=yes 而不是-o ExitOnForwardFailure=True。除此之外,一个有用的小课堂。谢谢!
      【解决方案5】:

      我是否可以建议尝试使用类似pyngrok 的方法以编程方式为您管理ngrok 隧道?完全披露,我是它的开发者。 SSH 示例here 和Django 示例here,但它就像安装pyngrok 一样简单:

      pip install pyngrok
      

      并使用它:

      from pyngrok import ngrok
      
      # <NgrokTunnel: "tcp://0.tcp.ngrok.io:12345" -> "localhost:22">
      ssh_tunnel = ngrok.connect(22, "tcp")
      

      或者暴露你的 MySQL 数据库:

      # <NgrokTunnel: "tcp://0.tcp.ngrok.io:12346" -> "localhost:3306">
      mysql_tunnel = ngrok.connect(3306, "tcp")
      

      或者暴露给 Django 开发服务器:

      # <NgrokTunnel: "http://<public_sub>.ngrok.io" -> "http://localhost:8000">
      http_tunnel = ngrok.connect(8000)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-04-02
        • 2016-02-17
        • 1970-01-01
        • 2017-07-10
        • 2011-09-12
        • 2019-02-04
        • 2020-08-06
        • 1970-01-01
        相关资源
        最近更新 更多