Перейти к основному содержимому

Политики для нескольких сетевых интерфейсов

Один агент qf может применять разные правила к нескольким сетевым интерфейсам хоста. Хост остаётся одной сущностью с одной gRPC-сессией и одним подписанным bundle, но каждое правило действует только на назначенных интерфейсах.

Возможности:

  • агент цепляется к нескольким NIC и видит трафик каждого отдельно;
  • политика получает область по интерфейсам (interface-scope): её правила действуют либо на всех подключённых NIC, либо только на именованных;
  • conntrack ведётся пер-интерфейс: established-соединение на одном NIC не считается established на другом; транзитный поток A→B держит две отдельные CT-записи;
  • телеметрия (события, потоки, счётчики) несёт измерение «интерфейс» — два enforcement-решения по одному routed-пакету (вход на одном NIC, выход на другом) различимы.

Когда применять

Один агент на многоинтерфейсном хосте заменяет раздельные наборы правил на пограничном узле. Типовые случаи:

  • Разные trust-зоны на одной машине — внешний NIC (публичный) и внутренний (управляющая/сервисная сеть): на внешнем deny по умолчанию жёстче, на внутреннем разрешены сервисные порты.
  • Транзитный/маршрутизирующий узел — пакет входит на один NIC и выходит на другой; каждое плечо получает своё решение и свою CT-запись.

На хосте с одним интерфейсом QF_IFACES не нужен: агент выбирает интерфейс обычным способом, а политика без области действует на нём.

Модель

Две независимые настройки:

  1. Набор энфорс-интерфейсов задаётся на агенте параметром QF_IFACES (список ИМЁН NIC). Агент подключается к каждому и сообщает Control Plane, какие NIC фактически привязаны.
  2. Область правил задаётся на политике полем 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 со стороны внешней сети, оставив его доступным изнутри.

  1. На агенте задаётся QF_IFACES=eth0,eth1. После перезапуска и подключения хост сообщает поддержку per-interface-политик и оба интерфейса.
  2. Политика external-lockdown со scope interface_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 предотвращает это. Порядок обновления:

  1. Обновить и перезапустить агент на многоинтерфейсном хосте.
  2. Дождаться появления capability и списка подключённых интерфейсов в состоянии хоста.
  3. Назначить политику с областью по интерфейсам.

Назначение 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-host qf_ifaces в Ansible-inventory.
  • policy-management.md — модель политик и правил, match-поля, приоритеты, версии.
  • selector.md — host-селекторы: кому применяется политика.
  • ops-runbook.md — эксплуатационные процедуры, апгрейды.