«Почему работает только один шлюз?» — такой вопрос может возникнуть, если добавить второй публичный 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. Подготовка системы

  1. Установите утилиту iproute2, если она еще не установлена:

sudo apt update
	sudo apt install iproute2

2. Убедитесь, что ядро 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_eth1

2. Добавьте маршрут по умолчанию для наших таблиц:

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_eth1

3. Создайте правила, по которым ядро будет выбирать таблицу маршрутизации для каждого исходящего пакета:

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 ms
ping 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.cfg

2. Измените сетевые настройки следующим образом:

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.19

3. Проверьте синтаксис файла:

sudo netplan try

4. Примените изменения конфигурации:

sudo netplan apply

Таким образом мы с помощью PBR настроили работу облачного сервера с двумя публичными IP-адресами. Сервер может одновременно принимать соединения по нескольким внешним адресам и корректно отвечать по каждому из них.

Это открывает возможность использовать несколько публичных IP-адресов как самостоятельные точки входа: привязывать к ним разные сайты или приложения, выносить отдельные сервисы на свой адрес, подключаться к внешним системам с нужного IP и проводить миграции или тестирования. При этом вся логика остается внутри одного сервера и не требует усложнения инфраструктуры.