如何在 Python Paramiko 中通过 HTTP 代理 ssh?

Posted

技术标签:

【中文标题】如何在 Python Paramiko 中通过 HTTP 代理 ssh?【英文标题】:How to ssh over HTTP proxy in Python Paramiko? 【发布时间】:2012-05-16 08:37:31 【问题描述】:

我正在调整 Python 脚本,使其独立于操作系统并在 Windows 上运行。我已将其 ssh 系统调用更改为对 paramiko 函数的调用。我被http代理身份验证的问题所困扰。在 Unix(实际上是 Cygwin)环境中,我会使用 ~/.ssh/config

Host *
    ProxyCommand corkscrew http-proxy.example.com 8080 %h %p

有没有办法使用 paramiko(或 Python ssh 模块)使用或不使用开瓶器来获得相同的效果? This post 似乎暗示了这一点,但我不知道如何。

注意:我位于只允许使用端口 80 的防火墙后面。我需要控制 Amazon ec2 实例,因此我将这些机器上的 sshd 服务器配置为监听端口 80。在我的 cygwin+corkscrew 中一切正常原型,但我想要一个没有 Cygwin 的 Python 脚本。

【问题讨论】:

【参考方案1】:

您可以通过SSHClient.connect(hostname,username,password,...,sock) 中的sock 参数使用任何预先建立的会话到paramiko。

下面是一个代码-sn-p,它通过 HTTP-Proxy-Tunnel (HTTP-CONNECT) 建立 SSH 隧道。首先建立到代理的连接,并指示代理连接到 localhost:22。结果是已建立会话上的 TCP 隧道,通常用于隧道 SSL,但可用于任何基于 tcp 的协议。

此方案适用于 tinyproxy 的默认安装,Allow <yourIP>ConnectPort 22/etc/tinyproxy.conf 中设置。在我的示例中,代理和 sshd 在同一主机上运行,​​但您需要的只是任何允许您将CONNECT 连接到您的 ssh 端口的代理。通常这仅限于端口 443(提示:如果您让 sshd 在 443 上侦听,这将适用于大多数公共代理,即使出于互操作和安全原因我不建议这样做)。如果这最终允许您绕过防火墙,则取决于所采用的防火墙类型。如果不涉及 DPI/SSL 拦截功能,你应该没问题。如果涉及 SSL 拦截,您仍然可以尝试通过 ssl 或作为 HTTP 有效负载的一部分进行隧道传输:)

import paramiko
import socket
import logging

logging.basicConfig(loglevel=logging.DEBUG)
LOG = logging.getLogger("xxx")

def http_proxy_tunnel_connect(proxy, target,timeout=None):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(timeout)
        sock.connect(proxy)
        LOG.debug("connected")
        cmd_connect = "CONNECT %s:%d HTTP/1.1\r\n\r\n"%target
        LOG.debug("--> %s"%repr(cmd_connect))
        sock.sendall(cmd_connect)
        response = []
        sock.settimeout(2) # quick hack - replace this with something better performing.
        try: 
            # in worst case this loop will take 2 seconds if not response was received (sock.timeout)
            while True:
                chunk = sock.recv(1024)
                if not chunk: # if something goes wrong
                    break
                response.append(chunk)
                if "\r\n\r\n" in chunk: # we do not want to read too far ;)
                    break
        except socket.error, se:
            if "timed out" not in se:
                response=[se]
        response = ''.join(response)
        LOG.debug("<-- %s"%repr(response))
        if not "200 connection established" in response.lower():
            raise Exception("Unable to establish HTTP-Tunnel: %s"%repr(response))
        return sock

if __name__=="__main__":
    LOG.setLevel(logging.DEBUG)
    LOG.debug("--start--")
    sock = http_proxy_tunnel_connect(proxy=("192.168.139.128",8888), 
                                     target=("192.168.139.128",22),
                                     timeout=50)
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname="192.168.139.128",sock=sock, username="xxxx", password="xxxxx")
    print "#> whoami \n%s"% ssh.exec_command("whoami")[1].read()

输出:

DEBUG:xxx:--start--
DEBUG:xxx:connected
DEBUG:xxx:--> 'CONNECT 192.168.139.128:22 HTTP/1.1\r\n\r\n'
DEBUG:xxx:<-- 'HTTP/1.0 200 Connection established\r\nProxy-agent: tinyproxy/1.8.3\r\n\r\n'
#> whoami 
root

hereare 有关如何通过代理进行隧道传输的其他一些资源。只需做任何需要建立隧道并将套接字传递给SSHClient.connect(...,sock)

【讨论】:

【参考方案2】:

有paraproxy,它实现了对Paramiko的代理支持。

您链接到的帖子暗示 Paramiko 可以在任意套接字上操作,但情况似乎并非如此。事实上,paraproxy 的工作原理是完成替换 paramiko 内部的特定方法,因为现有代码只是调用socket.socket() 来获取套接字,并且不提供任何挂钩代理的方式。

【讨论】:

OP 要求 Windows 支持,但根据 Paraproxy 1.2 README:可移植性 Paraproxy 需要 unix 域套接字才能做到这一点。因此,支持仅适用于 Linux 和 MacOS(这可能会根据对 Win32 支持的需求而改变)。 它仍然演示了一种使用代理支持修补 Paramiko 的技术。希望有人能提供更好的答案。

以上是关于如何在 Python Paramiko 中通过 HTTP 代理 ssh?的主要内容,如果未能解决你的问题,请参考以下文章

如何在python中通过引用调用静态方法[重复]

如何在python中通过命令行给出元组

在 Python 中通过 ElementTree 解析 xml 时如何保留命名空间

如何在C ++中通过其名称获取进程句柄?

如何在 Python 中通过身份验证从 URL 获取 CSV 文件

如何在python中通过正则表达式获取dict值