Я хотел бы добавить второе резервное интернет-подключение к моему серверу Linux. Я планирую использовать для этой цели USB LTE-модем.
Поскольку это сотовое соединение будет тарифицироваться, я хочу ограничить объем потребляемых данных до абсолютно необходимого минимума.
У меня есть пользовательское серверное приложение, в которое я могу вносить любые изменения. У него есть несколько задач, где бесперебойное подключение имеет решающее значение, и другие задачи, где время простоя не имеет особого значения.
Я представляю себе что-то вроде этого:
- Серверу необходимо сделать внешний запрос HTTP API. Первая попытка выполняется по маршруту системы по умолчанию (т. е. eth0, первичное интернет-подключение).
- Если запрос не выполнен или истекло время ожидания, повторите запрос через интерфейс LTE.
Только трафик, который мой серверный процесс явно хочет отправить через LTE, должен быть отправлен через LTE. Никакой другой трафик из любой части системы не должен проходить через LTE.
- В частности, я буду использовать узел
localAddress
опция сокета, указывающая, что запрос должен быть выполнен через LTE. - Как гарантировать, что остальной трафик не будет маршрутизироваться через интерфейс LTE (даже если eth0 не работает)?
- А как насчет разрешения DNS?
решение1
Я в конечном итоге добился этого, настроивальтернативная таблица маршрутови аправило политики маршрутизациидля исходного адреса резервного интерфейса.
Мой USB LTE-модем представлен как устройство NDIS, поэтому он просто отображается как eth1
IP 192.168.0.190 и выполняет внутреннюю маршрутизацию NAT. Я настроил eth1
статический IP и вручную настроил маршруты.
В конфигурации по умолчанию используется DHCP, поэтому отключите интерфейс и убедитесь, что все автоматически добавленные маршруты удалены.
Добавьте статическую IP-конфигурацию для интерфейса и включите ее.
Добавьте записи в альтернативную таблицу маршрутизации (я выбрал
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
Установите правила политики маршрутизации таким образом, чтобы приложения, явно использующие 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
таблице маршрутизации [default]).
Этовозможныйчто у вас есть что-то, что перечисляет все доступные IP и пытается отправить трафик с них, что может привести к неожиданному трафику через резервное соединение, но это маловероятно. Я не наблюдал такого трафика.
Обратите внимание, что это не касается разрешения DNS. В ситуации, когда основное соединение находится в автономном режиме, вам может повезти, и вы получите поиск из кэша, но на это не стоит полагаться. Я бы также не настраивал системный резолвер для отправки запросов через интерфейс LTE. Вместо этого ваше приложение может вручную обрабатывать разрешение DNS при выполнении запросов на резервное копирование.
С помощью node легко делать 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) { … });