Я очень полюбил обратные HTTP-прокси в нашей среде разработки и считаю, что обратный прокси-сервер виртуального хоста на основе DNS весьма полезен. Наличие только одного порта (и стандартного) на брандмауэре значительно упрощает управление.
Я хотел бы найти что-то подобное для SSH-соединений, но мне не повезло. Я бы предпочел не просто использовать SSH-туннелирование, поскольку для этого требуется открытие диапазонов портов, отличных от стандартных. Есть ли что-нибудь, что может это сделать?
Может ли HAProxy это сделать?
Я не верю, что SSH на основе имен - это то, что станет возможным с учетом того, как работает протокол.
Вот несколько альтернатив.
Вы можете настроить хост, который отвечает за порт 22, как шлюз. Затем вы можете настроить ssh-сервер для пересылки запросов внутрь на основе ключа. SSH Шлюз пример с ключами
Вы можете настроить своего клиента на использование этого хоста в качестве прокси. То есть, он будет использовать ssh для хоста шлюза, а затем использовать этот хост для подключения к внутреннему хосту. Прокси SSH с клиентом конфигурация.
Вы также можете настроить простой HTTP-прокси на периферии. Затем используйте это, чтобы разрешить входящие соединения. SSH через HTTP прокси.
Очевидно, что, учитывая все вышесказанное, очень важно правильно настроить и заблокировать шлюз.
Я искал решение этой проблемы в течение последних 16 месяцев. Но каждый раз, когда я смотрю, кажется невозможным сделать это с помощью протокола SSH, как указано в соответствующих RFC и реализовано в основных реализациях.
Однако, если вы хотите использовать слегка модифицированный клиент SSH и хотите использовать протоколы способом, который не совсем предполагался при их разработке, тогда это возможно. Подробнее об этом ниже.
Почему это невозможно
Клиент не отправляет имя хоста как часть протокола SSH.
Он может отправлять имя хоста как часть поиска DNS, но это может быть кэшировано, и путь от клиента через преобразователи к авторитетным серверам может не пересекать прокси, и даже если это так, нет надежного способа связать определенные запросы DNS с конкретные клиенты SSH.
С самим протоколом SSH тоже нет ничего особенного. Вы должны выбрать сервер, даже не увидев баннер версии SSH от клиента. Вы должны отправить баннер клиенту, прежде чем он отправит что-либо на прокси. Баннеры с серверов могут быть разными, и у вас нет возможности угадать, какой из них правильный.
Даже если этот баннер отправляется в незашифрованном виде, вы не можете его изменить. Каждый бит этого баннера будет проверяться во время установки соединения, поэтому вы немного позже вызовете сбой соединения.
Вывод для меня довольно ясен: нужно что-то изменить на стороне клиента, чтобы это соединение работало.
Большинство обходных путей инкапсулируют трафик SSH внутри другого протокола. Можно также представить себе дополнение к самому протоколу SSH, в котором баннер версии, отправляемый клиентом, включает имя хоста. Это может оставаться совместимым с существующими серверами, так как часть баннера в настоящее время указана как поле идентификации свободной формы, и хотя клиенты обычно ждут баннера версии с сервера перед отправкой своего собственного, протокол действительно разрешает клиенту отправлять свой баннер первый. Некоторые последние версии ssh-клиента (например, в Ubuntu 14.04) отправляют баннер, не дожидаясь его появления на сервере.
Я не знаю ни одного клиента, который предпринял бы шаги для включения имени хоста сервера в этот баннер. Я отправил патч к Список рассылки OpenSSH чтобы добавить такую функцию. Но он был отклонен из-за желания не раскрывать имя хоста никому, отслеживающему трафик SSH. Поскольку секретное имя хоста принципиально несовместимо с работой прокси на основе имени, не ожидайте увидеть официальное расширение SNI для протокола SSH в ближайшее время.
Реальное решение
На самом деле лучшим решением для меня было использование IPv6.
С IPv6 я могу иметь отдельный IP-адрес, назначенный каждому серверу, поэтому шлюз может использовать IP-адрес назначения, чтобы узнать, на какой сервер отправлять пакет. Иногда клиенты SSH могут работать в сетях, где единственный способ получить адрес IPv6 - использовать Teredo. Известно, что Teredo ненадежен, но только в том случае, если в конце соединения IPv6 используется общедоступное реле Teredo. Можно просто поставить ретранслятор Teredo на шлюз, где вы будете запускать прокси. Miredo можно установить и настроить как реле менее чем за пять минут.
Обходной путь
Вы можете использовать хост прыжка / хост бастиона. Этот подход предназначен для случаев, когда вы не хотите открывать SSH-порт отдельных серверов напрямую для общедоступного Интернета. У него есть дополнительное преимущество, заключающееся в сокращении количества внешних IP-адресов, необходимых для SSH, поэтому его можно использовать в этом сценарии. Тот факт, что это решение, предназначенное для добавления еще одного уровня защиты по соображениям безопасности, не мешает вам использовать его, когда вам не нужна дополнительная безопасность.
ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target
Грязный взлом, чтобы заставить его работать, если реальное решение (IPv6) находится за пределами вашей досягаемости
Взлом, который я собираюсь описать, следует использовать только в крайнем случае. Прежде чем вы даже подумаете об использовании этого хака, я настоятельно рекомендую получить IPv6-адрес для каждого из серверов, которые вы хотите получить извне через SSH. Используйте IPv6 в качестве основного метода для доступа к вашим SSH-серверам и используйте этот прием только тогда, когда вам нужно запустить SSH-клиент из сети, поддерживающей только IPv4, где вы не имеете никакого влияния на развертывание IPv6.
Идея состоит в том, что трафик между клиентом и сервером должен быть абсолютно допустимым трафиком SSH. Но прокси достаточно знать о потоке пакетов, чтобы идентифицировать имя хоста. Поскольку SSH не определяет способ отправки имени хоста, вы можете вместо этого рассмотреть другие протоколы, которые предоставляют такую возможность.
И HTTP, и HTTPS позволяют клиенту отправлять имя хоста до того, как сервер отправит какие-либо данные. Теперь вопрос в том, можно ли создать поток байтов, который одновременно действителен как SSH-трафик и как HTTP, так и HTTPS. HTTP это в значительной степени не запускается, но HTTP возможен (для достаточно либеральных определений HTTP).
SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$:
Host: example.com
Для вас это похоже на SSH или HTTP? Это SSH и полностью совместим с RFC (за исключением того, что некоторые двоичные символы были немного искажены при рендеринге SF).
Строка версии SSH включает поле комментария, которое в приведенном выше примере имеет значение / HTTP/1.1
. После новой строки SSH содержит некоторые двоичные пакетные данные. Первый пакет - это MSG_SSH_IGNORE
сообщение, отправленное клиентом и игнорируемое сервером. Полезная нагрузка, которую следует игнорировать:
:
Host: example.com
Если прокси-сервер HTTP достаточно либерален в том, что он принимает, то та же последовательность байтов будет интерпретироваться как метод HTTP, называемый SSH-2.0-OpenSSH_6.6.1
а двоичные данные в начале сообщения игнорирования будут интерпретироваться как имя заголовка HTTP.
Прокси-сервер не будет понимать ни метод HTTP, ни первый заголовок. Но он мог понять Host
заголовок, который является всем, что ему нужно для поиска серверной части.
Чтобы это работало, прокси-сервер должен быть спроектирован по принципу, что ему нужно только понимать достаточно HTTP, чтобы найти бэкэнд, и как только бэкэнд будет найден, прокси просто передаст необработанный поток байтов и оставит реальное завершение. HTTP-соединения, которое должно выполняться серверной частью.
Делать так много предположений о HTTP-прокси может показаться преувеличением. Но если вы захотели установить новое программное обеспечение, разработанное с намерением поддерживать SSH, тогда требования к HTTP-прокси не так уж плохи.
В моем случае я обнаружил, что этот метод работает на уже установленном прокси-сервере без изменений кода, конфигурации или чего-либо еще. И это было для прокси, написанного только с HTTP и без SSH.
Доказательство концепции клиент и доверенное лицо. (Отказ от ответственности, что прокси - это служба, управляемая мной. Не стесняйтесь заменить ссылку, как только будет подтверждено, что любой другой прокси поддерживает такое использование.)
Предостережения этого взлома
Я не верю, что это возможно, по крайней мере, так, как вы описали, хотя мне бы хотелось, чтобы меня доказали, что я ошибаюсь. Не похоже, что клиент отправляет имя хоста, к которому он хочет подключиться (по крайней мере, в открытом виде). Похоже, что первым шагом SSH-соединения является настройка шифрования.
Кроме того, у вас могут возникнуть проблемы с проверкой ключа хоста. Клиенты SSH будут проверять ключи на основе IP-адреса, а также имени хоста. У вас будет несколько имен хостов с разными ключами, но с одним и тем же IP-адресом, к которому вы подключаетесь.
Возможное решение - иметь хост-бастион, на котором клиенты могут подключаться к этому компьютеру по ssh, получать обычную (или, при желании, с ограничениями) оболочку, а затем оттуда могут подключаться по ssh к внутренним хостам.
В квартале появился новый ребенок. SSH piper будет маршрутизировать ваше соединение на основе предварительно определенных имен пользователей, которые должны быть сопоставлены с внутренними хостами. Это лучшее решение в контексте обратного прокси.
Мои первые тесты подтвердили, что SSH и SFTP работают.
Мне интересно, нельзя ли изменить Honeytrap (приманку с низким уровнем взаимодействия), в которой есть режим прокси.
Эта приманка может пересылать любой протокол на другой компьютер. Добавление системы виртуального хоста на основе имени (как это реализовано в Apache) могло бы сделать его идеальным обратным прокси для любого протокола, нет?
У меня нет навыков для этого, но, возможно, это был бы отличный проект.
По мере увеличения числа портов / хостов, к которым вы хотите получить доступ за брандмауэром, удобство VPN возрастает.
Но я не люблю VPN.
из-за того, как работает ssh, я думаю, что это невозможно. Подобно https, у вас должны быть разные (внешние) IP-адреса для разных хостов, потому что шлюз не знает, куда вы хотите подключиться, потому что все в ssh зашифровано.