如何在Python中通过ssh进行SSH交换? [英] How to ssh over http proxy in Python?

查看:283
本文介绍了如何在Python中通过ssh进行SSH交换?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将Python脚本改编为独立于操作系统并在Windows上运行.我已将其ssh系统调用更改为对 paramiko 函数的调用.我陷入了HTTP代理身份验证的问题.在Unix(实际上是Cygwin)环境中,我将使用〜/.ssh/config

I am adapting a Python script to be OS independent and run on Windows. I have changed its ssh system calls to calls to paramiko functions. I am stuck with the issue of http proxy authentication. In Unix (actually Cygwin) environment I would use ~/.ssh/config

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

是否有使用paramiko(或Python ssh模块)使用或不使用开瓶器来获取相同内容的方法? 这篇文章似乎暗示了这一点,但我不知道如何做.

Is there a way to obtain the same using paramiko (or the Python ssh module) either using or not using corkscrew? This post seems to suggest that, but I don't know how.

注意:我位于仅允许使用端口80的防火墙后面.我需要控制Amazon ec2实例,因此我在这些计算机上配置了sshd服务器以侦听端口80.原型,但我想拥有一个无需Cygwin即可运行的Python脚本.

Note: I am behind a firewall that allows me to use only port 80. I need to control Amazon ec2 instances so I configured the sshd server on those machines to listen to port 80. Everything is working fine in my cygwin+corkscrew prototype, but I would like to have a Python script that works without Cygwin.

推荐答案

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

You can use any pre-established session to paramiko via the sock parameter in SSHClient.connect(hostname,username,password,...,sock).

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

Below is a code-snippet that tunnels SSH via HTTP-Proxy-Tunnel (HTTP-CONNECT). At first the connection to the proxy is established and the proxy is instructed to connect to localhost:22. The result is a TCP tunnel over the established session that is usually used to tunnel SSL but can be used for any tcp based protocol.

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

This scenario works with a default installation of tinyproxy with Allow <yourIP> and ConnectPort 22 being set in /etc/tinyproxy.conf. The proxy and the sshd are running on the same host in my example but all you need is any proxy that allows you to CONNECT to your ssh port. Usually this is restricted to port 443 (hint: if you make your sshd listen on 443 this will work with most of the public proxies even thought I do not recommend to do this for interop and security reasons). If this ultimately allows you to bypass your firewall depends on what kind of firewall is employed. If there's no DPI/SSL-Interception features involved, you should be fine. If there's SSL-Interception involved you could still try to tunnel it via ssl or as part of HTTP payload :)

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

此处 查看全文

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