Резервное подключение к Интернету для конкретного процесса

Aug 17 2020

Я хочу добавить к моему серверу Linux дополнительное резервное подключение к Интернету. Планирую использовать для этого USB LTE-модем.

Поскольку это сотовое соединение будет измеряться, я хочу ограничить объем потребляемых данных до минимально необходимого уровня.

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

Я представляю себе что-то вроде этого:

  • Серверу необходимо сделать внешний запрос HTTP API. Первая попытка выполняется по системному маршруту по умолчанию (то есть через eth0, основное подключение к Интернету).
  • Если запрос завершился неудачно или истекло время, повторите запрос через интерфейс LTE.

Только трафик, который мой серверный процесс явно хочет отправить через LTE, должен отправляться через LTE. Никакой другой трафик из любой части системы не должен проходить через LTE.

  • В частности, я использую localAddressопцию сокета узла, чтобы указать, что запрос должен выполняться через LTE.
  • Как мне убедиться, что другой трафик не будет маршрутизироваться через интерфейс LTE (даже если eth0 не работает)?
  • Что насчет разрешения DNS?

Ответы

josh3736 Sep 01 2020 at 03:53

Я в конечном итоге достижение этой цели путем настройки альтернативной таблицы маршрутизации и правил политики маршрутизации для адреса источника резервного интерфейса.

Модем USB LTE, который у меня есть, представлен как устройство NDIS, поэтому он просто отображается как eth1с IP-адресом 192.168.0.190 и выполняет внутреннюю маршрутизацию NAT. Я настроил eth1статический IP-адрес и настроил маршруты вручную.

  1. В конфигурации по умолчанию используется DHCP, поэтому отключите интерфейс и убедитесь, что все автоматически добавленные маршруты удалены.

  2. Добавьте статическую IP-конфигурацию для интерфейса и откройте ее.

  3. Добавьте записи в альтернативную таблицу маршрутизации (я выбрал 1) для подсети и шлюза по умолчанию.

    # ip route add 192.168.0.0/24 dev eth1 src 192.168.1.190 table 1
    # ip route add default via 192.168.0.1 table 1
    
  4. Установите правила политики маршрутизации, чтобы приложения, которые явно используют 192.168.1.190 в качестве исходного адреса, использовали таблицу маршрутизации 1 вместо адреса по умолчанию.

    # ip rule add from 192.168.0.190/32 table 1
    # ip rule add to 192.168.0.190/32 table 1
    

На этом этапе вы сможете проверить свое подключение.

$ curl https://wtfismyip.com/text 1.2.3.4 # primary ISP external IP $ curl --interface 192.168.0.190 https://wtfismyip.com/text
5.6.7.8  # backup LTE external IP

Если все в порядке, сделайте конфигурацию постоянной. Я добавил в /etc/network/interfaces:

iface eth1 inet static
        address 192.168.0.190
        netmask 255.255.255.0
        post-up ip route add 192.168.0.0/24 dev eth1 src 192.168.0.190 table 1
        post-up ip route add default via 192.168.0.1 table 1
        post-up ip rule add from 192.168.0.190/32 table 1
        post-up ip rule add to 192.168.0.190/32 table 1

Теперь только приложения, которые явно привязываются к 192.168.0.190 при выполнении исходящих подключений, будут маршрутизироваться через резервное подключение. Весь остальной трафик маршрутизируется eth0(или что-то другое, настроенное в mainтаблице маршрутизации [по умолчанию]).

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

Обратите внимание, что это не касается разрешения DNS. В ситуации, когда основное соединение отключено, вам может повезти, и вы получите поиск из кеша, но на это не стоит полагаться. Я бы также не стал настраивать общесистемный преобразователь для отправки запросов через интерфейс LTE. Вместо этого ваше приложение может вручную обрабатывать разрешение DNS при выполнении запросов на резервное копирование.


С помощью узла выполнять HTTP-запросы (или любое TCP-соединение) с определенного адреса источника очень просто. Просто укажите localAddressопцию , например:

https.get('https://wtfismyip.com/text', { localAddress: '192.168.0.190' }, …);

Решение поиска в DNS немного сложнее. lookupОпция также доступна, что позволяет переопределить процесс разрешения на DNS по умолчанию. Вы можете использовать обычай dns.Resolverдля поиска. К сожалению, у узла не было возможности указать исходный адрес для поиска DNS, поэтому я добавил его . Имея это на месте, вы можете соединить части:

const resolver = new dns.Resolver();
resolver.setServers(['8.8.8.8']);
resolver.setLocalAddress('192.168.0.190'); // requires node > v15.0.0

https.get('https://wtfismyip.com/text', {
  localAddress: '192.168.0.190',
  lookup: function(hostname, opts, cb) {
    resolver.resolve(hostname, function(err, records) {
      if (err) cb(err);
      else if (!records[0]) cb(new Error('Missing DNS record'));
      else cb(null, records[0], 4);
    });
  }
}, function(res) { … });