При размещении нескольких VPS (виртуальных серверов) часто возникает задача: публичный IPv4-адрес (от англ. Internet Protocol version 4 – адресный интернет-протокол версии 4) нужен только на одной машине (например, на веб-сервере или балансировщике), а остальные серверы работают во внутренней приватной сети и наружу напрямую выходить не должны. При этом «внутренним» VPS всё равно периодически требуется доступ в интернет – для установки пакетов, обновлений системы, обращения к внешним API (от англ. Application Programming Interface – интерфейс программирования приложения) и т. п.
Решение – организовать GRE-туннель (протокол инкапсуляции сетевых пакетов) между VPS без белого IP (идентификатор устройства в компьютерной сети) и VPS с белым IP, а на шлюзовой VPS включить NAT (MASQUERADE) (преобразование сетевых адресов). В этом случае весь исходящий трафик «внутренних» машин будет уходить в интернет через шлюз под его публичным IP-адресом.
В статье рассматриваются:
- принцип работы схемы и роль GRE;
- пошаговая настройка на примере Ubuntu 24.04;
- сохранение конфигурации между перезагрузками;
- проверка работоспособности;
- расширение схемы на несколько VPS;
- типовые проблемы и их диагностика.
Что такое GRE и зачем он нужен
GRE (Generic Routing Encapsulation) – это протокол туннелирования, позволяющий инкапсулировать произвольные сетевые пакеты внутрь IP. Два сервера, имеющие между собой сетевую связность (в нашем случае – по приватной сети), поднимают виртуальный интерфейс greN, через который можно маршрутизировать трафик так, как если бы серверы были соединены «напрямую».
В рассматриваемой схеме:
- приватной сети между VPS недостаточно, чтобы попасть в интернет – у внутренних машин нет публичного IP и нет маршрута наружу;
- через GRE-туннель внутренняя VPS получает маршрут по умолчанию, ведущий на шлюзовую VPS;
- шлюзовая VPS с помощью
iptablesподменяет адрес источника на свой публичный IP (NAT) и отправляет пакеты в интернет; - ответные пакеты приходят на шлюз, NAT возвращает их во внутреннюю сеть через тот же туннель.
GRE выбран потому, что он прост, поддерживается ядром Linux «из коробки» и не требует дополнительного ПО.
Исходные данные
Приватная сеть Beget – 10.16.0.0/16 (может отличаться, в зависимости от региона размещения). Приватный IP-адрес каждой VPS можно посмотреть в панели управления.
Для примера:
- VPS-GW (шлюз) – приватный IP
10.16.0.5, публичный IP есть (белый IPv4). - VPS-A (клиент) – приватный IP
10.16.0.7, публичного IP нет.
Для туннеля используется служебная подсеть 172.16.0.0/30:
172.16.0.1– адрес интерфейсаgre1на шлюзе;172.16.0.2– адрес интерфейсаgre1на клиентской VPS.
Эта подсеть нигде в интернете и приватной сети не используется – она существует только внутри GRE-туннеля.
Настройка (Ubuntu 24.04)
Все команды выполняются от root (суперпользователь в UNIX-подобных операционных системах) (или через консольную утилиту sudo).
Шаг 1. Настройка шлюзовой VPS (VPS-GW)
1.1. Включить форвардинг (переадресацию) пакетов
Отредактируйте /etc/sysctl.conf и добавьте (либо раскомментируйте) строку:
net.ipv4.ip_forward=1Примените без перезагрузки:
sysctl -p1.2. Поднять GRE-интерфейс
Создайте systemd-юнит (конфигурационный файл, описывающий объект, которым управляет менеджер системы) /etc/systemd/system/gre1.service:
[Unit]
Description=GRE tunnel gre1
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/ip link add gre1 type gre local 10.16.0.5 remote 10.16.0.7 ttl 255
ExecStart=/sbin/ip link set gre1 up
ExecStart=/sbin/ip address add 172.16.0.1/30 dev gre1
ExecStop=/sbin/ip link del gre1
[Install]
WantedBy=multi-user.targetАктивируйте:
systemctl daemon-reload
systemctl enable --now gre1.service1.3. Настроить NAT и сохранить правила iptables
Установите пакет для хранения правил:
apt update
apt install -y iptables-persistentВ процессе установки мастер предложит сохранить текущие правила – можно согласиться, позже мы их перезапишем.
Добавьте правило NAT:
iptables -t nat -A POSTROUTING -s 172.16.0.0/30 -o eth0 -j MASQUERADEСохраните правила, чтобы они применились после перезагрузки:
netfilter-persistent saveШаг 2. Настройка клиентской VPS (VPS-A)
2.1. Поднять GRE-интерфейс
Создайте systemd-юнит /etc/systemd/system/gre1.service:
[Unit]
Description=GRE tunnel gre1
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/ip link add gre1 type gre local 10.16.0.7 remote 10.16.0.5 ttl 255
ExecStart=/sbin/ip link set gre1 up
ExecStart=/sbin/ip address add 172.16.0.2/30 dev gre1
ExecStart=/sbin/ip route add default via 172.16.0.1 dev gre1 metric 50
ExecStop=/sbin/ip link del gre1
[Install]
WantedBy=multi-user.targetlocal и remote поменялись местами относительно конфигурации шлюза.Активируйте:
systemctl daemon-reload
systemctl enable --now gre1.serviceПосле этого маршрут по умолчанию на VPS-A будет идти через туннель на шлюз.
Если на клиентской VPS уже есть маршрут по умолчанию в приватную сеть – новый маршрут через 172.16.0.1 его не заменит автоматически. Уточнить текущие маршруты можно командой ip route. При необходимости старый default удаляется через ip route del default.
Проверка работоспособности
Выполняйте команды с клиентской VPS-A.
- Туннель поднят и адрес назначен:
ip -br a show gre1Ожидаемый вывод:
gre1 UNKNOWN 172.16.0.2/30 …- Шлюз доступен внутри туннеля:
ping -c 3 172.16.0.1- Внешний хост (узел сети) пингуется (пинг – время, за которое отправленный в сеть запрос достигает адресата и возвращается обратно):
ping -c 3 8.8.8.8- Исходящий адрес – публичный IP шлюза:
curl ifconfig.meОтвет должен совпадать с публичным IP VPS-GW.
- Маршрут наружу идет через туннель:
traceroute 8.8.8.8Первым хопом должен быть 172.16.0.1.
Дополнительно на шлюзе можно посмотреть счетчики NAT:
iptables -t nat -L POSTROUTING -v -nЗначения pkts/bytes должны увеличиваться при трафике с клиента.
Масштабирование на несколько VPS
Если выход в интернет нужен с нескольких внутренних VPS, на каждом клиенте поднимается отдельный GRE-туннель к шлюзу, а на шлюзе создается отдельный интерфейс для каждого клиента.
Для этого удобно расширить служебную подсеть до /24 – например, 172.16.0.0/24 – и выделять каждой паре туннельных интерфейсов по адресу.
Пример распределения:
- Интерфейс
gre1на шлюзе – IP шлюза172.16.0.1/24, IP клиента172.16.0.2/24, клиент – приватный IP10.16.0.7. - Интерфейс
gre2на шлюзе – IP шлюза172.16.0.1/24, IP клиента172.16.0.3/24, клиент – приватный IP10.16.0.8. - Интерфейс
gre3на шлюзе – IP шлюза172.16.0.1/24, IP клиента172.16.0.4/24, клиент – приватный IP10.16.0.9.
На шлюзе заводится свой systemd-юнит (gre2.service, gre3.service, …) с уникальными remote и именем интерфейса. Правила NAT при этом достаточно одного – с подсетью-источником 172.16.0.0/24:
iptables -t nat -A POSTROUTING -s 172.16.0.0/24 -o eth0 -j MASQUERADE
netfilter-persistent saveНа каждом клиенте настройка аналогична одиночному случаю – меняется только свой адрес в туннеле.
Доступ к внутренним VPS извне (проброс портов)
Рассмотренная выше схема обеспечивает только исходящий трафик: внутренняя VPS может ходить в интернет, но подключиться к ней снаружи (по SSH (от англ. Secure SHell – защищенная оболочка), HTTP (от англ. Hypertext Transfer Protocol – протокол передачи гипертекста) и т. п.) по-прежнему нельзя – у нее нет публичного IP.
Чтобы сделать отдельные сервисы внутренней VPS доступными извне, на шлюзе настраивается DNAT (проброс портов). Принцип работы:
- Пакет приходит на публичный IP шлюза на заданный порт.
iptablesв цепочкеPREROUTINGподменяет адрес назначения на туннельный IP клиента.- Пакет уходит в
gre1и попадает на внутреннюю VPS. - Ответ возвращается через шлюз и уходит наружу.
Чтобы ответные пакеты гарантированно возвращались через шлюз (а не пытались уйти напрямую, чего у клиента без публичного IP всё равно не получится, но логика маршрутизации может сломать соединение), дополнительно настраивается MASQUERADE в сторону туннеля.
Все правила ниже выполняются на шлюзе (VPS-GW). После добавления правил обязательно сохраните их:
netfilter-persistent saveКейс 1. SSH на внутреннюю VPS
Пробрасываем внешний порт 2222 шлюза на порт 22 клиента 172.16.0.2:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 2222 \
-j DNAT --to-destination 172.16.0.2:22
iptables -t nat -A POSTROUTING -o gre1 -p tcp -d 172.16.0.2 --dport 22 \
-j MASQUERADEПодключение снаружи:
ssh -p 2222 user@<публичный_IP_шлюза>Внешний порт намеренно отличается от 22, чтобы не конфликтовать с SSH самого шлюза. Если на шлюзе SSH переведен на нестандартный порт, можно пробрасывать и 22.
Кейс 2. Несколько внутренних VPS на разных внешних портах
Когда за одним шлюзом стоит несколько клиентов и у каждого нужно открыть, например, SSH, порты разносятся по номерам:
- внешний порт шлюза
2201→172.16.0.2:22; - внешний порт шлюза
2202→172.16.0.3:22; - внешний порт шлюза
2203→172.16.0.4:22.
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 2201 -j DNAT --to-destination 172.16.0.2:22
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 2202 -j DNAT --to-destination 172.16.0.3:22
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 2203 -j DNAT --to-destination 172.16.0.4:22
iptables -t nat -A POSTROUTING -o gre1 -d 172.16.0.2 -p tcp --dport 22 -j MASQUERADE
iptables -t nat -A POSTROUTING -o gre2 -d 172.16.0.3 -p tcp --dport 22 -j MASQUERADE
iptables -t nat -A POSTROUTING -o gre3 -d 172.16.0.4 -p tcp --dport 22 -j MASQUERADEКейс 3. Диапазон портов
Если нужно пробросить целый диапазон (например, для игрового сервера или пассивного FTP (от англ. File Transfer Protocol – протокол передачи файлов)):
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 30000:30100 \
-j DNAT --to-destination 172.16.0.2
iptables -t nat -A POSTROUTING -o gre1 -d 172.16.0.2 -p tcp \
--dport 30000:30100 -j MASQUERADEБез указания порта после --to-destination диапазон сохраняется as-is: 30005 снаружи → 30005 внутри.
UDP
Для UDP-сервисов (от англ. User Datagram Protocol – протокол пользовательских датаграмм) правила те же, но с -p udp. Пример – проброс игрового UDP-порта 27015:
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 27015 \
-j DNAT --to-destination 172.16.0.2:27015
iptables -t nat -A POSTROUTING -o gre1 -d 172.16.0.2 -p udp --dport 27015 \
-j MASQUERADEПроверка проброса
Снаружи (с любой машины в интернете):
nc -vz <публичный_IP_шлюза> 2222На шлюзе должны расти счетчики DNAT:
iptables -t nat -L PREROUTING -v -nЕсли соединение не устанавливается – в первую очередь проверьте, что:
- на внутренней VPS сервис действительно слушает нужный порт (
ss -tlnp); - на внутренней VPS нет локального фаервола, блокирующего вход;
net.ipv4.ip_forwardна шлюзе равен1;- правило DNAT написано именно в таблице
natи цепочкеPREROUTING.
Примечания по другим ОС
Общая логика настройки VPS-сервера одинакова, различаются только пакеты и способы сохранения правил.
- Debian 13 – полностью аналогично Ubuntu 24.04:
iptables-persistent,sysctl.conf, systemd-юнит для GRE. - CentOS 9 / AlmaLinux 9 / Rocky Linux 9 / Fedora 41 – вместо
iptables-persistentпо умолчанию используетсяfirewalldлибоnftables. Возможные варианты:- через
firewalld– добавить маскарад в нужной зоне:firewall-cmd --permanent --add-masquerade
firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 \
-s 172.16.0.0/30 -o eth0 -j MASQUERADE
firewall-cmd --reload - либо отключить
firewalld, поставитьiptables-servicesи сохранить правила черезservice iptables save. - Форвардинг включается тем же
net.ipv4.ip_forward=1в/etc/sysctl.conf(или файле в/etc/sysctl.d/). - systemd-юнит для GRE используется без изменений.
- через
Диагностика проблем
Пропадает связь после ip route add default via 172.16.0.1
Скорее всего, потерян маршрут до самого шлюза по приватной сети и туннель «сам под собой» разваливается. Убедитесь, что в таблице маршрутизации остается конкретный маршрут до приватного IP шлюза через приватный интерфейс:
ip route get 10.16.0.5Ответ должен идти через приватный интерфейс (eth1/ens4 и т. п.), а не через gre1.
Пинг до 172.16.0.1 не проходит
- Проверьте, что интерфейс
gre1поднят на обеих сторонах (ip -br a show gre1). - Проверьте, что
localиremoteв конфигурациях зеркальны (на шлюзеlocal= IP шлюза, на клиентеlocal= IP клиента). - Убедитесь, что между VPS есть связность по приватной сети:
ping 10.16.0.5с клиента. - Проверьте, что GRE не режется фаерволом (
iptables -L -n, nft list ruleset). Протокол GRE – это IP protocol 47, не TCP (англ. Transmission Control Protocol – протокол управления передачей) и не UDP.
ping 172.16.0.1 работает, но ping 8.8.8.8 – нет
- На шлюзе проверьте форвардинг:
sysctl net.ipv4.ip_forwardДолжно быть 1.
- Проверьте правило NAT:
iptables -t nat -L POSTROUTING -v -nПри трафике счетчики должны расти.
- Убедитесь, что имя внешнего интерфейса в правиле (
-o eth0) совпадает с фактическим (ip -br a).
Пинги идут, а TCP/HTTPS (от англ. Hyper Text Transfer Protocol Secure – защищенный протокол передачи гипертекста) – рвутся или «залипают»
Типичный симптом проблемы с MTU (максимальный объем данных). GRE добавляет свой заголовок (24 байта), и пакеты размером в полный MTU приватной сети через туннель уже не проходят. Решение – уменьшить MTU интерфейса или включить MSS clamping (метод ограничения максимального размера) на шлюзе:
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu
netfilter-persistent saveЛибо вручную на обоих концах:
ip link set gre1 mtu 1400После перезагрузки туннель не поднимается
- Проверьте статус юнита:
systemctl status gre1. - Убедитесь, что он включен в автозапуск:
systemctl is-enabled gre1. - Если юнит стартует раньше, чем появляется приватный IP, добавьте в секцию
[Unit]зависимостьAfter=network-online.targetиWants=network-online.target(как в примерах выше), а также убедитесь, что включенsystemd-networkd-wait-onlineлибо его аналог.
Правила iptables исчезают после перезагрузки
Убедитесь, что установлен iptables-persistent и правила сохранены:
netfilter-persistent saveФайлы правил лежат в /etc/iptables/rules.v4 и /etc/iptables/rules.v6.
net.ipv4.ip_forward сбрасывается после перезагрузки
Значение должно быть прописано в /etc/sysctl.conf либо в отдельном файле в /etc/sysctl.d/ (например, /etc/sysctl.d/99-forward.conf). Проверка после перезагрузки:
sysctl net.ipv4.ip_forwardИтог
После выполнения всех шагов внутренние VPS без публичного IP получают полноценный выход в интернет через шлюзовую машину. Схема легко масштабируется на произвольное количество клиентов добавлением новых GRE-интерфейсов и не требует стороннего ПО – используются только штатные средства ядра Linux и iptables.
Если возникнут вопросы, напишите нам, пожалуйста, обращение в панели управления аккаунта (раздел “Помощь и поддержка”), а если вы захотите обсудить настройку VPS-сервера или наши продукты с коллегами по цеху и сотрудниками Beget – ждем вас в нашем сообществе в Telegram.