Перенаправление TCP Nginx на основе имени хоста

С выпуском балансировки нагрузки TCP для версии сообщества Nginx я хотел бы смешать сквозные данные OpenVPN и SSL. Единственный способ для Nginx узнать, как маршрутизировать трафик, - это использовать свое доменное имя.

 vpn1.app.com ─┬─► nginx at 10.0.0.1 ─┬─► vpn1  at 10.0.0.3
 vpn2.app.com ─┤                      ├─► vpn2  at 10.0.0.4
https.app.com ─┘                      └─► https at 10.0.0.5

Я просмотрел руководства по TCP и документация по модулю, но на нее не очень хорошо ссылаются. Если кто-нибудь может указать мне правильное направление, я был бы признателен.

Связанный вопрос о ServerFault: Может ли обратный прокси-сервер использовать SNI с SSL пройти?


person James Wong    schedule 12.01.2016    source источник
comment
Что-то отсутствует или неясно в моем ответе?   -  person cnst    schedule 25.01.2016
comment
-1 за невыполнение обещанной награды, несмотря на то, что был предоставлен исчерпывающий ответ, на который были даны ссылки, и не было запрошено никаких дополнительных разъяснений и не было указано на проблемы   -  person cnst    schedule 27.01.2016
comment
спасибо за то, что, наконец, принял и проголосовал за мой ответ, однако я недоумеваю, почему в ту же минуту некоторые из моих других несвязанных и бесспорных ответов были отвергнуты по абсолютно непонятной причине; Ново ли размещать награду, тихо не присуждать ее без причины, а затем, когда на нее указывают, молчаливо отрицать другие ответы того самого человека, который вам помог?   -  person cnst    schedule 29.01.2016
comment
Stack Overflow - это сайт для вопросов по программированию и разработке. Этот вопрос кажется не по теме, потому что он не о программировании или разработке. См. Какие темы можно задать здесь в Справочном центре. Возможно, Unix и Linux Stack Exchange или Information Security Stack Exchange было бы лучше спросить.   -  person jww    schedule 07.02.2017
comment
@jww Отметьте тему для модерации и помогите закрыть ее.   -  person James Wong    schedule 23.02.2017
comment
@JamesWong, нужен ли SSL на vpn1.app.com, если да, то может ли это быть дикой картой?   -  person rMili    schedule 16.08.2019


Ответы (3)


Предположения

Если я вас правильно понимаю, вы действительно хотите, чтобы nginx прослушивал один IP-адрес и комбинацию TCP-порта (например, listen 10.0.0.1:443), а затем, в зависимости от характеристик входящего трафика TCP-потока, перенаправлял его на один из 3 разных IP-адресов. адреса.

Вы явно не упоминаете, как вы ожидаете, что он будет различать 3 разных домена, но я предполагаю, что вы предполагаете, что это всего лишь TLS, и вы должны захотеть использовать какой-то механизм TLS SNI (указание имени сервера) для доменная дифференциация.

Я считаю, что документация по потокам, представленная на http://nginx.org/docs/, является достаточно авторитетной и исчерпывающей для модулей. на кону (я перечисляю все это здесь, поскольку, по-видимому, еще нет центрального места для перекрестных ссылок, например, пока нет ссылок из модуля "ядра потока" на подмодули (и docs/stream/ просто перенаправляет обратно docs/), что действительно довольно сбивает с толку, поскольку такие вещи, как http://nginx.org/r/upstream, задокументированы только для применения к http, без любое упоминание о применимости к stream, даже если в конце директивы примерно одинаковы):


Отвечать

Обратите внимание, что каждая директива nginx из каждого модуля имеет ограниченное количество применимых Context.

Таким образом, к сожалению, здесь просто нет директивы для отслеживания SNI!

Напротив, фактически задокументировано в stream_core, что, цитируя, "Different servers must listen on different address:port pairs.", как вы могли заметить, это также противоречит тому, как listen работает в более распространенном http_core, и является довольно недвусмысленной ссылкой на тот факт, что в настоящее время для listen в stream не реализована поддержка SNI.


Обсуждение

