Skip to main content
 首页 » 编程设计

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

2025年05月04日22softidea

我正在调整一个 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 中一切正常原型(prototype),但我想要一个无需 Cygwin 即可运行的 Python 脚本。

请您参考如下方法:

您可以通过 sock 将任何预先建立的 session 用于 paramiko SSHClient.connect(hostname,username,password,...,sock) 中的参数.

下面是一个代码片段,它通过 HTTP-Proxy-Tunnel (HTTP-CONNECT) 对 SSH 进行隧道传输。首先建立与代理的连接,并指示代理连接到 localhost:22。结果是建立 session 上的 TCP 隧道,通常用于隧道 SSL,但可用于任何基于 tcp 的协议(protocol)。

此方案适用于 tinyproxy 的默认安装与 Allow <yourIP>和 ConnectPort 22正在设置 /etc/tinyproxy.conf .在我的示例中,代理和 sshd 在同一主机上运行,​​但您所需要的只是允许您使用 CONNECT 的任何代理。到你的 ssh 端口。通常这仅限于端口 443(提示:如果你让你的 sshd 在 443 上监听,这将适用于大多数公共(public)代理,即使出于互操作和安全原因我不建议这样做)。如果这最终允许您绕过防火墙,则取决于所使用的防火墙类型。如果不涉及 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 

here are关于如何通过代理隧道的一些其他资源。只需做任何需要的事情来建立你的隧道并将套接字传递给 SSHClient.connect(...,sock)