Как SSH через HTTP прокси в Python?

Я адаптирую скрипт Python, чтобы он не зависел от ОС и работал в Windows. Я изменил системные вызовы ssh на вызовы функций paramiko. Я застрял с проблемой проверки подлинности прокси http. В среде Unix (фактически Cygwin) я бы использовал ~/.ssh/config

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

Есть ли способ получить то же самое, используя paramiko (или модуль Python ssh), используя или не используя corkscrew? Этот пост, кажется, предлагает это, но я не знаю как.

Примечание: я нахожусь за брандмауэром, который позволяет мне использовать только порт 80. Мне нужно контролировать экземпляры Amazon ec2, поэтому я настроил сервер sshd на этих машинах для прослушивания порта 80. В моем прототипе cygwin+corkscrew все работает нормально, но Я хотел бы иметь скрипт Python, который работает без Cygwin.

2 ответа

Вы можете использовать любой заранее установленный сеанс для paramiko через sock параметр в SSHClient.connect(hostname,username,password,...,sock),

Ниже приведен фрагмент кода, который туннелирует SSH через HTTP-Proxy-Tunnel (HTTP-CONNECT). Сначала соединение с прокси устанавливается, и прокси получает указание подключиться к 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

Вот некоторые другие ресурсы о том, как туннелировать через прокси. Просто сделайте все необходимое, чтобы установить туннель, и передайте сокет SSHClient.connect(...,sock)

Есть paraproxy, который реализует поддержку прокси для Paramiko.

Пост, на который вы ссылаетесь, предполагает, что Paramiko может работать через произвольный сокет, но это не так. Фактически, paraproxy работает путем завершения замены определенных методов внутри paramiko, поскольку существующий код просто вызывает socket.socket() получить сокет и не предлагает никакого способа перехвата прокси.

Другие вопросы по тегам