Сети · Qeli 0.8.1

Маршрутизация и 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, excludeclient.confЛокальный выбор устройства: full-tunnel и дополнительные маршруты/исключения
route_fileclient.conf + файлЛокально добавляет большой список CIDR в split-tunnel на Windows/macOS
qeli:// не переносит маршруты. Он содержит данные подключения. Push приходит после успешной аутентификации, а gateway/include/exclude/route_file остаются локальными.
Режим клиента

Split-tunnel и full-tunnel

Режим по умолчанию зависит от клиента: Linux CLI стартует в split-tunnel, GUI-клиенты — в full-tunnel. В split-режиме через Qeli идут TUN-пул и выданные сервером маршруты; для интернет-egress в full-tunnel нужен NAT на сервере.

/etc/qeli/client.conf · [qeli]
# 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
/etc/qeli/server.conf · [profile:main]
# нужно для выхода full-tunnel в интернет
routing.nat.enabled = true
# WAN определяется автоматически; при необходимости задайте явно
routing.nat.interface = ens3
Full-tunnel выбирает клиент. Один включённый NAT на сервере сам по себе не заставляет устройства отправлять весь интернет в туннель.
Server push

Профильные и персональные маршруты

Ключ повторяемый. CIDR обязателен и пишется первым; gateway и metric необязательны. Типовой next-hop — tun.address сервера.

/etc/qeli/server.conf · [profile:main]
# получат все пользователи профиля
route = 192.168.50.0/24 gateway=10.9.0.1 metric=100
route = 10.50.0.0/16
/etc/qeli/users.conf
[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
Большой список поверх UDPAuthOK фрагментируется; свыше примерно 28 КБ сервер откажет в подключении и предложит сократить список или использовать TCP
Совместимость UDP: клиенты версии 0.7.14 и новее собирают фрагментированный AuthOK. Если остались более старые клиенты, уменьшите push-список или дайте им TCP-профиль.
Локальный список

Загрузка IP-адресов и подсетей через route_file

route_file подключает один или несколько внешних файлов к локальному split-tunnel. Ключ можно повторять; записи из всех файлов объединяются с include, канонизируются и дедуплицируются.

client.conf · [qeli]
# 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
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 …
Это не server push. 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 достижим через этого клиента. Эти ключи не заменяют друг друга.

/etc/qeli/users.conf
[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 второй площадки.

/etc/qeli/users.conf
[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
server.conf + client.conf шлюза
# сервер, [profile:main]
routing.nat.enabled = false
routing.forward_private = true
routing.client_to_client = true

# каждый Linux-шлюз, [qeli]
forward = true
Не забудьте обратный путь. Узлы LAN должны отправлять удалённую подсеть через свой Qeli-шлюз — статическим маршрутом на основном роутере или настройкой самого шлюза.
За пределами push

Дополнительные механизмы маршрутизации

Эти параметры не заменяют route и route_file: они управляют NAT, выбором приложений, IPv6 или жизненным циклом интерфейса на конкретном клиенте.

МеханизмГде работаетНаправление и смысл
gateway_nat + lan_subnetLinuxNAT для LAN за клиентом; превращает Linux-клиент в шлюз для своей сети
exit_nodeLinuxNAT для трафика из туннеля при выходе через физический WAN клиента; сам exit-узел должен оставаться split-tunnel
apps + apps_modeWindows / macOS / AndroidВыбор приложений, а не сетей; iOS сохраняет настройку, но без MDM NEAppRule не применяет
allow_lanAndroidОставляет домашнюю LAN, link-local и локальный multicast вне VPN
dev_attach + QELI_TUNIP_FILELinux CLIПередаёт создание, адресацию и маршруты готового TUN внешнему контроллеру
allow_ipv6_leakКлиентЯвно разрешает IPv6 идти физическим путём, если full-tunnel согласован без IPv6; по умолчанию отсутствующее семейство блокируется
Локальная политика: эти ключи не входят в qeli:// и не приходят через server push. Их задают в доверенном client.conf или интерфейсе соответствующей платформы.
DNS

Прокси DNS и адрес, который получает клиент

dns.enabled поднимает прокси на сервере. dns.push_servers отдельно определяет, какой адрес будет передан клиентам.

dns.push_serversdns.enabledРезультат
заданлюбоеКлиент получает первый адрес и обращается к нему напрямую
пустоtrueКлиент получает dns.listen и использует серверный прокси, кэш и блоклист
пустоfalseDNS не пушится; устройство сохраняет свои резолверы
Частая ошибка: задать внешний dns.push_servers и ожидать, что запросы пройдут через встроенный прокси. В этом случае клиент обращается к указанному DNS напрямую.
Совместимость

Как клиенты применяют push-маршруты

Все актуальные клиенты применяют CIDR. Необязательные next-hop и metric зависят от платформы, поэтому не используйте их как единственный механизм приоритизации.

КлиентCIDRgateway/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на шлюзе и сервере ожидается 1
sudo iptables-save | grep -E 'qeli-nat|TCPMSS'проверяет NAT и MSS clamp

На клиенте ищите строку Pushed route applied. Если push виден в логе, но маршрута нет в ОС, проблема уже в платформенном применении, конфликте таблиц или правах.

Первоисточники

Полное описание маршрутизации