Раздельное туннелирование на Windows: как настроить split tunneling через маршруты

6622
Раздельное туннелирование на Windows: как настроить split tunneling через маршруты

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

Раздельное туннелирование на Windows решает вполне приземлённую задачу: часть трафика уходит в VPN, а часть остаётся на обычном подключении. Обычно такой режим нужен не ради красивой галочки в настройках, а когда корпоративные подсети должны идти через туннель, а обычный веб, видеосвязь, обновления или локальные сервисы лучше не тащить туда же.

На практике есть два рабочих подхода. Первый, через графический клиент VPN, быстрее и проще, но часто скрывает детали и ломается в неожиданных местах. Второй, через таблицу маршрутизации Windows, требует больше аккуратности, зато даёт предсказуемый результат и нормально документируется. Для WireGuard на Windows есть ещё удобный промежуточный вариант: использовать AllowedIPs как фильтр маршрутов.

Как работает раздельное туннелирование в Windows на самом деле

Сразу поправлю термин. Windows работает не с «postfix-маршрутами», а с маршрутами по префиксу. Система сначала ищет самое точное совпадение по адресу назначения, а уже потом смотрит на метрику. Поэтому маршрут для одного узла с маской /32 всегда важнее маршрута для подсети /24, а маршрут для подсети, в свою очередь, важнее дефолтного 0.0.0.0/0. Именно на этой логике и держится всё раздельное туннелирование.

Из-за этого split tunneling в Windows почти всегда сводится к одному вопросу: какие IP-префиксы должны идти в VPN, а какие должны обходить туннель. Дальше уже выбирается инструмент. Графический клиент пытается решить задачу за вас. Таблица маршрутизации решает задачу прямо и без догадок.

Подход Где удобен Слабое место
Графический клиент VPN Быстро включить режим по приложениям или по списку сетей Логику маршрутизации трудно проверить, исключения по приложениям часто неполные
Ручные маршруты Windows Повторяемая настройка, аудит, точный контроль Нужно понимать шлюз, интерфейс и префиксы
WireGuard AllowedIPs Чистая и понятная схема для туннеля на Windows Маршрутизация идёт по IP, а не по доменам и не по программам

Подход 1. Раздельное туннелирование через графический клиент VPN

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

Но у такого режима есть слабые места. Windows маршрутизирует трафик по IP, а не по названиям программ. Поэтому клиенту приходится добавлять дополнительную логику поверх системы. Из-за этого браузер может пойти мимо VPN, а его вспомогательный процесс, обновлятор, встроенный DNS-over-HTTPS или отдельный модуль видеозвонков уже нет. В результате пользователь видит «режим включён», а трафик живёт своей жизнью.

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

Подход 2. Ручная настройка через таблицу маршрутизации Windows

Здесь Windows ведёт себя честно и прозрачно. Команда route показывает таблицу маршрутов, позволяет добавлять постоянные записи и указывать интерфейс вручную. А если нужен более удобный вид на ту же таблицу, подойдёт netstat -r.

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

Сначала смотрим, что уже есть

route print
 netstat -r

В выводе нужны две вещи: индекс обычного сетевого интерфейса и IPv4-шлюз для прямого выхода. Чаще всего речь про Wi-Fi или Ethernet, а не про VPN-адаптер.

Примеры ручных маршрутов

Пусть локальный шлюз равен 192.168.1.1, а индекс обычного интерфейса равен 15. Тогда подсеть 203.0.113.0/24 можно пустить мимо VPN так:

route -p add 203.0.113.0 mask 255.255.255.0 192.168.1.1 if 15

Если нужен обход для одного конкретного узла, используйте хост-маршрут с маской 255.255.255.255:

route -p add 198.51.100.42 mask 255.255.255.255 192.168.1.1 if 15

Ключ -p делает маршрут постоянным, то есть запись переживёт перезагрузку. Удаление простое:

route delete 203.0.113.0
 route delete 198.51.100.42

Важный нюанс. Раздельное туннелирование по домену Windows из коробки не умеет. Windows маршрутизирует по IP-адресам. Сегодня домен резолвится в один адрес, завтра в другой, а послезавтра уедет на узел CDN в другой подсети. Поэтому для сервисов за CDN ручные маршруты по IP быстро превращаются в хрупкую конструкцию. Для таких задач иногда удобнее клиент с исключением по приложению, а не по адресу.

Где ручной маршрут реально выигрывает

Ручной метод особенно хорош, когда префиксы стабильны. Классический пример, доступ к офисной сети 10.50.0.0/16 через VPN, а весь остальной интернет напрямую. Или доступ к одной внутренней системе с фиксированным адресом. В таких случаях вы точно знаете, что должно уйти в туннель, и можете проверить правило одной командой.

Ручной метод плох там, где набор адресов постоянно гуляет. Публичные SaaS-платформы, видеосервисы, облака за балансировщиками, CDN и сервисы с агрессивным anycast быстро ломают идею «добавлю пару IP и всё заработает». В таком сценарии лучше не обещать себе лишнюю точность.

