Как использовать ssh через HTTP-прокси в Python Paramiko?

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

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

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

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


person markolopa    schedule 07.05.2012    source источник


Ответы (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)

person tintin    schedule 17.11.2015

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

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

person larsks    schedule 07.05.2012
comment
OP запросил поддержку Windows, но согласно Paraproxy 1.2 README: Portability Paraproxy требует, чтобы сокеты домена unix творили чудеса. В результате поддержка доступна только для Linux и MacOS (это может измениться в зависимости от потребности в поддержке Win32). - person This; 08.05.2012
comment
Он по-прежнему демонстрирует технику исправления Paramiko с поддержкой прокси. Надеюсь, кто-нибудь даст лучший ответ. - person larsks; 08.05.2012