Ситуация: у нас есть сервер и нам нужно подключиться к нему с помощью клиента. Но вот незадача: мы почему-то не можем инициировать сессию с клиента. Это может быть по причине NAT, настроек VPN-клиента или просто из-за ACL на МЭ.

Что делать? Давайте попробуем поменять местами клиент и сервер.

Есть две подсети, External и Internal, сервер в сети Internal, но МЭ не пускает запросы из внешней сети.

Схема стенда. Надо обратиться к серверу справа, но МЭ не пускает. Зато пускает обратно
Схема стенда. Надо обратиться к серверу справа, но МЭ не пускает. Зато пускает обратно

Собираем стенд:

  • клиент curl, на "левом" сервере

  • веб-сервер nginx, на "правом" сервере

  • Firewall, он же МЭ, OPNsense (это как PfSense, только открытый).

Проверяем. Работает =))

Журнал на МЭ. Слева на право блокирует, справа налево пропускает.
Журнал на МЭ. Слева на право блокирует, справа налево пропускает.
Правила для интерфейса LAN. Настройки по умолчанию, разрешаем все
Правила для интерфейса LAN. Настройки по умолчанию, разрешаем все
Правил для интерфейса WAN. По умолчанию запрещено все
Правил для интерфейса WAN. По умолчанию запрещено все

Итак, для решения нашей задачи будем использовать утилиту socat. Socat — это механизм для соединения двух потоков данных. Полезные ссылки по инструменту:

https://linux.die.net/man/1/socat

https://www.redhat.com/sysadmin/getting-started-socat

https://bitsanddragons.wordpress.com/2020/06/05/address-already-in-use-socat-not-working-on-osx/

На "левом" узле (клиент, который должен стать сервером) запускаем следующую команду:

sudo socat -d -d TCP4-LISTEN:81,reuseaddr TCP4-LISTEN:82,fork,reuseaddr

Где -d -d - это флаги дебага, расширенного вывода.

TCP4-LISTEN означает, что socat должен слушать по протоколу TCP и IPv4, далее через знак «:» указывается адрес и порт. Правда, в нашем случае адрес можно опустить, тогда он будет слушать по всем адресам.

reuseaddr — разрешает другим сокетам связываться с этим же адресом, даже если он используется

fork — после установления соединения канал обрабатывается в дочернем процессе; то есть создается много дочерних процессов для разных соединений.

Что такое sudo, наверное, объяснять не надо =))

Получается, что socat сначала будет ждать, когда к нему подключатся по порту 81, а потом будет ждать, когда к нему подключатся по порту 82. И когда это произойдет, он будет перенаправлять трафик между этими двумя соединениями.

Итак, запускаем. Видим следующий вывод:

socat[150853] N listening on AF=2 0.0.0.0:81

Ждет, когда к нему подключится "правый" сервер (сервер, который должен стать клиентом). Если что, N – это notify, уровень логирования (W - warning, E - error). Для того, чтобы такие записи видеть, мы дебаг и включали.

Переходим на "Правый" сервер и выполняем команду:

sudo socat -d -d TCP4:192.168.200.129:81 TCP4:127.0.0.1:80

Тут мы видим, что вместо TCP4-LISTEN указано просто TCP4. Это означает, что в данном случае socat сам инициирует подключение.

Первый адрес — это подключение через межсетевой экран от «правого» сервера к «левому», по разрешенной зеленой стрелке.

Второй адрес — это подключение к локальному веб-серверу.

Почему же тут нет fork, reuseaddr? Потому что эти соединения будут единственны и постоянны. Один раз подключились к удаленному серверу и веб-серверу — и работаем.

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

Итак, запустили. В журнале видим, что он установил подключение к "левому" серверу по порту 81, а также подключился к веб-серверу на локальной машине по порту 80:

