Я искал решение этой проблемы в течение последних 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.
Подтверждение концепции клиента и прокси . (Отказ от ответственности за то, что прокси-сервер - это служба, которой я управляю. Не стесняйтесь заменять ссылку, как только любой другой прокси-сервер подтвердит поддержку этого использования.)
Предостережения от этого взлома
- Не используйте это. Лучше использовать реальное решение - IPv6.
- Если прокси-сервер пытается понять HTTP-трафик, он наверняка сломается.
- Полагаться на модифицированный SSH-клиент нехорошо.