Селективная маршрутизация через собственный VPS: нужное — в туннель, остальное — напрямую
Внутренняя разработка, собственный инструмент: домашний и рабочий периметр, где часть внешних ресурсов должна ходить через свой VPS, а весь остальной интернет — напрямую и на полной скорости. Штатный механизм прошивки эту задачу не решал, поэтому маршрутизация собрана вручную и полностью автоматизирована через HTTP API роутера.
Туннель нужен для части ресурсов, а включается на весь интернет
Задача формулируется в одну строку: несколько внешних ресурсов должны быть доступны с устройств в сети, все остальные — работать как обычно. Решение «в лоб» — поднять VPN на роутере и завернуть туда весь трафик — задачу закрывает и сразу создаёт три новые.
Первая: всё, что зависит от географии выхода, начинает вести себя иначе — локальные сервисы, платёжные шлюзы, выдача поиска, видео. Вторая: латентность и полоса всего домашнего трафика теперь упираются в один арендованный сервер, включая тяжёлое видео и звонки. Третья: любой сбой туннеля означает не «не открылся один сайт», а «интернета нет вообще».
Штатный ответ прошивки на это — маршрутизация по спискам доменов: заводишь группу доменов, привязываешь её к интерфейсу туннеля, прошивка сама подставляет маршруты. На практике на основном сегменте сети этот механизм маршрутов не создаёт вообще — он лишь кэширует резолвенные адреса. Обнаруживается это не из документации, а по факту: домен резолвится, а трафик всё равно уходит мимо туннеля.
Два механизма вместо одного нерабочего: маршруты и точечный DNS
Селективная маршрутизация ломается в двух местах сразу: имя резолвится не туда и пакет уходит не туда. Поэтому механизма два, и они закрывают разные половины задачи — статические маршруты отвечают за то, куда уйдёт пакет, точечные DNS-привязки — за то, какой адрес вообще будет получен.
У ресурса собственные AS-диапазоны или он живёт на общем облачном фронте — от этого зависит метод
Статические маршруты по AS-диапазонам прямо в таблицу маршрутизации, интерфейс — туннель
Точечные привязки: только эти домены резолвятся публичным резолвером через туннель
HTTP API роутера, challenge-response авторизация, батчи, сохранение конфигурации
Диагностика утечек, снимок конфигурации перед изменением, watchdog на стороне VPS
Статические маршруты — там, где у сервиса есть свои AS-диапазоны
Единственный механизм, который на основном сегменте действительно уводит трафик в туннель, — запись в таблице маршрутизации. Для сервиса выясняются его собственные диапазоны по номеру автономной системы, каждый диапазон кладётся отдельным маршрутом на интерфейс туннеля. Через туннель идёт ровно то, что перечислено в маршрутах, — ни байтом больше.
Общий облачный фронт статикой не добивается
Обратная сторона того же правила: если ресурс живёт на крупном CDN, его адресные блоки делит с ним половина интернета. Завернуть такой блок целиком — значит утащить в туннель кучу постороннего трафика и получить ровно ту деградацию, от которой уходили. Здесь работают только точечные подсети под наблюдаемые узлы либо отдельный сегмент сети с полным туннелем — это осознанный выбор, а не обход.
DNS-привязки — только точечные, никогда глобальные
Маршрут бесполезен, если имя резолвится в подменённый адрес. Поэтому для нужных доменов прописан отдельный резолвер, доступный через туннель. Правила, которые стоили отладки:
- привязка делается на домен, а не глобально — иначе моргнувший туннель убивает резолв всей сети
- апексы общих CDN и облачных API привязывать нельзя — по ним ходит половина остального интернета
- привязка бренд-домена подхватывает поддомены суффиксным сопоставлением — список не приходится вести руками
- резолв проверяется только из того же сегмента: межсегментные DNS-запросы режутся, и это даёт ложный диагноз
Мобильные и игровые клиенты ходят по прямым адресам
Отдельный класс сюрпризов: приложения нередко коннектятся к своим API и игровым серверам по прямым адресам, часто по UDP, вообще не спрашивая DNS. DNS-привязка их не спасает — нужны именно маршруты. WireGuard при этом несёт UDP прозрачно, так что вопрос только в полноте списка диапазонов.
Автоматизация — через HTTP API, потому что SSH ненадёжен
SSH на устройстве регулярно недоступен: слоты сессий залипают, порт уходит в timeout ровно тогда, когда что-то надо срочно поправить. Вся конфигурация переведена на HTTP API роутера, хелпер — на стандартной библиотеке Python:
- авторизация challenge-response: сервер отдаёт realm и challenge, клиент считает производный хеш и не передаёт пароль
- сессия живёт минуты — обёртка сама переавторизуется на
401и повторяет запрос - массовые правки идут батчами по два-три десятка записей — API дедуплицирует одинаковые объекты удаления, и слишком большой батч молча теряет часть работы
- изменение вступает в силу до перезагрузки, только если явно вызвано сохранение конфигурации — это отдельный шаг, а не побочный эффект
Диагностика утечек: что резолвится, но идёт мимо туннеля
Самый полезный скрипт контура сверяет две вещи: адреса, которые роутер уже нарезолвил для отслеживаемых доменов, и покрытие статических маршрутов. Всё, что есть в первом списке и отсутствует во втором, — утечка мимо туннеля. Именно так нашёлся отдельный медиа-диапазон сервиса, из-за которого сам сервис открывался, а картинки и вложения в нём — нет.
Снимок конфигурации перед изменением и заготовленный откат
Перед любой партией правок в файл с меткой времени сохраняется срез состояния: группы доменов, настройки прокси-резолвера, привязки серверов имён, таблица маршрутов. Отдельно записана процедура отката DNS на провайдерский — короткая последовательность вызовов, которую можно выполнить, когда сеть уже в плохом состоянии и разбираться некогда.
Ничего экзотического — важно, где именно оно применено
Туннель до собственного VPS; UDP приложений проходит прозрачно
Единственная точка выхода туннеля: systemd-юнит + watchdog по расписанию
Записи по AS-диапазонам в таблице маршрутизации — единственный механизм, который реально уводит трафик
Per-domain резолв через туннель вместо подменённых ответов провайдерского резолвера
Challenge-response авторизация, короткая сессия, авто-переавторизация — вместо нестабильного SSH
urllib, http.cookiejar, hashlib — ни одной внешней зависимости, скрипт запускается где угодно
Три сегмента с разными политиками: полный туннель / селективно / напрямую
JSON-дамп маршрутов, привязок и групп доменов перед каждой партией правок
Нужные ресурсы доступны, остальной интернет не замедлился
остальное идёт напрямую через провайдера, без потери скорости и без смены гео
≈ 155 статических маршрутов + ≈ 190 точечных DNS-привязок, все заведены скриптом
watchdog чинит битую конфигурацию сервиса сам, без ручного захода на сервер
Политика выбирается подключением к сети, а не перенастройкой устройства
Три сегмента с разными политиками: полный туннель, селективная маршрутизация и прямой выход через провайдера. Устройству ничего не настраивают — его подключают к нужной сети. Это же даёт запасной путь: если селективный набор маршрутов чего-то не покрывает, устройство временно переводится в сегмент с полным туннелем, и разбираться можно спокойно.
Инцидент, который переопределил собственное правило
После очередной перезагрузки пропал весь интернет: не резолвилось вообще ничего, время не синхронизировалось, работал только туннель. Причина оказалась внешней — резолвер провайдера перестал отдавать корректные ответы. Лечение противоречило ранее записанному правилу «никогда не вешать глобальный резолвер на туннель»: пришлось игнорировать DNS-серверы, приходящие от провайдера по DHCP, и поднять публичный резолвер через живой туннель. Правило в шпаргалке было переписано вместе с условием, при котором оно перестаёт действовать, — это, пожалуй, полезнее самого фикса.
Смена точки выхода — это миграция всех правил, а не переключение тумблера
Когда пришлось перевести контур на другой VPS, выяснилась цена связки «маршруты + DNS»: перевести нужно и все маршруты, и все доменные привязки. Оставленная на мёртвом интерфейсе привязка не даёт ошибки — она просто тихо ломает резолв конкретного домена. Перенос сделан тем же скриптом, батчами, со сверкой после каждого шага.
Watchdog доучили на реальном отказе
Отдельный отказ: после перезагрузки VPS сервис туннеля не поднимался — в конфигурации остался пустой блок пира, и сервис падал при каждом старте. Watchdog это не ловил, потому что умел реагировать только на пропажу интерфейса и на протухший handshake. Теперь он дополнительно санирует конфигурацию перед пересборкой и триггерится на состояние «сервис в failed». Класс отказа стал самовосстанавливающимся.
Если убрать детали: получился предсказуемый сетевой периметр, где список «что идёт через туннель» — это явная, проверяемая и версионированная конфигурация, а не набор случайных настроек, которые никто не рискует трогать.
Где ещё ложится та же методология
Это кейс не про домашний роутер. Это типовая задача «часть трафика — по особому маршруту, остальное — как обычно, и всё это воспроизводимо». Тот же подход переносится почти без изменений:
- → Офис или филиал, которому нужны конкретные внешние сервисы, но нельзя сажать всю сеть на один арендованный канал
- → Гео-зависимые внешние API — через выделенный exit-адрес идут только вызовы к ним, остальной трафик сервиса не меняет маршрут
- → Разделение рабочего и личного трафика на одном физическом канале — сегментами с разными политиками, без агентов на устройствах
- → Любое сетевое устройство с HTTP API — конфигурация становится скриптом: воспроизводимой, версионируемой и откатываемой
- → Аудит существующего периметра: что реально уходит в туннель, а что только считается ушедшим — это разные списки, и их надо сверить
- Хелпер на stdlib для challenge-response авторизации в HTTP API устройства: авто-переавторизация по 401, батчинг правок, явное сохранение конфигурации
- Скрипт диагностики утечек: сверка резолвенных адресов с покрытием таблицы маршрутов — находит то, что считается завёрнутым, но идёт мимо
- Снимок конфигурации перед каждой партией правок + записанная процедура отката, исполнимая на уже сломанной сети
- Правило разделения: собственные AS-диапазоны — маршрутами, общий облачный фронт — отдельным сегментом, а не широкими блоками
- Watchdog, который триггерится на failed-состояние сервиса и санирует конфигурацию, а не только следит за наличием интерфейса
Если нужен доступ к конкретным ресурсам, а не весь интернет через чужой канал — это настраивается
Начинать имеет смысл с инвентаризации: какие ресурсы действительно нужны через туннель, есть ли у них собственные адресные диапазоны и что уже утекает мимо. Дальше конфигурация превращается в скрипт — с бэкапом, откатом и watchdog, а не в набор ручных настроек, которые никто не помнит.
Аудит за 5 000 ₽ — с конкретным отчётом и сметой
Расскажу что внедрить в вашем бизнесе в первую очередь, какая будет окупаемость, и нужен ли вообще AI для вашей задачи (иногда — нет).
Или просто напишите свой вопрос — отвечу в течение 2 часов