WireGuard на Windows: AllowedIPs как фильтр маршрутов

Для WireGuard на Windows схема обычно получается чище. Клиент сам берёт AllowedIPs из конфигурации и превращает их в маршруты для интерфейса. На уровне логики AllowedIPs работают как таблица выбора пути для исходящих пакетов и как список допустимых адресов для входящих. В документации WireGuard для Windows отдельно указано, что сервис добавляет маршруты на основе AllowedIPs.

Проще говоря, если вам нужен split tunnel, не ставьте туда 0.0.0.0/0 и ::/0 без необходимости. Перечислите только те подсети и узлы, которые действительно должны идти через туннель.

Пример конфигурации WireGuard для split tunnel

[Interface]
 PrivateKey = ... 
 Address = 10.14.0.2/32
 DNS = 10.14.0.53
 
 [Peer]
 PublicKey = ... 
 Endpoint = vpn.example.com:51820
 AllowedIPs = 10.20.0.0/16, 10.30.40.12/32, 192.168.200.0/24
 PersistentKeepalive = 25

В такой конфигурации через туннель пойдут только три указанных префикса. Обычный веб, обновления Windows и остальной трафик останутся на прямом канале.

Если же в AllowedIPs указать 0.0.0.0/0, ::/0, получится уже full tunnel. Для Windows у WireGuard здесь есть ещё один нюанс: при наличии /0 клиент включает более жёсткие правила и отдельное поведение для DNS. Поэтому переход между full tunnel и split tunnel меняет не только маршрутную схему, но и то, как система ведёт себя при сбоях и утечках.

Ещё один неочевидный момент связан с DNS. Если в секции [Interface] указать внутренний DNS-сервер компании, например 10.14.0.53, Windows может начать резолвить имена через него. Для внутренней зоны такое поведение нормальное и даже полезное. Но если вы ожидали, что публичные домены останутся на обычном DNS провайдера, настройку надо проверять отдельно, а не надеяться на интуицию.

PowerShell-скрипт для добавления и удаления маршрутов

Если команду route add нужно гонять регулярно, удобнее завернуть её в PowerShell. Ниже скрипт, который работает с обычным интерфейсом, сам забирает его шлюз и умеет показывать, добавлять и удалять маршруты. Запускать лучше из повышенной консоли.

param(
     [ValidateSet("Show","Add","Remove")]
     [string]$Action = "Show",
 
     [string]$InterfaceAlias = "Wi-Fi",
 
     [string[]]$Prefixes = @(
         "203.0.113.0/24",
         "198.51.100.42/32"
     )
 )
 
 $cfg = Get-NetIPConfiguration -InterfaceAlias $InterfaceAlias
 if (-not $cfg) {
     throw "Интерфейс не найден: $InterfaceAlias"
 }
 
 $ifIndex = $cfg.InterfaceIndex
 $nextHop = $cfg.IPv4DefaultGateway.NextHop
 
 if (-not $nextHop) {
     throw "У интерфейса $InterfaceAlias нет IPv4-шлюза"
 }
 
 switch ($Action) {
     "Show" {
         Get-NetRoute -InterfaceIndex $ifIndex -AddressFamily IPv4 |
             Sort-Object DestinationPrefix |
             Format-Table DestinationPrefix, NextHop, RouteMetric, InterfaceIndex, PolicyStore
     }
 
     "Add" {
         foreach ($prefix in $Prefixes) {
             $exists = Get-NetRoute -DestinationPrefix $prefix -InterfaceIndex $ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue
             if (-not $exists) {
                 New-NetRoute -DestinationPrefix $prefix -InterfaceIndex $ifIndex -NextHop $nextHop -PolicyStore PersistentStore | Out-Null
                 Write-Host "Добавлен маршрут $prefix через $nextHop, ifIndex $ifIndex"
             } else {
                 Write-Host "Маршрут уже существует: $prefix"
             }
         }
     }
 
     "Remove" {
         foreach ($prefix in $Prefixes) {
             Get-NetRoute -DestinationPrefix $prefix -InterfaceIndex $ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue |
                 Remove-NetRoute -Confirm:$false
             Write-Host "Удалён маршрут $prefix"
         }
     }
 }

Смысл параметра InterfaceAlias простой: туда нужно подставить обычный сетевой интерфейс, через который должен идти трафик в обход VPN. Чаще всего это Wi-Fi или Ethernet, а не туннельный адаптер. Если подставить VPN-интерфейс, получится обратный результат.

Как проверить, что split tunnel действительно работает

Проверка нужна обязательно. Визуально всё может выглядеть правильно, а по факту один сервис уже идёт мимо туннеля, а второй ещё сидит внутри него.

1. Проверка таблицы маршрутов

route print
 netstat -r

Ищите нужный префикс, нужный шлюз и нужный интерфейс. Если маршрут для конкретного узла или подсети есть, но идёт через VPN-адаптер, цель вы не достигли.

2. Проверка пути пакета через tracert

tracert -d 198.51.100.42
 tracert -d 10.30.40.12

