Ситуация: у нас есть сервер и нам нужно подключиться к нему с помощью клиента. Но вот незадача: мы почему-то не можем инициировать сессию с клиента. Это может быть по причине NAT, настроек VPN-клиента или просто из-за ACL на МЭ.
Что делать? Давайте попробуем поменять местами клиент и сервер.
Есть две подсети, External и Internal, сервер в сети Internal, но МЭ не пускает запросы из внешней сети.
![Схема стенда. Надо обратиться к серверу справа, но МЭ не пускает. Зато пускает обратно Схема стенда. Надо обратиться к серверу справа, но МЭ не пускает. Зато пускает обратно](https://habrastorage.org/getpro/habr/upload_files/e7a/0dd/0df/e7a0dd0df260971141e73c02c04671ac.png)
Собираем стенд:
клиент curl, на "левом" сервере
веб-сервер nginx, на "правом" сервере
Firewall, он же МЭ, OPNsense (это как PfSense, только открытый).
Проверяем. Работает =))
![Журнал на МЭ. Слева на право блокирует, справа налево пропускает. Журнал на МЭ. Слева на право блокирует, справа налево пропускает.](https://habrastorage.org/getpro/habr/upload_files/e47/7e0/bd1/e477e0bd1e4c2d64cd4fe3dfef29bd76.png)
![Правила для интерфейса LAN. Настройки по умолчанию, разрешаем все Правила для интерфейса LAN. Настройки по умолчанию, разрешаем все](https://habrastorage.org/getpro/habr/upload_files/6b5/cce/919/6b5cce919fca289fc1a79df1942cd5de.png)
![Правил для интерфейса WAN. По умолчанию запрещено все Правил для интерфейса WAN. По умолчанию запрещено все](https://habrastorage.org/getpro/habr/upload_files/af8/e69/8e8/af8e698e8fa35015187f0b9c97247aa5.png)
Итак, для решения нашей задачи будем использовать утилиту 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.
![В журнале МЭ мы видим, что сервер инициирует подключение к клиенту В журнале МЭ мы видим, что сервер инициирует подключение к клиенту](https://habrastorage.org/getpro/habr/upload_files/75d/6de/ab4/75d6deab41a01704dd7a5a6b48fb9ab7.png)
А непосредственно на клиенте… напрямую не работает, зато через localhost подключение осуществляется.
![Запросы с клиента. Напрямую не работает, зато через localhost подключение осуществляется Запросы с клиента. Напрямую не работает, зато через localhost подключение осуществляется](https://habrastorage.org/getpro/habr/upload_files/1d9/e0d/e6b/1d9e0de6b4c866d4f54738bb67ecef3a.png)
Контрольная проверка: выполняем запрос на сервере (на самого себя). Убеждаемся, что запрос идентичен, обращаем внимание на Content-Length.
![Запросы с сервера на свой адрес. Ответы совпадают с тем, что видит клиент Запросы с сервера на свой адрес. Ответы совпадают с тем, что видит клиент](https://habrastorage.org/getpro/habr/upload_files/a06/3ee/c38/a063eec3859f892c2d5e08687bfc639b.png)
Вывод
Итак, все работает. Мы поменяли местами клиент и сервер с целью инициировать подключения к клиенту со стороны сервера и использовали при этом утилиту 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
Вот как это выглядит:
![А вот как это работает А вот как это работает](https://habrastorage.org/getpro/habr/upload_files/f2f/ddb/8e5/f2fddb8e5aadcf3d691181aca531b3ae.png)
На этом все, благодарю за внимание!
Комментарии (9)
IkaR49
19.08.2024 16:53А чем не устроил реверсивный прокси по SSH? Старый, проверенный, достаточно безопасный способ, и более простой.
Ghaeskaerr Автор
19.08.2024 16:53+1Хотелось с Сокатом поиграться... К тому же про sshd материала полно, а по socat я не нашел
FSA
19.08.2024 16:53Читая подобные заметки, хочется передать привет всем противникам IPv6. При такой схеме, типичной для IPv4, приходится городить костыли вместо того, чтобы просто открыть доступ к нужному IP и порту снаружи.
А в целом, да. Идея использовать дешёвый VPS с белым IPv4 и пробросить через него нужные порты вполне рабочая. Вариантов решений масса. Можно даже пользоваться подобной схемой, когда ваш сервер не имеет на шлюзе постоянного IP.
Ghaeskaerr Автор
19.08.2024 16:53Не совсем понял, а при чем тут ipv4? Только в том, что приходится прятать адреса серверов за NAT?
perryshe
Первоначальная причина не найдена? Т.е. просто обошли?
Ghaeskaerr Автор
Идея действительно возникла, когда надо было доступ получить в обход мэ. Тогда решили по-другому, но идея поменять местами клиент и сервер осталась, в порядке технического извращения. В итоге получилось это реализовать на стенде.
eri
Я через сокат онлайн кассы прокидывал через интернет на сервер( который для касс клиент). Но сокат имеет свойства занимать тцп соединение и зависать на нем. И после смерти это соединение остается ещё в каком-то зависшем состоянии. Перезапускается только вместе с компьютером.
Переписал на питоне. Метода работает.