В качестве предмета обсуждения и предложения по разрешению проблемы предположение, что трафик OpenVPN - это просто TLS с отслеживаемым SNI, также не обязательно верно (но я не слишком знаком с OpenSSL или SNI):

  • Учтите, что даже если сегодня SNI пассивно отслеживается, это явно противоречит обещанию TLS обеспечивать безопасность соединения и, как таковое, может измениться в будущей версии TLS.

  • Для обсуждения, если OpenVPN просто использует соединение TLS, и если он НЕ использует TLS для аутентификации пользователей с помощью пользовательских сертификатов (что значительно усложнит задачу для MitM потока, но все еще несут данные аутентификации все время), то теоретически если бы nginx действительно имел поддержку SNI около listen в stream, то вы, возможно, смогли бы активно MitM это с nginx (поскольку proxy_ssl уже поддерживается в stream_proxy).

Что наиболее важно, я считаю, что OpenVPN лучше всего запускать по собственному протоколу на основе UDP, и в этом случае вы можете использовать один и тот же IP-адрес и номер порта для одного экземпляра https на основе TCP и другого для OpenVPN на основе UDP. без конфликта.

В конце концов, вы можете спросить, для чего же тогда модуль stream был бы полезен? Я считаю, что его целевой аудиторией будет (0) балансировка нагрузки HTTP/2 с несколькими upstream серверами на основе hash IP-адреса клиента, например, и / или (1), более простой и протокольно- независимая замена для stunnel.

person cnst    schedule 23.01.2016
comment
@TorstenBronger, я не думаю, что люди читают текст вопроса, который конкретно касается OpenVPN. Поддерживает ли сам OpenVPN даже SNI? - person cnst; 07.02.2017
comment
@TorstenBronger, Кстати, получается, что OpenSSL не поддерживает SNI, поэтому вопрос спорный, и, следовательно, другой ответ отвечает на другой вопрос. Источник: stackoverflow.com/questions/42078600/ - person cnst; 08.02.2017
comment
После того, как nginx маршрутизируется по имени хоста, OpenVPN больше не нуждается в SNI. - person Torsten Bronger; 08.02.2017
comment
Этот ответ уже устарел. Смотрите другие ответы. - person Richard Kiefer; 02.08.2019
comment
@RichardKiefer У меня есть http-прокси в одном апстриме. Он отлично работает с конфигурацией ответа lochnair, но в журналах прокси вместо записи IP-адреса клиента он регистрирует IP-адрес сервера. Можете ли вы мне помочь, как я могу зарегистрировать IP-адрес клиента? - person Khakhar Shyam; 09.01.2020
comment
@KhakharShyam, извините, я плохо владею этой областью. Подумайте о том, чтобы задать вопрос, касающийся вашей конкретной проблемы. - person Richard Kiefer; 09.01.2020
comment
лучший ответ я нашел вас все мое исследование большое спасибо @cnst - person Paulo Boaventura; 26.12.2020
comment
Существует хорошее введение в stream / tcp от разработчиков nginx в блоге. - person Alex; 09.04.2021

Теперь это возможно с добавлением модуля ngx_stream_ssl_preread, добавленного в Nginx 1.11.5. и модуль ngx_stream_map, добавленный в 1.11.2.

Это позволяет Nginx читать TLS Client Hello и решать на основе расширения SNI, какой бэкэнд использовать.

