Вы добавляете правило для VPN-трафика в цепочку FORWARD, запускаете запрос с той же машины Linux, и счётчик остаётся на нуле. Правило, скорее всего, верное. Пакет никогда не шёл этим путём, потому что запрос, созданный на самой машине, ею не пересылается. Прослеживание жизни пакета от сокета до провода объясняет почему, и где правило должно находиться вместо этого.

Найдите, где родился пакет

Порядок хуков Netfilter разделяет три случая, и только один из них затрагивает FORWARD:

OriginPathHook that sees it
Local processOUTPUT then POSTROUTINGOUTPUT, POSTROUTING
Addressed to a local servicePREROUTING then INPUTPREROUTING, INPUT
Received for another hostPREROUTING, FORWARD, POSTROUTINGFORWARD

Локальное приложение начинается в пути OUTPUT, потому что ядро уже установило, что пакет возникает здесь и никогда не является кандидатом на пересылку. Создание туннеля эту классификацию не меняет; туннель — это устройство, через которое пакет может выйти, а не этап, превращающий локальный вывод в транзит.

Два пакета, одна история

Когда туннель поднят, внутренний пакет приложения и зашифрованный внешний пакет — разные объекты. Внутренний пакет несёт адрес назначения и полезную нагрузку, выбранные приложением. Внешний пакет несёт шифртекст на адрес и порт партнёра, и именно он действительно пересекает физический интерфейс.

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

Пространства имён сдвигают границу

Контейнер усложняет это, не меняя принципа. Запрос приложения — это локальный вывод внутри сетевого пространства имён контейнера. Хост получает этот трафик на виртуальном интерфейсе Ethernet и может его переслать, поэтому правило FORWARD на хосте может иметь смысл, хотя процесс, создавший запрос, никогда не касался цепочки OUTPUT хоста.

Прежде чем выбирать хук, установите, где на самом деле живут набор правил и туннель. Туннель внутри контейнера, туннель на хосте с контейнером, маршрутизируемым через него, и туннель в отдельном пространстве имён — это три разные топологии с тремя разными местами для правил. Читайте правила внутри пространства имён, которому принадлежит устройство, а не только в пространстве по умолчанию.

Спросите, нужна ли вам метка вообще

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

Классификация нужна, когда решение зависит от того, что маршрут не может выразить, например от источника, пользователя или процесса. Для принимаемого трафика работает метка в PREROUTING. Для локального трафика метка должна ставиться на этапе вывода, который способен запустить новый поиск маршрута после установки метки. В nftables это цепочка типа route на хуке вывода; обычная цепочка фильтрации вывода установит значение и оставит маршрут неизменным.

В iptables эквивалент живёт в раскладке mangle OUTPUT, и действует то же требование: решение о маршрутизации нужно пересмотреть после появления метки. Затем правило политики должно потребить метку и указать на таблицу, содержащую пригодный маршрут. Установка метки conntrack — это третья, отдельная операция; она сохраняет значение на потоке и сама по себе не ставит это значение на текущий пакет, чтобы маршрутизация могла его прочитать.

Проверьте поиск и счётчик вместе

nft list ruleset
ip route get 198.51.100.25
ip route get 198.51.100.25 mark 0x1

Адрес и метка приведены для примера, и два запроса маршрута отвечают на разные вопросы. Второй спрашивает, что сделало бы ядро, если бы пакет нёс эту метку; он не показывает, что ваш набор правил назначает какую-либо. Относитесь к нему как к проверке гипотезы, а не как к доказательству того, что политика работает.

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

Наконец, ограничьте проверку тем, что механизм способен выразить. Один маршрут для одного адреса не становится постоянной политикой для сервиса, адреса которого меняются. Сначала определите охват пакетов, затем проверьте, может ли устройство маршрутизации и меток сохранить этот охват после повторного подключения.

Похожие статьи