socat[620719] N successfully connected from local address AF=2 192.168.100.168:58118
socat[620719] N opening connection to AF=2 127.0.0.1:80
socat[620719] N successfully connected from local address AF=2 127.0.0.1:35176

Возвращаемся на «левый» сервер и смотрим что там.

socat[150853] N accepting connection from AF=2 192.168.100.168:39974 on AF=2 192.168.200.129:81

socat[150853] N listening on AF=2 0.0.0.0:82

Первая строчка – он дождался подключения на первый адрес.

Вторая строчка – он начал слушать второй адрес.

Отлично, серверы друг друга видят. Теперь давайте попробуем с клиента обратиться к веб-серверу. На МЭ видим заблокированные попытки пройти напрямую с клиента на сервер, а также успешное подключение в обратную сторону (от сервера к клиенту) по порту 81, как мы указывали в команде socat.

В журнале МЭ мы видим, что сервер инициирует подключение к клиенту
В журнале МЭ мы видим, что сервер инициирует подключение к клиенту

А непосредственно на клиенте… напрямую не работает, зато через localhost подключение осуществляется.

Запросы с клиента. Напрямую не работает, зато через localhost подключение осуществляется
Запросы с клиента. Напрямую не работает, зато через localhost подключение осуществляется

Контрольная проверка: выполняем запрос на сервере (на самого себя). Убеждаемся, что запрос идентичен, обращаем внимание на Content-Length.

Запросы с сервера на свой адрес. Ответы совпадают с тем, что видит клиент
Запросы с сервера на свой адрес. Ответы совпадают с тем, что видит клиент

Вывод

Итак, все работает. Мы поменяли местами клиент и сервер с целью инициировать подключения к клиенту со стороны сервера и использовали при этом утилиту socat.

Команда на клиенте: sudo socat -d -d TCP4-LISTEN:81,reuseaddr TCP4-LISTEN:82,fork,reuseaddr

Команда на сервере: sudo socat -d -d TCP4:192.168.200.129:81 TCP4:127.0.0.1:80

Вот как это выглядит:

А вот как это работает
А вот как это работает

На этом все, благодарю за внимание!

Комментарии (9)


  1. perryshe
    19.08.2024 16:53

    Первоначальная причина не найдена? Т.е. просто обошли?


    1. Ghaeskaerr Автор
      19.08.2024 16:53

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


      1. eri
        19.08.2024 16:53

        Я через сокат онлайн кассы прокидывал через интернет на сервер( который для касс клиент). Но сокат имеет свойства занимать тцп соединение и зависать на нем. И после смерти это соединение остается ещё в каком-то зависшем состоянии. Перезапускается только вместе с компьютером.

        Переписал на питоне. Метода работает.


  1. IkaR49
    19.08.2024 16:53

    А чем не устроил реверсивный прокси по SSH? Старый, проверенный, достаточно безопасный способ, и более простой.


    1. Ghaeskaerr Автор
      19.08.2024 16:53
      +1

      Хотелось с Сокатом поиграться... К тому же про sshd материала полно, а по socat я не нашел


  1. CapitaBlack
    19.08.2024 16:53

    Можно сделать на cloudflared


    1. Ghaeskaerr Автор
      19.08.2024 16:53

      А он к их сервису не привязан?


  1. FSA
    19.08.2024 16:53

    Читая подобные заметки, хочется передать привет всем противникам IPv6. При такой схеме, типичной для IPv4, приходится городить костыли вместо того, чтобы просто открыть доступ к нужному IP и порту снаружи.

    А в целом, да. Идея использовать дешёвый VPS с белым IPv4 и пробросить через него нужные порты вполне рабочая. Вариантов решений масса. Можно даже пользоваться подобной схемой, когда ваш сервер не имеет на шлюзе постоянного IP.


    1. Ghaeskaerr Автор
      19.08.2024 16:53

      Не совсем понял, а при чем тут ipv4? Только в том, что приходится прятать адреса серверов за NAT?