Настройка механизма фильтрации трафика

Этот раздел описывает управление правилами фильтрации для узлов и сервисов, развёрнутых с помощью Kolla Ansible. Поддерживается фильтрация трафика на основании правил nftables, включая фильтрацию трафика в контейнерах, использующих интерфейсы типа bridge на базе Podman. При использовании сетей типа host правила фильтрации для контейнеров не применяются.

Параметры фаервола задаются в globals.yml или globals.d/firewall.yml репозитория региона.

При каждом запуске правила фаервола полностью пересоздаются.

Включение фаервола

По умолчанию управление фаерволом отключено (enable_firewall: "no"). Для включения:

  1. Откройте веб-интерфейс GitLab.

  2. Перейдите в репозиторий региона project_k / deployments / <имя региона>.

  3. Перейдите в файл globals.yml или создайте файл globals.d/firewall.yml. Добавьте в файл следующую конфигурацию:

    ## Firewall options
    enable_firewall: "yes"
    
    ## Разрешение исходящего трафика
    firewall_allow_mgmt_outgoing: "yes"
    
    ## Добавление drop-правила в конце цепочек
    firewall_enable_trailing_drop: "yes"
    

где:

  • enable_firewall: "yes" — включение управления фаерволом через nftables;

  • firewall_allow_mgmt_outgoing: "yes" — разрешение всего исходящего трафика через api_interface (сетевой интерфейс, через который OpenStack-сервисы принимают API-запросы и взаимодействуют внутри кластера; как правило, это интерфейс управляющей сети). Необходимо для подключений сервисов к базам данных, очередям сообщений и другим компонентам кластера. Значение по умолчанию — "yes". Переопределяйте только в том случае, если нужно ограничить исходящий трафик;

  • firewall_enable_trailing_drop: "yes" — добавление правила drop в конец цепочек для блокировки всего трафика, не разрешённого явными правилами. Значение по умолчанию — "yes". Установите "no", если нужно сначала понаблюдать за трафиком, не блокируя его: неразрешённый трафик не будет отбрасываться (см. Режим наблюдения).

При включении фаервола автоматически применяются правила по умолчанию для трёх типов трафика:

  • management — доступ к management IP-адресу каждого узла;

  • external — доступ к внешнему VIP-адресу (kolla_external_vip_address);

  • internal — доступ к внутреннему VIP-адресу (kolla_internal_vip_address).

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

Секция firewall_rules

Секция firewall_rules предназначена для задания правил доступа к VIP-адресам или другим точкам подключения. Она дополняет правила, связанные с контейнерами, и применяется отдельно.

Ниже приведены правила по умолчанию, которые применяются автоматически при включении фаервола:

firewall_rules:
  management:
    allow:
      - "10.0.0.0/8"
      - "172.16.0.0/12"
      - "192.168.0.0/16"
    deny:
      - "224.0.0.0/4"
    destination: "{{ hostvars[inventory_hostname].ansible_host | default(inventory_hostname) }}"
    destination_ports:
      - "22"
  external:
    allow:
      - "0.0.0.0/0"
    deny:
      - "224.0.0.0/4"
    destination: "{{ kolla_external_vip_address }}"
    destination_ports:
      - "80"
      - "443"
      - "1024:65535"
  internal:
    allow:
      - "0.0.0.0/0"
    deny:
      - "224.0.0.0/4"
    destination: "{{ kolla_internal_vip_address }}"
    destination_ports:
      - "80"
      - "443"
      - "1024:65535"

По умолчанию задано три блока:

  • management — разрешает доступ к management IP-адресу узла из приватных подсетей (RFC 1918: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) только на порт 22 (SSH). Ограничение по портам обязательно: на management IP-адресе слушают memcached, RabbitMQ, MariaDB и другие сервисы, поэтому без destination_ports они оказались бы доступны с любого узла внутренних сетей. Узлы кластера получают полный доступ через тип keystack (см. ниже), операторам же нужен только SSH.

  • external — разрешает доступ к внешнему VIP из любого источника на портах 80 и 443 (HTTP/HTTPS для доступа к OpenStack API через HAProxy) и в диапазоне 1024:65535 (эндпоинты OpenStack-сервисов, которые HAProxy маршрутизирует на внутренние порты). Если переопределить этот блок и не указать нужные порты, внешний API станет недоступен.

  • internal — разрешает доступ на те же порты, что и external, но для внутреннего VIP, используемого для межсервисного взаимодействия компонентов OpenStack. В случае переопределения также важно сохранить доступ к нужным портам — иначе межсервисное взаимодействие с OpenStack нарушится.

