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

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

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

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

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

При использовании Podman управление фаерволом включено по умолчанию.

Чтобы изменить параметры фаервола выполните следующие действия:

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

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

  3. Откройте файл globals.yml или создайте файл globals.d/firewall.yml.

  4. Укажите необходимые параметры. Ниже приведены значения по умолчанию:

    firewall_allow_mgmt_outgoing: "yes"
    firewall_enable_trailing_drop: "no"
    
    • firewall_allow_mgmt_outgoing — разрешает исходящий трафик через api_interface — интерфейс, через который сервисы OpenStack принимают API-запросы и взаимодействуют внутри кластера. Как правило, это интерфейс управляющей сети. Доступ необходим для подключения к базам данных, очередям сообщений и другим компонентам кластера.

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

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

    Правила firewall_rules можно сужать, но только в режиме enforcing: false с выключенным firewall_enable_trailing_drop.

  5. Сохраните изменения и примените их по инструкции в разделе Применение и проверка конфигурации.

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

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

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

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

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

Секция firewall_rules

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

Правила по умолчанию заданы в двух местах:

  • ansible/group_vars/all.yml в Kolla Ansible — переменная firewall_rules с блоками management, external и internal;

  • ansible/roles/firewall/defaults/main.yml — переменная default_firewall_rules с блоками external, internal и vrrp. Блок vrrp разрешает трафик протокола firewall_vrrp_protocol к адресу 224.0.0.18.

Ниже приведён набор firewall_rules из ansible/group_vars/all.yml. Копировать его в конфигурацию региона без изменений не требуется:

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 и vrrp;

    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:
      - keystack
      - podman_override
      - management
      - external
      - internal
    

    При переопределении этой переменной также укажите firewall_enable_trailing_drop: "no" (см. ограничение в разделе Тип правил по умолчанию — KeyStack).

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

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

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

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

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

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

firewall_enable_trailing_drop: "no"

firewall_traffic_types:
  - management
  - keystack
  - external
  - internal

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

При переопределении firewall_traffic_types укажите firewall_enable_trailing_drop: "no". При включённом завершающем правиле drop правила по умолчанию в цепочке keystack могут не примениться, что нарушит взаимодействие узлов региона. Это ограничение относится к переопределению порядка правил, а не к режиму наблюдения: явные правила deny продолжают блокировать трафик.

Добавление внешних узлов в 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, который обеспечивает восстановление конфигурации после перезагрузки.

Примеры настройки правил

Ограничение доступа из контейнера toolbox к Cinder API

В примере запрещается исходящий трафик из контейнера kolla_toolbox на порт 8776/tcp, используемый Cinder API. Правило применяется только при использовании контейнером сети типа bridge.

  1. Откройте веб-интерфейс развёрнутого GitLab.

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

  3. Откройте или создайте файл globals.d/firewall.yml.

  4. Добавьте группу правил toolbox в секцию firewall_containers:

    firewall_containers:
      toolbox:
        deny:
          - "0.0.0.0/0"
        destination_ports:
          - "8776"
        containers:
          - "kolla_toolbox"
    

    toolbox — произвольное имя группы правил. Значение 0.0.0.0/0 задаёт все IPv4-адреса, а containers — контейнер, к исходящему трафику которого применяется ограничение.

  5. Сохраните изменения и примените их по инструкции Применение и проверка конфигурации.

Ограничение доступа по SSH с гипервизора

В примере запрещаются входящие SSH-соединения с выбранного гипервизора на порт 22/tcp узлов региона. Запрещающее правило должно применяться раньше разрешающего правила keystack.

  1. Откройте веб-интерфейс развёрнутого GitLab.

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

  3. Откройте или создайте файл globals.d/firewall.yml.

  4. Добавьте конфигурацию:

    firewall_enable_trailing_drop: "no"
    
    firewall_rules:
      hypervisor_ssh:
        deny:
          - "<hypervisor_ip>/32"
        destination_ports:
          - "22"
    
    firewall_traffic_types:
      - hypervisor_ssh
      - keystack
    

    Вместо <hypervisor_ip> укажите IP-адрес гипервизора без угловых скобок. Суффикс /32 ограничивает правило одним IPv4-адресом. hypervisor_ssh — имя группы правил, которое должно совпадать в firewall_rules и firewall_traffic_types.

    Если в firewall_rules уже заданы другие группы, сохраните их и добавьте hypervisor_ssh. При необходимости укажите остальные используемые типы в firewall_traffic_types, сохранив hypervisor_ssh перед keystack.

    При переопределении порядка правил обязательно укажите firewall_enable_trailing_drop: "no". Явное правило deny при этом блокирует SSH-соединения.

  5. Сохраните изменения и примените их по инструкции Применение и проверка конфигурации.

Применение и проверка конфигурации

  1. Откройте веб-интерфейс GitLab и перейдите в репозиторий региона project_k / deployments / <имя региона>.

  2. Создайте новый пайплайн: Build > Pipelines > New pipeline.

  3. Оставьте ветку по умолчанию, соответствующую установленному релизу.

  4. В переменной KOLLA_ANSIBLE_DEPLOY_ACTION укажите значение reconfigure.

  5. В переменной KOLLA_ARGS укажите значение --tags firewall.

  6. Запустите пайплайн, нажав кнопку New pipeline.

  7. Дождитесь завершения задач на этапе setup.

  8. Запустите задачу deploy на этапе deploy и дождитесь её успешного завершения.

  9. Подключитесь к узлу, на котором применялись правила, по SSH и проверьте текущую конфигурацию nftables:

    $ sudo nft list ruleset
    
  10. Проверьте, что заданные правила присутствуют, разрешённые соединения устанавливаются, а запрещённые блокируются.

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

    $ sudo journalctl -k --grep='FW-DROP|FW-PERMIT'
    

Изменения, внесённые вручную командой nft, будут заменены при следующем применении конфигурации фаервола. Для сохранения правил задавайте их в репозитории региона.