Маршрутизация и server push
Кто создаёт маршрут, как серверный push сочетается с локальными include/exclude и route_file и почему client_subnet, allowed_networks и gateway решают другие задачи.
Шесть механизмов — шесть разных задач
Сначала определите направление: нужно отправить маршрут клиенту, разрешить назначение, зарегистрировать сеть за клиентом или завернуть весь трафик устройства.
| Механизм | Где | Направление и смысл |
|---|---|---|
route | [profile:*] | Сервер пушит CIDR всем пользователям профиля |
route | [user:*] | Сервер пушит CIDR одному пользователю; персональный список заменяет профильный |
client_subnet | [user:*] | Сервер регистрирует входящий путь к сети, которая находится за этим клиентом |
allowed_networks | [user:*] | Whitelist назначений, куда пользователь может отправлять пакеты |
gateway, include, exclude | client.conf | Локальный выбор устройства: full-tunnel и дополнительные маршруты/исключения |
route_file | client.conf + файл | Локально добавляет большой список CIDR в split-tunnel на Windows/macOS |
Split-tunnel и full-tunnel
Режим по умолчанию зависит от клиента: Linux CLI стартует в split-tunnel, GUI-клиенты — в full-tunnel. В split-режиме через Qeli идут TUN-пул и выданные сервером маршруты; для интернет-egress в full-tunnel нужен NAT на сервере.
# split-tunnel: только TUN-пул, server-push и include
gateway = false
include = 10.50.0.0/16
exclude = 203.0.113.0/24
# full-tunnel: весь остальной трафик тоже через Qeli
gateway = true
kill_switch = true
# нужно для выхода full-tunnel в интернет
routing.nat.enabled = true
# WAN определяется автоматически; при необходимости задайте явно
routing.nat.interface = ens3
Профильные и персональные маршруты
Ключ повторяемый. CIDR обязателен и пишется первым; gateway и metric необязательны. Типовой next-hop — tun.address сервера.
# получат все пользователи профиля
route = 192.168.50.0/24 gateway=10.9.0.1 metric=100
route = 10.50.0.0/16
[user:contractor]
# получит только этот пользователь
route = 192.168.50.128/25 gateway=10.9.0.1
route, профильные route ему не отправляются. Если нужны оба набора, перечислите их в секции пользователя.| Ситуация | Что произойдёт |
|---|---|
route_local=false | Явно выданные сервером CIDR всё равно применяются; этот флаг управляет только широкими RFC1918 |
route=0.0.0.0/0 или префикс /1…/7 | Клиентское ядро отфильтрует слишком широкий push: сервер не может незаметно превратить split-tunnel в full-tunnel |
В INI записан advertised_routes или push_routes | Такого INI-ключа нет; используйте повторяемый route |
| Большой список поверх UDP | AuthOK фрагментируется; свыше примерно 28 КБ сервер откажет в подключении и предложит сократить список или использовать TCP |
Загрузка IP-адресов и подсетей через route_file
route_file подключает один или несколько внешних файлов к локальному split-tunnel. Ключ можно повторять; записи из всех файлов объединяются с include, канонизируются и дедуплицируются.
# Windows
gateway = false
route_file = C:\qeli\corp-routes.txt
route_file = C:\qeli\openvpn-routes.txt
# macOS: используйте абсолютный путь
# route_file = /Users/alice/.config/qeli/routes.txt
# одна сеть на строку
10.20.0.0/16
192.0.2.0/24 # филиал
; один IPv4-адрес записывается как /32
203.0.113.17/32
route 172.16.9.7 255.255.0.0 vpn_gateway 10
route-ipv6 2001:db8:42::/48
| Правило | Что это означает |
|---|---|
| Формат | Поддерживаются IPv4/IPv6 CIDR, route ADDRESS [NETMASK] [gateway] [metric] и route-ipv6 CIDR. Пустые строки, полнострочные и inline-комментарии #/; игнорируются |
| Повторяемость | Укажите route_file несколько раз. Пути с пробелами допустимы; каждый файл участвует в общей канонизации и дедупликации. |
| Лимит | Не более 250 000 уникальных маршрутов суммарно после объединения include и всех route_file. |
| Семейство адресов | Файл принимает IPv4 и IPv6 CIDR. Клиент устанавливает маршруты только для семейств, присутствующих в согласованном NetworkPlan; для ipv6=required отсутствие IPv6 завершает подключение fail-closed. |
| Направление | Только добавление в туннель, как include. Для обхода туннеля используйте exclude в client.conf |
| Перечитывание | Все файлы читаются в единый snapshot при получении NetworkPlan; после правки переподключите профиль. Чтение, установка и cleanup больших списков отменяются кнопкой Disconnect. |
| Ошибка чтения | Нечитаемый файл, неверная строка или маска и превышение лимита останавливают подключение fail-closed: Qeli не продолжает работу с неполной политикой маршрутов. |
| Успешная загрузка | В журнале появляется Loaded N route(s) from … |
route_file применяется только локально клиентами Windows/macOS. Rust CLI его распознаёт как чужой платформенный ключ, но не читает; Android и iOS сохраняют при переносе профиля, но не применяют.route на профиле; свой список для пользователя — route в users.conf; локальный большой список Windows/macOS — route_file; переносимый локальный список — include/exclude в client.conf.ACL назначения и сеть за клиентом
allowed_networks ограничивает исходящие назначения пользователя. client_subnet сообщает серверу, что указанный CIDR достижим через этого клиента. Эти ключи не заменяют друг друга.
[user:branch-a]
static_ip = 10.9.0.10
# сеть находится ЗА branch-a: входящий iroute на сервере
client_subnet = 192.168.50.0/24
# сам branch-a может ходить только сюда
allowed_networks = 10.9.0.0/24, 192.168.60.0/24
Пустой allowed_networks означает отсутствие ACL-ограничения, а не запрет всего. Сеть client_subnet должна быть непересекающейся с pool.cidr и сетями других площадок.
Минимальный site-to-site без NAT
Каждый шлюз получает фиксированный адрес, регистрирует свою LAN через client_subnet и получает персональный маршрут к LAN второй площадки.
[user:branch-a]
static_ip = 10.9.0.10
client_subnet = 192.168.50.0/24
route = 192.168.60.0/24 gateway=10.9.0.1
[user:branch-b]
static_ip = 10.9.0.11
client_subnet = 192.168.60.0/24
route = 192.168.50.0/24 gateway=10.9.0.1
# сервер, [profile:main]
routing.nat.enabled = false
routing.forward_private = true
routing.client_to_client = true
# каждый Linux-шлюз, [qeli]
forward = true
Дополнительные механизмы маршрутизации
Эти параметры не заменяют route и route_file: они управляют NAT, выбором приложений, IPv6 или жизненным циклом интерфейса на конкретном клиенте.
| Механизм | Где работает | Направление и смысл |
|---|---|---|
gateway_nat + lan_subnet | Linux | NAT для LAN за клиентом; превращает Linux-клиент в шлюз для своей сети |
exit_node | Linux | NAT для трафика из туннеля при выходе через физический WAN клиента; сам exit-узел должен оставаться split-tunnel |
apps + apps_mode | Windows / macOS / Android | Выбор приложений, а не сетей; iOS сохраняет настройку, но без MDM NEAppRule не применяет |
allow_lan | Android | Оставляет домашнюю LAN, link-local и локальный multicast вне VPN |
dev_attach + QELI_TUNIP_FILE | Linux CLI | Передаёт создание, адресацию и маршруты готового TUN внешнему контроллеру |
allow_ipv6_leak | Клиент | Явно разрешает IPv6 идти физическим путём, если full-tunnel согласован без IPv6; по умолчанию отсутствующее семейство блокируется |
qeli:// и не приходят через server push. Их задают в доверенном client.conf или интерфейсе соответствующей платформы.Прокси DNS и адрес, который получает клиент
dns.enabled поднимает прокси на сервере. dns.push_servers отдельно определяет, какой адрес будет передан клиентам.
dns.push_servers | dns.enabled | Результат |
|---|---|---|
| задан | любое | Клиент получает первый адрес и обращается к нему напрямую |
| пусто | true | Клиент получает dns.listen и использует серверный прокси, кэш и блоклист |
| пусто | false | DNS не пушится; устройство сохраняет свои резолверы |
Как клиенты применяют push-маршруты
Все актуальные клиенты применяют CIDR. Необязательные next-hop и metric зависят от платформы, поэтому не используйте их как единственный механизм приоритизации.
| Клиент | CIDR | gateway/metric |
|---|---|---|
| Linux CLI | ✓ | Применяет оба поля |
| Android | ✓ | Читает оба поля |
| Windows / macOS | ✓ | Читает, но маршрут привязывается к TUN; ограничение пишется в лог |
| iOS source | ✓ | Использует CIDR, дополнительные поля не применяет |
Как понять, где потерялся маршрут
Проверяйте последовательно: конфиг сервера, фактический push, таблицу маршрутов клиента, forwarding и NAT/firewall.
sudo qeli check-config --config /etc/qeli/server.confотбрасывает битые CIDR и конфликтующие профилиsudo qeli show-routes <user>показывает маршруты конкретного пользователяip route showпроверяет таблицу Linux-клиентаsudo sysctl net.ipv4.ip_forwardна шлюзе и сервере ожидается 1sudo iptables-save | grep -E 'qeli-nat|TCPMSS'проверяет NAT и MSS clampНа клиенте ищите строку Pushed route applied. Если push виден в логе, но маршрута нет в ОС, проблема уже в платформенном применении, конфликте таблиц или правах.