Для каждого блока правил доступны следующие параметры:

  • allow — список разрешённых источников (CIDR);

  • deny — список запрещённых источников (CIDR);

  • destination — целевой IP-адрес (например, внешний или внутренний VIP);

  • destination_ports — список портов или диапазонов (диапазон задаётся через двоеточие, например 1024:65535);

  • protocol — сетевой протокол (по умолчанию tcp, если заданы порты);

  • enforcing — режим работы deny-правил: true (по умолчанию) — отбрасывать трафик; false — только логировать (см. Режим наблюдения).

Переопределение и объединение правил

Важно различать два механизма:

  • Переопределение переменной. Если задать firewall_rules в globals.yml региона, значение целиком заменяет набор по умолчанию из роли (приоритет переменных Ansible, без автоматического слияния). То же относится к firewall_containers, firewall_inbound, firewall_traffic_types и firewall_ruleorder — они применяются как есть, без объединения.

  • Объединение в роли. Только для firewall_rules роль выполняет одно явное рекурсивное слияние:

    1. базой служит набор default_firewall_rules из роли — он содержит только блоки ``external`` и ``internal`` и гарантирует доступ к VIP-адресам;

    2. поверх него рекурсивно накладывается ваш firewall_rules;

    3. последним добавляется блок keystack.

При рекурсивном слиянии вложенные словари объединяются по ключам, а списки заменяются целиком (не дополняются). Например, задав firewall_rules.external.destination_ports, вы замените весь список портов, но сохраните allow/deny/destination из базового набора.

Предупреждение

Блока management в default_firewall_rules нет. Если вы переопределяете firewall_rules в globals.yml и не указываете в нём management, правило управляющего доступа (SSH) исчезнет, а трафик к management IP попадёт под завершающее drop-правило. Всегда явно перечисляйте все нужные блоки, включая management.

Секция firewall_containers

Правила задаются для каждого контейнерного сервиса через переменную firewall_containers. Раздел применяется только для контейнеров с сетями типа bridge — при использовании network_mode: host правила firewall_containers не действуют.

В примере ниже запрещается трафик от контейнеров cinder_scheduler и cinder_api на порт 22 (SSH). Это предотвращает инициирование SSH-соединений из этих контейнеров.

firewall_containers:
  podman_override:
    deny:
      - "0.0.0.0/0"
    destination_ports:
      - "22"
    containers:
      - "cinder_scheduler"
      - "cinder_api"

Здесь podman_override — произвольное имя группы правил. Одна группа может охватывать несколько контейнеров через параметр containers. Имена контейнеров должны соответствовать именам сервисов Kolla Ansible.

Поддерживаемые параметры:

  • allow — список разрешённых источников (CIDR);

  • deny — список запрещённых источников (CIDR);

  • destination_ports — список целевых портов;

  • protocol — сетевой протокол (по умолчанию tcp, если заданы порты);

  • containers — применение правила на конкретные контейнеры.

Секция firewall_inbound

Сервисы с сетью типа bridge, опубликованные на management IP-адресе узла (memcached, mcrouter, MariaDB, Redis), при обращении к ним проходят через DNAT и не попадают в цепочку KEYSTACK. Без отдельной фильтрации они были бы доступны с любого узла, маршрутизируемого до контроллера. Секция firewall_inbound задаёт для таких сервисов фильтрацию до DNAT (в цепочке INBOUND_FILTER).

firewall_inbound:
  memcached:
    destination_ports:
      - "{{ memcached_port }}"
  mariadb:
    destination_ports:
      - "{{ database_port }}"
  redis:
    destination_ports:
      - "{{ redis_port }}"

Для каждого сервиса достаточно указать destination_ports (и при необходимости destination — по умолчанию используется api_interface_address узла, и log_prefix). Список разрешённых источников формируется автоматически: адреса узлов кластера, записи firewall_keystack и подсеть firewall_container_subnet. Остальные источники отбрасываются с логированием FW-DROP inbound <сервис>.

Примечание

