Как поменять местами клиент и сервер
Ситуация: у нас есть сервер и нам нужно подключиться к нему с помощью клиента. Но вот незадача: мы почему-то не можем инициировать сессию с клиента. Это может быть по причине NAT, настроек VPN-клиента или просто из-за ACL на МЭ.
Что делать? Давайте попробуем поменять местами клиент и сервер.
Есть две подсети, External и Internal, сервер в сети Internal, но МЭ не пускает запросы из внешней сети.
Собираем стенд:
клиент curl, на "левом" сервере
веб-сервер nginx, на "правом" сервере
Firewall, он же МЭ, OPNsense (это как PfSense, только открытый).
Проверяем. Работает =))
Итак, для решения нашей задачи будем использовать утилиту 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 подключение осуществляется.
Контрольная проверка: выполняем запрос на сервере (на самого себя). Убеждаемся, что запрос идентичен, обращаем внимание на 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
Вот как это выглядит:
На этом все, благодарю за внимание!