Пробрасывание произвольного IP адреса внутрь виртуальной машины за NAT

в 22:37, , рубрики: linux, networking, policy routing, vpn-туннель, системное администрирование, метки: , , ,

Редко, но метко в моей практике возникает задача, когда нужно быстро поднять сервер с «белым ip» для демонстрации результатов работы или просто для целей тестирования нового функционала перед выкатыванием его в продакшен. Каждый раз арендовать VDS для этого накладно, а связываться с облаками для подобных целей мне не хочется.

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

Чтобы решить задачу наиболее общим способом были пославлены следующие цели:

  • получить на тестовом сервере (srv-test), который находится за NAT, ip адрес, который доступен извне;
  • на тестовом сервере не должно быть артефактов реализации.

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

Пробрасывание произвольного IP адреса внутрь виртуальной машины за NAT
На внешнем сервере был запущен минимальный openvz контейнер, в котором крутится только openvpn. Чтобы отличать этот контейнер от других серверов, я буду называеть его srv-ovpn.

Первичный IP адрес контейнера — 212.78.101.113 — он же по совместительству «белый» ip, по которому должен быть доступен тестовый сервер (буду называть его srv-test) из интернета. Также у srv-ovpn есть дополнительный ip адрес 212.78.101.114 — на этот адрес подключается VPN клиент. В принципе от второго адреса можно отказаться, если немного усложнить policy routing, но у меня были свободные ip и было лениво заморачиваться с подобной экономией.

Адрес шлюза, через который сервер виртуальных машин подключен к интернету — 93.100.17.39. Чтобы srv-ovpn не светил подключение для всех — закрываю его на файерволе

root@srv-ovpn # iptables -A INPUT -p udp -s 93.100.17.39 --dport 1194 -j ACCEPT
root@srv-ovpn # iptables -A INPUT -p udp --dport 1194 -j REJECT
root@srv-ovpn # iptables-save > /etc/sysconfig/iptables

Теперь OpenVPN сервер доступен только для меня и надежно скрыт от посторонних глаз.

Далее поднимаю на локальном сервере виртуалок (назову этот сервера — srv-virt) VPN соединение до openvz контейнера srv-ovpn:

root@srv-virt # service openvpn start vpntunnel

После окончания инициализации появился новый интерфейс tun0, который получил адрес 10.117.9.6. Второй конец туннеля имеет адрес 10.117.9.1. Сетевой адаптер, входящий в VLAN srv-test, виден на сервере srv-virt с именем veth3 и имеет адрес 212.78.101.112.

Чтобы автоматизировать процесс настройки VPN соединения я использовал скрипт /etc/openvpn/vpntunnel.sh, который вызывается OpenVPN при инициализации/деинициализации VPN интерфейса.

#!/bin/sh
TUN_DEV="$2"
TUN_IP="$5"
REMOTE_IP="212.78.101.113"
IPR_BIN="/bin/ip"
IPT_BIN="/sbin/iptables"

if [ ! -r /etc/iproute2/rt_tables ]; then
 echo "File not found: /etc/iproute2/rt_tables"
 echo "iproute2 package must be installed"
 exit 1
fi

case $1 in
    start)
        grep -q '^100tvpntunnel' /etc/iproute2/rt_tables || echo '100tvpntunnel' >> /etc/iproute2/rt_tables
        ${IPR_BIN} rule add from ${REMOTE_IP} table vpntunnel
        ${IPR_BIN} route add default via ${TUN_IP} dev ${TUN_DEV} table vpntunnel
        ${IPR_BIN} route flush cache
        ${IPT_BIN} -t nat -A PREROUTING -d ${TUN_IP} -j DNAT --to-destination ${REMOTE_IP}
        ${IPT_BIN} -t nat -A POSTROUTING -o ${TUN_DEV} -j SNAT --to-source ${TUN_IP}
        ;;
    stop)
        ${IPT_BIN} -t nat -D PREROUTING -d ${TUN_IP} -j DNAT --to-destination ${REMOTE_IP}
        ${IPT_BIN} -t nat -D POSTROUTING -o ${TUN_DEV} -j SNAT --to-source ${TUN_IP}
        ${IPR_BIN} route del default via ${TUN_IP} dev ${TUN_DEV} table vpntunnel
        ${IPR_BIN} route flush cache
        ${IPR_BIN} rule del from ${REMOTE_IP} table vpntunnel
        ;;
    *)
        echo "Incorrect usage"
        exit 1
esac

В конфиг VPN соединения нужно добавить вызов скрипта

...
up "/etc/openvpn/vpntunnel.sh start"
down "/etc/openvpn/vpntunnel.sh stop"
down-pre
...

Аналогичный конфиг OpenVPN должен быть и на сервере srv-ovpn.

Осталось настроить на сервере srv-test адрес 212.78.101.113/31 со шлюзом 212.78.101.112 и все должно заработать.

В этом посте все ip адреса и названия серверов заменены на вымышленные и любое совпадение с реальными — случайность.

Автор: tataranovich

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js