Ключ -d отключает обратный DNS и делает вывод чище. Если трафик должен обходить VPN, первым заметным прыжком обычно будет локальный шлюз. Если маршрут должен идти через туннель, картина будет другой. Полного идеала тут нет, потому что часть узлов не отвечает на ICMP, но для грубой проверки команда полезна.

3. Проверка активных соединений через netstat

netstat -ano
 netstat -ano | findstr ESTABLISHED

Эта команда показывает активные соединения и PID процессов. Когда нужно понять, какой процесс реально открыл соединение и на какой адрес, netstat помогает быстро отделить догадки от фактов. Особенно полезно, если графический клиент обещал исключение по приложению, а вы хотите увидеть реальную картину.

4. Проверка DNS через nslookup

nslookup example.com
 nslookup corp.internal 10.14.0.53

Первый запрос показывает, какой DNS-сервер Windows использует по умолчанию. Второй проверяет конкретный внутренний резолвер. Такой тест полезен для быстрой диагностики утечки DNS, но тут важно не переоценивать возможности команды. nslookup проверяет именно системный резолв, а не поведение всех приложений. Браузер с DNS-over-HTTPS, клиент мессенджера или корпоративный агент могут резолвить имена вообще по своим правилам.

Из-за этого нормальная проверка выглядит так: сначала смотрите системный DNS через nslookup, потом путь трафика через tracert, а затем подтверждаете реальным соединением через netstat. Только связка этих трёх шагов даёт внятную картину.

Частые ошибки, из-за которых настройка выглядит правильной, но работает криво

  • Маршрут добавили не через тот интерфейс. Вместо Wi-Fi указали VPN-адаптер.
  • Исключение сделали по домену в голове, а Windows живёт по IP-адресам.
  • У сервиса адреса меняются, а маршрут добавлен для вчерашнего IP.
  • В WireGuard оставили 0.0.0.0/0, а потом удивились, что получился full tunnel.
  • Проверили только веб-сайт в браузере и не заметили, что DNS или фоновые процессы пошли другой дорогой.
  • VPN-клиент включает kill switch или фильтры на уровне брандмауэра, поэтому один лишь маршрут уже не способен переиграть политику клиента.

Что выбрать в реальной жизни

Если задача домашняя и нужно быстро разделить трафик по программам, разумно начать с графического клиента. Если задача рабочая, нужна повторяемость, понятная схема и возможность быстро разбираться в сбоях, лучше идти в маршруты или сразу в WireGuard с аккуратно заполненным AllowedIPs.

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

Практический вывод по настройке split tunneling на Windows

Самый надёжный путь на Windows выглядит так. Сначала решаете, делите ли трафик по приложениям или по IP-префиксам. Если по префиксам, сразу идите в маршрутную логику. Для WireGuard перечисляйте только нужные подсети в AllowedIPs. Для универсального сценария используйте route -p add или PowerShell с постоянными маршрутами. После настройки всегда проверяйте результат не одной командой, а связкой route print, tracert, netstat и nslookup. Иначе легко получить красивую конфигурацию, которая на деле разделяет трафик совсем не там, где вы планировали.

FAQ по раздельному туннелированию на Windows

Можно ли настроить split tunnel в Windows по домену, а не по IP?

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

Что лучше для WireGuard, AllowedIPs или ручные route add?

Если трафик должен делиться в рамках самого туннеля WireGuard, обычно удобнее AllowedIPs. Если нужно поверх уже работающего VPN тонко перекинуть часть сетей в обход, ручные маршруты Windows дают больше контроля.

Почему сайт всё равно идёт через VPN, хотя маршрут добавлен?

Частая причина, у сервиса изменился IP, адрес сидит за CDN или клиент VPN жёстко фильтрует трафик на уровне брандмауэра. Ещё одна причина, маршрут добавили через неправильный интерфейс.

nslookup достаточно, чтобы проверить утечки DNS?

Нет. nslookup показывает только системный резолв. Для нормальной проверки надо смотреть и сам DNS, и путь пакета, и реальные соединения приложений.

Нужны ли права администратора для route add и PowerShell-маршрутов?

Да, в типовом сценарии нужны. Без повышенной консоли добавление и удаление маршрутов обычно не сработает.


туннелирование Windows VPN маршрут WireGuard DNS PowerShell
Alt text
Обращаем внимание, что все материалы в этом блоге представляют личное мнение их авторов. Редакция SecurityLab.ru не несет ответственности за точность, полноту и достоверность опубликованных данных. Вся информация предоставлена «как есть» и может не соответствовать официальной позиции компании.
рассекречено
Антипов жжёт
ПЛАТОН СОВРАЛ. ОБЩЕСТВО ВЫЖИЛО
Сапольски пишет бестселлер. Харрис ведёт подкаст. Правда о детерминизме валяется на каждой полке. Бунта не случилось. Только подкастеры стали добрее.
благородная
ложь
сдохла
дело № V-1983

Юрий Кочетов

Здесь я делюсь своими не самыми полезными, но крайне забавными мыслями о том, как устроен этот мир. Если вы устали от скучных советов и правильных решений, то вам точно сюда.