Правила firewall_inbound всегда работают в режиме блокировки — флаг enforcing для них не применяется. Чтобы открыть доступ к этим сервисам стороннему узлу (например, внешней системе мониторинга БД), добавьте его в firewall_keystack.

Порядок правил

Порядок правил настраивается через две переменные:

  • firewall_ruleorder — задаёт порядок применения правил внутри одного типа:

    firewall_ruleorder:
      - allow
      - deny
    
  • firewall_traffic_types — задаёт порядок применения типов правил относительно друг друга:

    firewall_traffic_types:
      - podman_override
      - management
      - external
      - internal
    

Тип правил по умолчанию — KeyStack

Роль автоматически добавляет тип правил keystack первым в firewall_traffic_types. Этот тип обеспечивает межузловую коммуникацию внутри кластера и работает следующим образом:

  • При каждом применении фаервола роль собирает IP-адреса всех узлов из inventory-группы firewall — по интерфейсу api_interface.

  • Для текущего узла создаётся правило, разрешающее входящий трафик с этих адресов на его management IP-адрес.

Благодаря этому все узлы кластера могут взаимодействовать друг с другом, что обеспечивает корректную работу OpenStack-сервисов.

Тип keystack нельзя удалить из конфигурации. Однако его можно явно указать в firewall_traffic_types, чтобы изменить порядок применения относительно других правил:

firewall_traffic_types:
  - management
  - keystack
  - external
  - internal

Добавление внешних узлов в keystack

Чтобы предоставить узлу, не входящему в inventory-группу firewall (например, узлу развёртывания, CI или системе мониторинга), такой же доступ, как у узла кластера, добавьте его адрес в firewall_keystack:

firewall_keystack:
  - interface: "{{ api_interface }}"
    address: "10.220.68.3"

Адреса из firewall_keystack на интерфейсе api_interface попадают как в набор keystack_allow (полный доступ к management IP-адресу узла, все порты), так и в список разрешённых источников секции firewall_inbound. Это единственный способ открыть внешнему узлу доступ к сервисам с сетью типа bridge (MariaDB, Redis, memcached, mcrouter), которые фильтруются до DNAT.

Пустые адреса (например, незаданные переменные окружения) автоматически отбрасываются, поэтому такие записи безопасны.

Для доступа только по SSH добавлять узел в firewall_keystack не нужно — достаточно указать его подсеть в firewall_rules.management.allow.

Режим наблюдения

Перед включением блокировки можно запустить фаервол в режиме наблюдения: правила deny не отбрасывают трафик, а только логируют его. Это позволяет увидеть, что было бы заблокировано, не нарушая работу сервисов.

Режим включается двумя настройками:

  • firewall_enable_trailing_drop: "no" — отключает завершающее drop-правило, поэтому трафик, не разрешённый явными правилами, не отбрасывается;

  • enforcing: false в нужных блоках firewall_rules / firewall_containers — соответствующие deny-правила вместо drop логируют трафик и пропускают его.

firewall_enable_trailing_drop: "no"

firewall_rules:
  management:
    allow: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
    destination_ports: ["22"]
    enforcing: false
  external:
    enforcing: false
  internal:
    enforcing: false

Заблокированный (или разрешённый в режиме наблюдения) трафик логируется в журнал ядра с префиксами:

  • FW-DROP — трафик отброшен (боевой режим, enforcing: true);

  • FW-PERMIT — трафик пропущен, но был бы отброшен (режим наблюдения, enforcing: false).

Частота логирования ограничена переменной firewall_filter_log_rate (по умолчанию 10/minute), чтобы поток сообщений не переполнял журнал. Просмотреть записи можно командой grep -E 'FW-DROP|FW-PERMIT' /var/log/messages (или через journalctl -k).

Примечание

Флаг enforcing действует на блоки firewall_rules и firewall_containers. Правила секции firewall_inbound всегда блокируют трафик.

Сохранение настроек

Конфигурация сохраняется в файл, путь к которому зависит от базового дистрибутива:

  • Для дистрибутивов на базе RHEL (например, Rocky, SberLinux) используется путь /etc/sysconfig/nftables.conf;

  • Для других систем (например, Debian или Ubuntu) — /etc/nftables.conf.

Также будет установлен и запущен системный сервис nftables-persist, который обеспечивает восстановление конфигурации после перезагрузки.