
«Почему работает только один шлюз?» — такой вопрос может возникнуть, если добавить второй публичный IP-адрес на Linux-сервер.
На практике необходимость добавить второй публичный IP-адрес на сервер возникает в самых разных сценариях. Например, когда на одном сервере размещены несколько сайтов или сервисов, и каждому из них требуется собственный публичный IP-адрес. Еще эта опция нужна при запуске новой версии сайта или приложения на отдельном адресе без остановки текущего, а также когда требуется использовать отдельный IP-адрес для интеграции с внешними системами, которые принимают соединения только с заранее разрешенных адресов.
Итак, вы назначили на сервер оба IP-адреса, корректно прописали настройки сети, но работает только один канал связи. Запросы уходят через один интернет-канал, хотя активны оба.
Дело в том, что в Linux-системах может быть только один маршрут по умолчанию (default gateway). Если у сервера несколько внешних интерфейсов с разными подсетями, то ответы на запросы, пришедшие через второй интерфейс, сервер попытается отправить через основной шлюз таблицы маршрутов. Это приведет к асимметричной маршрутизации и отбрасыванию пакетов.
Привет, Хабр! Меня зовут Саломея Яковлева, я специалист продуктовой поддержки в Selectel. В этой статье мы разберемся, как избежать такой проблемы с помощью механизма policy-based routing (PBR) на базе iproute2: создадим отдельные таблицы маршрутов и правила, направляющие трафик через правильный шлюз.
Что такое policy-based routing
Policy-based routing (PBR) — это механизм в Linux, позволяющий более гибко настроить маршрут трафика на основе адреса источника, входного интерфейса, протокола и других параметров. Такая маршрутизация используется при наличии нескольких сетевых интерфейсов и необходимости отправлять определенные пакеты через определенный интерфейс.
В отличие от классического механизма (destination-routing), где вся маршрутизация хранится в одной таблице, PBR опирается на несколько таблиц и правила, которые определяют, какую таблицу применить к конкретному пакету. Если для группы пакетов не указ��но, как их маршрутизировать, то они будут отправлены по стандартным правилам маршрутизации.
Исходные данные
Облачный сервер на базе Linux. В этой статье для демонстрации будет использована операционная система Ubuntu 24.04 LTS 64-bit.
На двух портах виртуальной машины назначены приватные IP. Публичный IP-адрес ассоциируется с приватным IP-адресом сервера, а входящий трафик обрабатывается облачным роутером.
В нашем примере для сервера MyServer добавлены два порта:
приватный адрес 192.168.0.2 и публичный 31.129.32.248,
приватный адрес 192.168.0.3 и публичный 31.41.155.3.

Публичные плавающие IP-адреса функционируют за NAT, поэтому в конфигурации не указываются. В нашей документации подробно описана работа публичных IP-адресов.
Текущий конфигурационный файл netplan:
network:
version: 2
ethernets:
eth0:
addresses:
- 192.168.0.2/24
match:
macaddress: fa:16:3e:38:52:73
mtu: 1500
nameservers:
addresses:
- 188.93.16.19
- 188.93.17.19
routes:
- to: 0.0.0.0/0
via: 192.168.0.1
set-name: eth0
eth1:
addresses:
- 192.168.0.3/24
match:
macaddress: fa:16:3e:65:85:ed
mtu: 1500
nameservers:
addresses:
- 188.93.16.19
- 188.93.17.19
set-name: eth1
Используется статическая конфигурация сети с одной таблицей маршрутизации. Шлюз по умолчанию задан только для интерфейса eth0, поэтому весь внешний трафик идет через него. Интерфейс eth1 не участвует в маршрутизации во внешние сети.