stream {

    map $ssl_preread_server_name $name {
        vpn1.app.com vpn1_backend;
        vpn2.app.com vpn2_backend;
        https.app.com https_backend;
        default https_default_backend;
    }

    upstream vpn1_backend {
        server 10.0.0.3:443;
    }

    upstream vpn2_backend {
        server 10.0.0.4:443;
    }

    upstream https_backend {
        server 10.0.0.5:443;
    }

    upstream https_default_backend {
        server 127.0.0.1:443;
    }

    server {
        listen 10.0.0.1:443;
        proxy_pass $name;
        ssl_preread on;
    }
}
person Lochnair    schedule 19.10.2016
comment
Для тех, кто приезжает сюда в будущем, думая, что это может сработать для SSH-соединений - не беспокойтесь, SSH-клиенты не передают DNS-имя. - person Dae; 08.03.2017
comment
@Dae, SSH даже не имеет отношения к SSL. - person cnst; 16.08.2017
comment
в таком случае, если я настроил прослушивание обычного https-прокси на 127.0.0.1:443, я могу связать поток с обычным http-прокси в nginx? Например, чтобы использовать nginx в качестве обратного прокси-сервера vpn / httos, а также в качестве прокси-сервера для разгрузки SSL для https. - person Lapsio; 04.10.2017
comment
@Lapsio Я немного не понимаю, какую именно настройку вы пытаетесь достичь, но да, наличие потока TCP перед веб-сервером HTTP работает отлично. - person Lochnair; 05.10.2017
comment
@Lochnair да, я сделал это, хостинг потока и http прокси на одной машине работает нормально. К сожалению, хотя кажется, что клиент OpenVPN (по крайней мере, RouterOS и NetworkManager в реализациях Linux) не пересылает имя хоста в SNI, поэтому мне пришлось по умолчанию использовать VPN и занести в белый список все имена хостов https, чтобы явно направить их на HTTP-прокси. И подстановочный знак на карте, похоже, тоже не работает: c - person Lapsio; 06.10.2017
comment
Мне пришлось установить proxy_protocol on;, чтобы это работало, возможно, другим нужно сделать то же самое ... - person Carel; 18.01.2018
comment
@Carel proxy_protocol здесь нужен только в том случае, если вы используете его на вышестоящих серверах. - person Lochnair; 18.01.2018
comment
Можно ли указать IP-адрес источника для каждого бэкэнда? - person yegle; 03.08.2019
comment
@Lochnair, нужен ли нам SSL на vpn1.app.com, если да, может ли это быть дикой картой? - person rMili; 16.08.2019
comment
@rMili У вас есть, и да, это возможно. На данном этапе сертификаты еще даже не отправлены, так что это не имеет значения. - person Lochnair; 24.08.2019
comment
Даже этот ответ выглядит хорошо, кажется, что клиент OpenVpn не отправляет SNI, я пробовал этот подход, но я не могу заставить его работать: stackoverflow.com/q/57713606/4175637 - person Symon; 29.08.2019
comment
@Lochnair У меня http прокси в одном апстриме. Он отлично работает с указанной выше конфигурацией, но в журналах прокси вместо регистрации IP-адреса клиента он регистрирует IP-адрес сервера. Можете ли вы мне помочь, как я могу зарегистрировать IP-адрес клиента? - person Khakhar Shyam; 09.01.2020
comment
Очень важно отметить, что серверы, на которые нацелены восходящие потоки, не должны использовать http2. Потому что, если они используют один и тот же сертификат (например, тот же сертификат с подстановочными знаками, что и в моем случае), то трафик, который должен достигать другого хоста, будет отправляться через то же соединение / канал http2. Причины - person turbophi; 01.02.2020
comment
Мне также пришлось сделать load_module /usr/lib/nginx/modules/ngx_stream_module.so; поверх файла конфигурации nginx - person xtrinch; 12.01.2021

Как упоминал @Lochnair, вы можете использовать модуль ngx_stream_map и переменная $ server_addr для решения этой проблемы. Вот мой пример.

Мой IP-адрес хоста 192.168.168.22, и я использую keepalived, привязанные 2 виртуальных IP-адреса к eth0.

$sudo ip a
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
link/ether 5c:f3:fc:b9:f0:84 brd ff:ff:ff:ff:ff:ff
inet 192.168.168.22/24 brd 192.168.168.255 scope global eth0
   valid_lft forever preferred_lft forever
inet 192.168.168.238/32 scope global eth0
   valid_lft forever preferred_lft forever
inet 192.168.168.239/32 scope global eth0
   valid_lft forever preferred_lft forever

$nginx -v
nginx version: nginx/1.13.2

$cat /etc/nginx/nginx.conf
...
stream {
    upstream pod53{
        server 10.1.5.3:3306;
    }
    upstream pod54{
        server 10.1.5.4:3306;
    }

    map $server_addr $x {
        192.168.168.238 pod53;
        192.168.168.239 pod54;
    }
    server {
        listen 3306;
        proxy_pass $x;
    }
}

Таким образом, я могу посещать разные службы MySQL с одним и тем же портом 3306 через разные VIP-адреса. Точно так же, как посещение другой службы HTTP с тем же портом через другой server_name.

192.168.168.238 -> 10.1.5.3
192.168.168.239 -> 10.1.5.4
person aloisio    schedule 29.06.2017
comment
В этом примере выполняется выборка по IP-адресу. Вопрос не в этом, где требуется выбор по имени SNI. Конечно, все намного проще, если вы можете позволить себе отдельный IPv4-адрес для каждого домена HTTPS. - person vog; 06.02.2019