Направить в обход VPN только один локальный веб-сервис по порту
Решите, может ли правило по порту источника выпустить одну службу хоста через WAN, пока остальной хост продолжает использовать VPN, и чего это стоит.
Одна служба хоста должна отвечать интернету через физический восходящий канал, пока всё остальное на машине продолжает использовать туннель. Три условия решают, выдержит ли маршрутизация на основе политик такую нагрузку, и каждое из них дёшево проверить до написания правила. Служба должна завершаться на этом хосте, а не за DNAT или перенаправленным портом, собственные правила VPN должны разрешать исключение, и сопоставлять нужно ответ, а не запрос. Если не выполняется любое из трёх, команды ниже загрузятся без ошибок, но не сделают ничего полезного.
Проверьте три условия, прежде чем писать правило
Контейнер, публикующий порт через DNAT, или сервер за перенаправлением порта создаёт пакеты, которые не возникают на этом хосте. Эти пакеты пересылаются, поэтому правило, привязанное к локальному вводу хоста, их никогда не видит и трафик продолжает использовать маршрут по умолчанию. Подтвердите, где служба действительно работает, прежде чем продолжать, потому что именно в этом условии люди ошибаются чаще всего.
Второе условие — политика, а не механика. Любой маршрут в обход туннеля — это исключение из назначения туннеля, и на управляемом хосте это решение принимает владелец политики, а не человек, отлаживающий тайм-аут.
Сопоставляйте ответ, а не порт назначения клиента
Ответ сервера, слушающего TCP 443, покидает хост с портом источника 443. Браузер на том же хосте, подключаясь к удалённому сайту HTTPS, отправляет эфемерный порт источника и порт назначения 443. И то и другое — трафик порта 443, и только один из них должен использовать таблицу WAN, поэтому сопоставление по порту назначения захватило бы не ту половину.
Направление ответа — это то, что можно различить. Сопоставьте порт источника, ограничьте правило локально созданными пакетами, и правило покроет собственные ответы службы, не затрагивая запросы браузера.
Постройте полную таблицу возврата, прежде чем правило на неё укажет
Пустая таблица означает отсутствие маршрута, что выглядит как сломанная служба, а не сломанная политика. Сначала заполните её, с номером таблицы и приоритетом, которые больше ничего на хосте не занимает:
ip -4 route add table 100 192.0.2.0/24 dev eth0
ip -4 route add table 100 default via 192.0.2.1 dev eth0
ip -4 rule add priority 1000 iif lo ipproto tcp sport 443 lookup 100
Считывайте номера с работающего хоста, а не копируйте их. ip -4 rule show показывает, какие приоритеты заняты, а добавление правила на уже существующий приоритет молча меняет порядок двух правил, которые по отдельности выглядят правильно.
Настоящую работу здесь выполняют два селектора. iif lo — это способ, которым ядро сообщает о пакетах, созданных самим хостом, поэтому правило не может случайно перехватить пересылаемый трафик, приходящий извне. ipproto tcp sport 443 сужает его до одного транспорта и одного порта, что так же узко, как позволяет требование. Уберите селектор протокола, и правило захватит также UDP 443, а это другая служба с другим поведением.
Держите частные ответы внутри туннеля
Если тот же слушатель отвечает и клиентам на частной стороне, маршрут WAN по умолчанию, перехватывающий всё в таблице 100, попытается отправить эти ответы обратно через восходящий канал. Добавьте частные префиксы, которые не должны следовать за WAN по умолчанию, или позвольте им перейти к следующему правилу политики:
ip -4 route add table 100 throw 10.90.0.0/24
Маршрут throw завершает поиск в этой таблице и возвращает пакет списку правил, поэтому следующая политика VPN получает свой шанс. Сам он ничего не направляет в туннель, и если ни одно последующее правило не имеет подходящего маршрута, пакет всё равно теряется; проверьте следующую политику, вместо того чтобы считать throw исправлением.
Фильтрация обратного пути и правила межсетевого экрана хоста по-прежнему применяются к приходящему запросу. Посмотрите на их реальные вердикты, прежде чем ослаблять проверку, потому что исключение маршрутизации не заставляет rp_filter принять обратный путь, который он считает невозможным.
Проверьте TCP, затем QUIC, затем перезагрузку
Откройте одно соединение с авторизованного внешнего клиента и одно с частной стороны, затем изучите поиск ответа с реальными адресами и портами. Если внешний ответ попадает в таблицу 100, а частный нет, политика делает то, что вы просили.
HTTP/3 обслуживается по UDP и не будет сопоставлен правилом TCP, поэтому слушателю QUIC нужен собственный селектор, иначе он продолжит использовать туннель, пока HTTPS работает. Сохраняйте правила, на которых вы остановитесь, в компоненте, владеющем интерфейсом, и подтверждайте их после переподключения или обновления DHCP. Когда конкурирующий менеджер удаляет маршрут времени выполнения после перезагрузки, это проблема владения, которую нужно решать в источнике, а не повод добавлять сверху более широкие правила NAT.