Политики для нескольких сетевых интерфейсов
Один агент qf может применять разные правила к нескольким сетевым интерфейсам хоста. Хост остаётся одной сущностью с одной gRPC-сессией и одним подписанным bundle, но каждое правило действует только на назначенных интерфейсах.
Возможности:
- агент цепляется к нескольким NIC и видит трафик каждого отдельно;
- политика получает область по интерфейсам (interface-scope): её правила действуют либо на всех подключённых NIC, либо только на именованных;
- conntrack ведётся пер-интерфейс: established-соединение на одном NIC не считается established на другом; транзитный поток A→B держит две отдельные CT-записи;
- телеметрия (события, потоки, счётчики) несёт измерение «интерфейс» — два enforcement-решения по одному routed-пакету (вход на одном NIC, выход на другом) различимы.
Когда применять
Один агент на многоинтерфейсном хосте заменяет раздельные наборы правил на пограничном узле. Типовые случаи:
- Разные trust-зоны на одной машине — внешний NIC (публичный) и внутренний (управляющая/сервисная сеть): на внешнем deny по умолчанию жёстче, на внутреннем разрешены сервисные порты.
- Транзитный/маршрутизирующий узел — пакет входит на один NIC и выходит на другой; каждое плечо получает своё решение и свою CT-запись.
На хосте с одним интерфейсом QF_IFACES не нужен: агент выбирает интерфейс
обычным способом, а политика без области действует на нём.
Модель
Две независимые настройки:
- Набор энфорс-интерфейсов задаётся на агенте параметром
QF_IFACES(список ИМЁН NIC). Агент подключается к каждому и сообщает Control Plane, какие NIC фактически привязаны. - Область правил задаётся на политике полем
interface_names. Пустой список — правила применяются на всех подключённых интерфейсах хоста; непустой — только на перечисленных.
Неспециализированное (unscoped) правило копируется в срез каждого подключённого NIC. Специализированное (scoped) — только в срез своих NIC. Порядок first-match внутри интерфейса сохраняется.
Дефолтные ingress/egress-действия остаются глобальными на хост — различие поведения по NIC выражается явными scoped-правилами, а не разными дефолтами.
Настройка агента
Список энфорс-интерфейсов — параметр QF_IFACES в agent.conf (или per-host
qf_ifaces в Ansible-inventory). Полное описание — в справочнике
agent-config.md. Ключевое:
- список задаётся по именам через запятую/пробел, например
eth0,eth1; - пусто → используется
QF_IFACE; если не задан и он, интерфейс определяется по маршруту по умолчанию; - авто-привязка ко всем интерфейсам ОС не делается: loopback/veth/bridge/CNI общим правилом не отличить от реальных NIC, поэтому набор указывается явно;
- имена NIC должны быть стабильны между перезагрузками (udev/systemd
ens*/enp*): агент резолвит имя в ifindex в рантайме.
Область политики по интерфейсам
У политики есть поле interface_names — список имён NIC, на которых действуют её
правила.
В консоли поле называется «Область по интерфейсам» (Interface scope) в форме политики: пустое — правила применяются на всех подключённых интерфейсах каждого выбранного хоста; заполненное — только на указанных. Консоль подсказывает имена из наблюдаемого инвентаря хостов и помечает имя, которого нет ни в одном инвентаре, — вероятную опечатку.
Через REST — поле interface_names в теле политики:
{
"name": "external-lockdown",
"priority": 100,
"selector": { "matchLabels": { "role": "edge" } },
"interface_names": ["eth0"],
"rules": [
{ "name": "deny-mgmt-port", "priority": 10, "direction": "ingress",
"action": "deny", "match": { "protocol": "tcp", "dst_ports": ["9090"] } }
]
}
При обновлении политики (PUT) поле присутствие-зависимо: если interface_names
опущено — текущая область сохраняется; пустой список [] — область снимается
(правила становятся общими для всех NIC); непустой — область задаётся заново.
Имя, которого нет среди подключённых NIC хоста, приводит к отклонению всего бандла на этом хосте (см. «Стабильность имён и отсутствующий NIC»).
Пример: внешний и внутренний NIC
Пограничный хост с eth0 (внешняя сеть) и eth1 (внутренняя). Задача — закрыть
порт 9090 со стороны внешней сети, оставив его доступным изнутри.
- На агенте задаётся
QF_IFACES=eth0,eth1. После перезапуска и подключения хост сообщает поддержку per-interface-политик и оба интерфейса. - Политика
external-lockdownсо scopeinterface_names: ["eth0"]и правиломdeny ingress tcp 9090.
Результат: пакет на eth0:9090 дропается, тот же порт на eth1 не затронут —
правило отсутствует в срезе eth1. Событие дропа несёт interface: eth0.
Чтобы то же правило действовало на обоих NIC, область оставляют пустой — правило копируется в срез каждого подключённого интерфейса.
Телеметрия
Log- и flow-события, а также rule-счётчики несут измерение «интерфейс»:
- в REST-ответах событий и потоков присутствует поле
interface; в консоли — колонка «Интерфейс» в панелях событий и потоков; - счётчики правил привязаны к паре
(интерфейс, правило); - дашборды мониторинга показывают два решения по routed-пакету раздельно, без ложного слияния в один ряд.
Так вход-решение на одном NIC и выход-решение на другом по одному транзитному пакету остаются различимы.
Порядок раскатки
Control Plane не отправляет scoped bundle агенту без заявленной поддержки
per-interface политики (per_interface_policy в его capabilities). Старый агент
проигнорировал бы неизвестное поле и применил бы scoped-правило глобально —
проверка capability предотвращает это. Порядок обновления:
- Обновить и перезапустить агент на многоинтерфейсном хосте.
- Дождаться появления capability и списка подключённых интерфейсов в состоянии хоста.
- Назначить политику с областью по интерфейсам.
Назначение scoped-политики до апгрейда оставляет хост на последней применённой
(last-good) конфигурации и помечает его degraded.
Эксплуатационные оговорки
Стабильность имён и отсутствующий NIC
Конфигурация и область политики заданы по именам интерфейсов. Правило,
ссылающееся на отсутствующий или переименованный NIC, приводит к тому, что агент
отклоняет весь пакет правил, сохраняет последнее рабочее поколение и сообщает причину.
Хост при этом переходит в degraded, прежние правила продолжают действовать.
Хостам с hotplug или нестабильным неймингом нужны предсказуемые имена
(udev/systemd ens*/enp*).
Одноразовый сброс conntrack на апгрейде
Формат ключа conntrack включает идентификатор интерфейса. При первом обновлении
со старого формата агент пересоздаёт несовместимую закреплённую карту, поэтому
состояния соединений теряются. Пакет существующей TCP-сессии больше не совпадает
с условием established и проверяется следующими правилами или default action.
Завершающий deny может прервать такую сессию. Обновление выполняется поэтапно,
с проверкой трафика после каждого хоста.
Откат может изменить политику
Переход на версию без нового формата ключа повторно сбрасывает conntrack. Кроме того, старая версия не понимает область по интерфейсам и может истолковать сохранённый scoped bundle как глобальный. Перед откатом scoped-политики заменяются на безопасную политику без области и подтверждается новое поколение. Сам откат оценивается как изменение сетевой политики.
См. также
- agent-config.md — параметр
QF_IFACESи per-hostqf_ifacesв Ansible-inventory. - policy-management.md — модель политик и правил,
match-поля, приоритеты, версии. - selector.md — host-селекторы: кому применяется политика.
- ops-runbook.md — эксплуатационные процедуры, апгрейды.