Раздельное туннелирование в WireGuard
Настройка маршрутизации в WireGuard упирается в одну строку: AllowedIPs определяет, какому пиру уйдёт пакет, и сузить список на клиенте недостаточно — на сервере он же работает фильтром источника. Построчный разбор полей .conf — в гайде «Настройка клиента WireGuard»; здесь — только маршруты.
Коротко. Пакет уходит пиру с самым длинным совпавшим префиксом, порядок строк в файле роли не играет. Вычитания в протоколе нет: сеть
/16выводят из канала ровно 16 дополняющими префиксами. Разделения по программам в конфигурации нет — на Android его делаетVpnService.
Как WireGuard выбирает, куда отправить пакет?
По радикс-дереву префиксов: при отправке в нём ищется адрес назначения, при приёме — адрес источника. В allowedips.c запросов к дереву ровно два: wg_allowedips_lookup_dst() подбирает пира для исходящего пакета, wg_allowedips_lookup_src() сверяет входящий. Побеждает самый длинный совпавший префикс: /32 у одного пира сильнее /0 у другого.
Пакет, чей источник вне AllowedIPs приславшего пира, отбрасывается молча — без ICMP и без ошибки в приложении. Стороны несимметричны: на сервере у пира стоят только его внутренние адреса (/32), на клиенте — то, что заворачивается в канал; /0 на сервере отдаст клиенту весь исходящий трафик. Установка с нуля — в гайде по настройке WireGuard на своём VPS.
Почему маршрут по умолчанию заворачивает весь трафик?
0.0.0.0/0 совпадает с любым адресом и забирает всё, что не подошло к более узкой записи. Второй пир с /0 канал не удваивает: при равной длине префикса выигрывает записанный последним.
wg-quick относится к /0 особо: признак — сравнение elif [[ $1 == */0 ]], после чего поднимается policy-routing с таблицей, начиная с номера 51820 (занятый номер скрипт инкрементирует), правилами not fwmark $table table $table и table main suppress_prefixlength 0 плюс net.ipv4.conf.all.src_valid_mark=1. Метка не пускает зашифрованный UDP самого WireGuard обратно в свой канал.
Побочный эффект — на Windows: по docs/netquirk.md службы WireGuard для Windows единственный пир с /0 включает kill-switch, «All other packets are blocked». В Android условие такое же — sawDefaultRoute плюс config.getPeers().size() == 1. Набор дополняющих префиксов защиту выключает: что делать дальше — в гайде по клиенту.
Как вычесть подсеть из полного маршрута?
Никак: вычитания нет, сеть исключают перечислением дополняющих префиксов, и их ровно столько, какова длина её маски. Минус в wg(8) значит другое — «ips prefixed with ’-’ are removed if present» при инкрементальном обновлении (флаг WGALLOWEDIP_REMOVE_ME). Строка AllowedIPs = 0.0.0.0/0, -192.168.0.0/16 соберётся без ошибок и ничего не исключит.
Домашняя сеть 192.168.0.0/16 выводится шестнадцатью префиксами:
AllowedIPs = 0.0.0.0/1, 128.0.0.0/2, 192.0.0.0/9, 192.128.0.0/11,
192.160.0.0/13, 192.169.0.0/16, 192.170.0.0/15, 192.172.0.0/14,
192.176.0.0/12, 192.192.0.0/10, 193.0.0.0/8, 194.0.0.0/7,
196.0.0.0/6, 200.0.0.0/5, 208.0.0.0/4, 224.0.0.0/3
Сумма дополнения и самой сети даёт ровно 2³² = 4 294 967 296 адресов; для /19 нужно 19 строк. Имя хоста допустимо лишь в Endpoint, поэтому вывести сервис «по домену» нельзя.
Чем разделение по программам отличается от подсетей?
Это разные уровни: подсети — свойство протокола, программы — свойство операционной системы. В wg(8) описано восемь ключей конфигурации, и ни один не про программы; на iOS и macOS AllowedIPs становятся includedRoutes, снова только маршруты.
ExcludedApplications и IncludedApplications понимает лишь Android-клиент, списки взаимоисключающие: при обоих сразу летит BadConfigException с Reason.INVALID_KEY. Фильтрует не WireGuard, а системный VpnService через addDisallowedApplication() по имени пакета. Те же переключатели в интерфейсе — в клиенте Amnezia.
Какие сервисы имеет смысл вывести мимо канала?
Российские сервисы, чья логика опирается на регион и стабильность адреса: платёжные приложения, транспортные и торговые сервисы, госпорталы. Их антифрод и геолокация рассчитаны на российский адрес. Порядок работы: определить номер автономной системы, выгрузить префиксы через RIPEstat, перегенерировать список при изменениях; смежный разбор — на странице «Банк не работает с VPN». За NAT нужен PersistentKeepalive: Endpoint обновляется только по входящим аутентифицированным пакетам.
Пошаговая правка маршрутов в конфигурации
Правка идёт в /etc/wireguard/<интерфейс>.conf.
Шаг 1. Что уже стоит в списке
wg show wg0 allowed-ips покажет пары «ключ → префиксы»; расхождение с файлом значит, что список правили через wg set без записи.
Шаг 2. Подсети по номеру автономной системы
Актуальные префиксы отдаёт RIPE NCC: stat.ripe.net/data/announced-prefixes/data.json?resource=AS<номер>; для AS13238 запрос 15 августа 2026 года вернул 47.
Шаг 3. Дополняющие префиксы вместо нуля
Замените 0.0.0.0/0 набором дополнения и перезапустите интерфейс: wg-quick down wg0 && wg-quick up wg0. Уход от /0 выключает kill-switch.
Шаг 4. Linux: Table = off и свои правила
С Table = off функция add_route() выходит по условию [[ $TABLE != off ]] || return 0 и маршрутов не появляется вовсе: ip route и ip rule пишете сами.
Шаг 5. Android: списки программ
Клиент ставьте с wireguard.com/install. Заполните один список: «Exclude» выводит отмеченное мимо канала, «Include only» пускает только отмеченное.
Шаг 6. Проверка обоих маршрутов
Сделайте traceroute (в Windows tracert) до адреса из исключённой подсети: первым переходом должен быть шлюз провайдера, а не сервер.
Что делать, если трафик идёт не туда?
Определите уровень поломки: список префиксов на сервере, маршруты в системе или трафик самого канала.
| Симптом | Причина | Решение |
|---|---|---|
Пинг до 10.8.0.1 идёт, до 10.8.0.3 — нет | адрес не вписан в AllowedIPs пира на сервере | добавить нужный /32 в список пира |
| Интерфейс поднят, трафик идёт мимо канала | Table = off без собственных ip route | добавить маршруты или вернуть Table = auto |
Не поднимается после ручного /0 | зашифрованный UDP попал в свой же канал | вернуть fwmark и правило not fwmark |
Порядок строк в файле не важен: wg-quick сортирует список sort -nr -k 2 -t /. Сравнение протоколов — в разделе «Настройка защищённого канала».
Частые вопросы
Короткие ответы про ручную правку списка префиксов.
Материал носит образовательный, научно-технический характер; параметры сверялись с wg(8) и wg-quick(8). Клиент ставьте только с wireguard.com/install или из github.com/WireGuard: приватный ключ лежит в конфигурации открытым текстом, а посторонние сборки и «зеркала» проверить нельзя.
AllowedIPs — это маршруты или список доступа?
И то и другое одновременно. Руководство wg(8) описывает поле как адреса, от которых входящий трафик пира разрешён и к которым направляется исходящий. В ядерном модуле это одно радикс-дерево с двумя точками входа: wg_allowedips_lookup_dst() ищет пира по адресу назначения для исходящего пакета, wg_allowedips_lookup_src() — по адресу источника для входящего. Поэтому сузить список на клиенте и забыть про сервер нельзя: смысл записей на двух сторонах разный.
Можно ли исключить подсеть строкой с минусом?
Нет. Минус в wg(8) относится к инкрементальному обновлению через wg set: адреса с префиксом «-» удаляются из списка, если они там были (в коде это флаг WGALLOWEDIP_REMOVE_ME). Запись AllowedIPs = 0.0.0.0/0, -192.168.0.0/16 соберётся без ошибок и ничего не исключит — диапазон останется покрыт маршрутом по умолчанию. Единственный рабочий способ — заменить /0 набором дополняющих префиксов.
Сколько префиксов нужно, чтобы вычесть сеть?
Ровно столько, какова длина маски исключаемой сети: для /16 это 16 префиксов, для /19 — 19, для /24 — 24. Проверить результат можно арифметически: сумма адресов дополнения плюс сама исключённая сеть должна дать 2 в 32-й степени, то есть 4 294 967 296 адресов для IPv4.
Почему нельзя указать домен вместо подсети?
Поле AllowedIPs принимает только IP-адреса с CIDR-маской: парсер wireguard-tools выдаёт ошибку «AllowedIP is not in the correct format» при некорректной записи и не разрешает маску больше 32 для IPv4 и больше 128 для IPv6. Имя хоста допустимо исключительно в поле Endpoint, где указывается точка подключения. Поэтому вывести сервис из канала по домену на уровне протокола невозможно — только по подсетям.
Чем разделение по программам отличается от маршрутов?
Это разные уровни. Маршруты — свойство самого протокола, а фильтрация по программам в формате конфигурации отсутствует: в wg(8) перечислены только PrivateKey, ListenPort, FwMark, PublicKey, PresharedKey, AllowedIPs, Endpoint и PersistentKeepalive. Ключи ExcludedApplications и IncludedApplications понимает лишь Android-клиент, а реальную фильтрацию выполняет системный VpnService через addDisallowedApplication и addAllowedApplication по имени пакета.