Существует ли обратный прокси-сервер SSH на основе имени виртуального хоста?


36

Я полюбил обратные прокси-серверы HTTP в нашей среде разработки и нашел обратный прокси-сервер виртуального хоста на основе DNS весьма полезным. Если на брандмауэре открыт только один порт (и стандартный), это значительно упрощает управление.

Я хотел бы найти что-то похожее, чтобы делать соединения SSH, но мне не повезло. Я бы предпочел не просто использовать SSH-туннелирование, поскольку для этого требуется открыть диапазоны портов, отличные от стандартных. Есть ли что-нибудь, что может это сделать?

Может ли HAProxy сделать это?


Передача файлов вашего приложения или фактический SSH-доступ к хостам?
Кайл Ходжсон

Цель - прямой SSH-доступ к хосту. Это связано с желанием запустить собственный сервер Mercurial, но наш сервер находится за брандмауэром. Сейчас я работаю над простой настройкой версии HTTP, но я хотел, чтобы коммиты использовали SSH вместо HTTP. Прямой доступ SSH к другим серверам был бы бонусом, если бы это было возможно.
Ахансон

Это такая досадная проблема.
уклон

Насколько я понимаю, HAProxy теперь поддерживает это через SNI. Хотя я пока не смог этого добиться.
Карел

Ответы:


24

Я не верю, что SSH на основе имен будет чем-то возможным, учитывая, как работает протокол.

Вот несколько альтернатив.

  • Вы можете настроить хост, отвечающий за порт 22, который будет действовать как шлюз. Затем вы можете настроить сервер ssh для пересылки запросов внутрь на основе ключа. Пример SSH- шлюза с ключами

  • Вы можете настроить свой клиент для использования этого хоста в качестве прокси. То есть он будет подключаться к хосту шлюза, а затем использовать этот хост для подключения к внутреннему хосту. SSH прокси с настройкой клиента .

  • Вы также можете настроить простой http-прокси на краю. Затем используйте это, чтобы разрешить входящие соединения. SSH через HTTP прокси .

Очевидно, что со всем вышеперечисленным очень важно убедиться, что вы правильно настроили и заблокировали шлюз.


18

Я искал решение этой проблемы в течение последних 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-клиент нехорошо.

Можете ли вы уточнить, что вы подразумеваете под the gateway can use the destination IP address to find out which server to send the packet toрешением IPv6?
Джейкоб Форд

@JacobFord Ключ к пониманию этого предложения заключается в том, что я использую слово шлюз, а не прокси . С IPv6 вы получаете достаточно IP-адресов, чтобы назначить один для каждого хоста, и при этом у вас есть запасные адреса. Все машины, которые вы поставили бы за прокси-сервером, будут иметь свой собственный IP-адрес, к которому вы будете обращаться по именам. Таким образом, вам больше не нужен прокси, клиенты отправляют свой трафик на IP-адрес нужного хоста, и вы можете направлять трафик прямо туда.
Касперд

для обходного пути есть относительно новый командный переключатель для ssh -J, поэтому вы можете использовать: ssh -J user1 @ front user2 @ target
PMN

3

Я не верю, что это было бы возможно, по крайней мере, так, как вы описали, хотя я бы хотел оказаться неправым. Не похоже, что клиент отправляет имя хоста, к которому он хочет подключиться (по крайней мере, в открытом виде). Первый шаг SSH-соединения, похоже, заключается в настройке шифрования.

Кроме того, у вас могут возникнуть проблемы с проверкой ключа хоста. Клиенты SSH будут проверять ключи на основе IP-адреса, а также имени хоста. У вас будет несколько имен хостов с разными ключами, но с тем же IP-адресом, к которому вы подключаетесь.

Возможным решением было бы иметь «бастионный» хост, на котором клиенты могут подключиться по ssh к этой машине, получить обычную (или ограниченную, если необходимо) оболочку, а затем оттуда получить ssh на внутренние хосты.


Бастионная концепция - это то, что у нас сейчас есть, но это проблематично для моего контроля версий (без дополнительных усилий).
Ахансон

Очень раздражает, что ssh не отправляет fqdn и обрабатывает DNS на стороне клиента. Я думаю, что люди не беспокоились о публичном IP-раздувании и NAT, когда создавалось большинство протоколов на уровне приложений TCP / IP. Потому что, если серьезно, вы должны уметь делать NAT на основе FQDN с iptables (то есть фильтрами ядра).
уклон

Ключ хоста может быть ключом обратного прокси. Это полезно, если вы доверяете безопасности бэкэнда, так как у вас есть контроль над внутренней сетью и хостами.
rox0r

@bias Нельзя выполнять NAT на основе имени хоста, даже если протокол более высокого уровня отправляет имя хоста. Причина, по которой это не может быть сделано, заключается в том, что выбор бэкэнда должен быть сделан при получении пакета SYN, но имя хоста отправляется только после того, как SYN был обработан и SYN-ACK был возвращен. Однако вы можете использовать очень тонкий прокси прикладного уровня для протоколов, которые отправляют имя хоста, таких как http и https.
Касперд

3

На блоке новый ребенок. SSH piper будет маршрутизировать ваше соединение на основе заранее определенных имен пользователей, которые должны быть сопоставлены с внутренними хостами. Это лучшее решение в контексте обратного прокси.

СШ Пайпер на GitHub

Мои первые тесты подтвердили, SSH и SFTP оба работают.


Это фактически MITM-атака. Я ожидаю, что он сломается в каждой настройке, защищенной от MITM-атак.
Касперд

1
На самом деле принимающей стороной является и хозяин, и человек посередине, таким образом, участвуют только эти двое.
Király István

@ KirályIstván это потрясающий проект и доказательство того, что другие ответы не на 100% правильные. Я создал похожий инструмент на основе github.com/gliderlabs/sshfront и некоторого bash / python (я не могу открыть его, но это не так интересно - bash запускает ssh-клиент, а python используется в качестве обработчика аутентификации). Но у меня есть одна проблема с текущим решением. Он не поддерживает sftp (scp работает). Он зависает при попытке запустить подсистему debug1: Sending subsystem: sftp. Оказывается, передняя часть рубашки не поддерживает подсистемы. Вы поддерживаете подол в СШ Пайпер?
Maciek Sawicki

1
@MaciekSawicki Я не автор. Я просто использую это в своем проекте. .)
Király István

2

Мне интересно, если Honeytrap (honeypot с низким взаимодействием), который имеет режим прокси, не может быть изменен для достижения этой цели.

Этот honeypot может пересылать любой протокол на другой компьютер. Добавление системы vhost на основе имен (реализованной Apache) может сделать ее идеальным обратным прокси-сервером для любого протокола?

У меня нет навыков, чтобы достичь этого, но, возможно, это мог бы быть отличный проект.


отличная идея!
уклон

1
Функция vhost в Apache полагается на то, что клиент отправляет имя хоста до того, как с сервера будут отправлены какие-либо данные. Протокол SSH не включает в себя такое имя хоста, поэтому я не понимаю, как бы вы даже начали реализовывать такую ​​функцию. Но если вы сможете развить свои идеи, я был бы рад реализовать доказательство концепции или сказать вам, почему это не сработает.
Касперд

1

По мере увеличения количества портов / хостов, к которым вы хотите получить доступ через брандмауэр, повышается удобство VPN.

Я не люблю VPN, хотя.


1

из-за того, как работает ssh, я думаю, что это невозможно. Подобно https, у вас должны быть разные (внешние) IP-адреса для разных хостов, потому что шлюз не знает, куда вы хотите подключиться, потому что все в ssh зашифровано

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.