Бесплатный курс «Системный администратор Linux с нуля»
Освойте администрирование Linux на SelectOS и станьте востребованным специалистом.
Шаг 1. Подготовка системы
Установите утилиту iproute2, если она еще не установлена:
sudo apt update
sudo apt install iproute22. Убедитесь, что ядро Linux поддерживает необходимые опции — расширенную маршрутизацию (Advanced Router) и несколько таблиц маршрутизации (Multiple Tables).
Это можно сделать следующей командой:
grep -E "CONFIG_IP_ADVANCED_ROUTER|CONFIG_IP_MULTIPLE_TABLES" /boot/config-$(uname -r)Ожидаемый результат выполнения команды:
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_MULTIPLE_TABLES=yЕсли обе опции включены (=y), PBR полностью поддерживается. На современных образах Ubuntu, Debian, CentOS и т. п. эти опции включены по умолчанию.
Шаг 2. Создание таблиц маршрутизации
Дополнительные таблицы маршрутизации создаются в файле /etc/iproute2/rt_tables. Вы можете открыть его любым редактором и отредактировать следующим образом:
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
101 rt_eth0
102 rt_eth1 В нашем примере таблица rt_eth0 будет использоваться для интерфейса eth0, таблица rt_eth1 для интерфейса eth1.
Шаг 3. Настройка правил маршрутизации
Правила маршрутизации (ip rule) определяют, через какую таблицу маршрутизации проходит каждый пакет.
Давайте рассмотрим вывод ip rule немного подробнее:
ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup defaultЧисло в начале строки — идентификатор правила. Ядро обрабатывает правила в порядке возрастания идентификатора.
from all— условие, определяющее, какие пакеты попадают под правило. В данном примере — любые.
lookup— таблица маршрутизации, в которой ядро будет искать маршрут.
Условия, которые можно использовать в ip rule:
from— адрес источника пакета;to— адрес назначения;iif— интерфейс, на который пакет пришел;oif— интерфейс, через который пакет должен выйти (для локально сформированных пакетов);tos— значение поля TOS IP-пакета;fwmark— метка пакета заданная, например, через nftables/iptables.
Все условия можно комбинировать, а также использовать префикс not для исключения пакетов, соответствующих условиям.
Теперь перейдем к настройке наших правил.
1. Добавьте в каждую из созданных таблиц маршрут до локальной сети:
sudo ip route add 192.168.0.0/24 dev eth0 table rt_eth0
sudo ip route add 192.168.0.0/24 dev eth1 table rt_eth12. Добавьте маршрут по умолчанию для наших таблиц:
sudo ip route add default via 192.168.0.1 dev eth0 table rt_eth0
sudo ip route add default via 192.168.0.1 dev eth1 table rt_eth13. Создайте правила, по которым ядро будет выбирать таблицу маршрутизации для каждого исходящего пакета:
sudo ip rule add from 192.168.0.2/32 table rt_eth0
sudo ip rule add from 192.168.0.3/32 table rt_eth1Проверяем вывод ip rule:
ip rule show
0: from all lookup local
32762: from 192.168.0.3 lookup rt_eth1
32763: from 192.168.0.2 lookup rt_eth0
32766: from all lookup main
32767: from all lookup defaultЕсли совпадает адрес отправителя, ядро будет отправлять трафик по нашим правилам из таблиц rt_eth0 и rt_eth1.
Теперь сервер доступен по обоим публичным адресам:
ping 31.129.32.248
PING 31.129.32.248 (31.129.32.248) 56(84) bytes of data.
64 bytes from 31.129.32.248: icmp_seq=1 ttl=57 time=7.01 ms
64 bytes from 31.129.32.248: icmp_seq=2 ttl=57 time=5.70 ms
64 bytes from 31.129.32.248: icmp_seq=3 ttl=57 time=8.23 msping 31.41.155.3
PING 31.41.155.3 (31.41.155.3) 56(84) bytes of data.
64 bytes from 31.41.155.3: icmp_seq=1 ttl=57 time=5.94 ms
64 bytes from 31.41.155.3: icmp_seq=2 ttl=57 time=10.2 ms
64 bytes from 31.41.155.3: icmp_seq=3 ttl=57 time=10.2 msШаг 4. Настройка конфигурационного файла
После перезагрузки нашего облачного сервера все созданные правила слетят. Чтобы этого избежать в дальнейшем, нужно сохранить все правила и маршруты в конфигурационном файле netplan.
1. По умолчанию в готовых образах Selectel включен агент cloud-init, из-за которого настройки сети будут синхронизироваться и после перезагрузки сервера будут указываться прежние настройки. Чтобы отключить синхронизацию, отключите конфигурирование сети:
echo "network: {config: disabled}" >> /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg2. Измените сетевые настройки следующим образом:
network:
version: 2
renderer: networkd
ethernets:
eth0:
addresses:
- 192.168.0.2/24
routes:
- to: 0.0.0.0/0
via: 192.168.0.1
table: 101 #идентификатор таблицы rt_eth0
routing-policy:
- from: 192.168.0.2
table: 101 #идентификатор таблицы rt_eth0
nameservers:
addresses:
- 188.93.16.19
- 188.93.17.19
eth1:
addresses:
- 192.168.0.3/24
routes:
- to: 0.0.0.0/0
via: 192.168.0.1
table: 102 #идентификатор таблицы rt_eth1
routing-policy:
- from: 192.168.0.3
table: 102 #идентификатор таблицы rt_eth1
nameservers:
addresses:
- 188.93.16.19
- 188.93.17.193. Проверьте синтаксис файла:
sudo netplan try4. Примените изменения конфигурации:
sudo netplan applyТаким образом мы с помощью PBR настроили работу облачного сервера с двумя публичными IP-адресами. Сервер может одновременно принимать соединения по нескольким внешним адресам и корректно отвечать по каждому из них.
Это открывает возможность использовать несколько публичных IP-адресов как самостоятельные точки входа: привязывать к ним разные сайты или приложения, выносить отдельные сервисы на свой адрес, подключаться к внешним системам с нужного IP и проводить миграции или тестирования. При этом вся логика остается внутри одного сервера и не требует усложнения инфраструктуры.
