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

Эксплуатация qf

Руководство описывает резервное копирование, ротацию поддерживаемых ключей, обслуживание PostgreSQL, масштабирование и диагностику qf.


Содержание​

  1. Бэкап и восстановление
  2. Ротация секретов
  3. Управление сертификатами
  4. Обслуживание PostgreSQL
  5. Масштабирование qf-cp
  6. Диагностика
  7. Управление политиками
  8. Известные ограничения

1. Бэкап и восстановление​

1.1 Бэкап PKI-каталога​

PKI-каталог (QF_PKI_DIR, по умолчанию /etc/qf/pki) содержит:

ФайлОписание
ca.crtСертификат CA (публичный, можно распространять)
ca.keyПриватный ключ CA (AES-256-GCM, зашифрован QF_MASTER_KEY)
server.crt / server.keyTLS-сертификат и ключ сервера CP
bundle-signing.key / bundle-signing.pubПара ключей Ed25519 для подписи пакетов правил; приватный ключ зашифрован QF_MASTER_KEY
# Бэкап
PKI_DIR=/etc/qf/pki
BACKUP_FILE="qf-pki-$(date +%Y%m%d-%H%M%S).tar.gz"

tar -czf "$BACKUP_FILE" -C "$(dirname "$PKI_DIR")" "$(basename "$PKI_DIR")"
chmod 600 "$BACKUP_FILE"

# Хранить бэкап в защищённом месте (шифрование at-rest)
# Приватные ключи зашифрованы QF_MASTER_KEY, но архив всё равно содержит чувствительные данные

Резервная копия PKI должна соответствовать резервной копии QF_MASTER_KEY. Сам master key хранится отдельно от архива PKI. Без него закрытые ключи из архива нельзя расшифровать.

1.2 Восстановление PKI-каталога​

BACKUP_FILE="qf-pki-20260101-120000.tar.gz"
TARGET_DIR=/etc/qf

# Все реплики Control Plane должны быть остановлены до восстановления PKI.
systemctl stop qf-cp
tar -xzf "$BACKUP_FILE" -C "$TARGET_DIR"
chown -R root:root "$TARGET_DIR/pki"
chmod 700 "$TARGET_DIR/pki"
chmod 600 "$TARGET_DIR/pki"/*.key

# Запустить CP с тем же QF_MASTER_KEY, что использовался для этой копии
systemctl start qf-cp

1.3 Бэкап PostgreSQL​

# Полный дамп
pg_dump -h localhost -U qf -d qf -F c -f "qf-db-$(date +%Y%m%d-%H%M%S).pgdump"

# Восстановление
# --clean удаляет существующие объекты: восстановление выполняется в целевую
# базу, выбранную для полной замены данными из дампа.
pg_restore -h localhost -U qf -d qf --clean --if-exists \
"qf-db-20260101-120000.pgdump"

2. Ротация секретов​

2.1 QF_MASTER_KEY нельзя менять без перешифрования PKI​

QF_MASTER_KEY шифрует ca.key и bundle-signing.key. В поставке нет команды, которая перешифровывает эти файлы новым мастер-ключом. Простая замена переменной не является ротацией: qf-cp перестанет расшифровывать PKI и не запустится.

Поддерживаемой процедуры смены master key нет. Ключ хранится в менеджере секретов, резервируется вместе с соответствующей версией PKI и восстанавливается тем же значением. При ошибочной замене возвращается прежний ключ; зашифрованные файлы удалять нельзя.

2.2 Ротация QF_JWT_SECRET​

QF_JWT_SECRET подписывает refresh-токены (HS256). Ротация делает существующие refresh-токены недействительными. Уже выданные RS256 access-токены продолжают работать до окончания срока действия, по умолчанию не более 30 минут; затем пользователю нужно войти снова.

NEW_JWT=$(openssl rand -hex 32)

# Обновить и перезапустить
sed -i "s/QF_JWT_SECRET=.*/QF_JWT_SECRET=$NEW_JWT/" /etc/qf/cp.env
systemctl restart qf-cp

# Kubernetes
kubectl -n qf patch secret qf-cp-secrets \
--patch="{\"stringData\":{\"QF_JWT_SECRET\":\"$NEW_JWT\"}}"
kubectl -n qf rollout restart deployment/qf-cp

API-токены, хранящиеся в БД, эта ротация не затрагивает.

2.3 Ротация QF_JWT_PRIVATE_KEY​

QF_JWT_PRIVATE_KEY подписывает RS256 access-токены. После замены ключа ранее выданные access-токены перестают проходить проверку в Control Plane. Действующий refresh-токен позволяет получить access-токен с новым ключом, если одновременно не менялся QF_JWT_SECRET.

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 \
-out jwt-private-new.pem

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

qf-analytics кэширует известный публичный ключ до 24 часов. Для немедленного прекращения приёма старых access-токенов после ротации перезапускаются и его реплики.

2.4 Ротация ключа подписи bundle​

Ключ Ed25519 подписывает bundle политик. qf не поддерживает одновременную подпись старым и новым ключами. Поэтому агенты со старым публичным ключом отклоняют новые bundle и сохраняют last-good generation до повторного enrollment.

Процедура:

  1. Остановить все реплики qf-cp и переместить текущую пару в защищённую резервную копию:

    install -d -m 700 /secure-backup/qf-bundle-signing
    mv /etc/qf/pki/bundle-signing.key /secure-backup/qf-bundle-signing/
    mv /etc/qf/pki/bundle-signing.pub /secure-backup/qf-bundle-signing/
  2. Запустить одну реплику qf-cp. Она сгенерирует новую пару и зашифрует приватный ключ текущим QF_MASTER_KEY.

  3. Переэнроллить каждый агент по процедуре из раздела 3.2. ca.crt сохраняется, а новый bundle-signing.pub приходит в ответе CP.

  4. После проверки всего парка запустить остальные реплики. При необходимости отката нужно остановить CP и вернуть оба файла пары из резервной копии.


3. Управление сертификатами​

3.1 Проверить срок сертификата​

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

# Проверить сертификат конкретного агента
openssl x509 -in /etc/qf/agent.crt -noout -dates -subject

# Список всех сертификатов и их сроков в БД
psql "$QF_DB_DSN" -c "
SELECT h.hostname, c.serial, c.not_after, c.status
FROM certificates c
JOIN hosts h ON h.id = c.host_id
WHERE c.status = 'active'
ORDER BY c.not_after ASC;"

3.2 Переэнролмент агента (серт истёк или заменяется)​

Когда сертификат агента истекает или отзывается, CP отвергает его mTLS-соединение. Если автоматическое обновление уже невозможно, требуется повторный энролмент.

Повторный enrollment существующей записи выполняется только токеном single_host. bulk предназначен для новых хостов и не может заменить сертификат уже зарегистрированного hostname.

# 1. UUID существующей записи хоста
HOST_ID=PASTE_HOST_UUID_HERE

# 2. Создать single_host enrollment-токен через CP API (или UI)
TOKEN=$(curl --fail --silent --show-error -X POST https://cp.example.com/tokens \
-H "Authorization: Bearer $JWT" \
-H 'Content-Type: application/json' \
-d "{\"type\":\"single_host\",\"target_host_id\":\"$HOST_ID\",\"ttl_seconds\":3600,\"max_uses\":1}" \
| jq -r .token)

# 3. На агенте: остановить сервис, сохранить текущую пару для аварийного отката,
# записать TOKEN в QF_ENROLL_TOKEN и удалить только клиентскую пару
systemctl stop qf-agent
cp /etc/qf/agent.crt /etc/qf/agent.crt.bak
cp /etc/qf/agent.key /etc/qf/agent.key.bak
rm /etc/qf/agent.crt /etc/qf/agent.key
# записать полученное значение в QF_ENROLL_TOKEN файла /etc/qf/agent.conf

# 4. Запустить агента — host ID сохранится, прежний сертификат будет отозван
systemctl start qf-agent

ca.crt и bundle-signing.pub сохраняются: первый остаётся trust anchor, второй заменяется ответом CP. После успешного подключения проверяется очистка токена, затем удаляются резервные копии старой пары:

grep QF_ENROLL_TOKEN /etc/qf/agent.conf # пусто или отсутствует
rm /etc/qf/agent.crt.bak /etc/qf/agent.key.bak

Подробный сценарий — в руководстве по enrollment.

3.3 Немедленно отозвать доступ хоста​

Архивация хоста через API отзывает все его активные сертификаты, немедленно добавляет их serial в blocklist и разрывает текущий agent stream:

curl --fail --silent --show-error -o /dev/null -w '%{http_code}\n' -X DELETE \
https://cp.example.com/hosts/$HOST_ID \
-H "Authorization: Bearer $JWT"
# 204

Запись хоста остаётся в корзине и сохраняет историю. Если хост нужно вернуть в эксплуатацию, сначала используется POST /hosts/{id}/restore, затем выполняется повторный enrollment с новым single_host-токеном. Изменение одного поля status и прямой SQL не являются процедурой отзыва: они не гарантируют немедленный blocklist и разрыв активного stream.

3.4 Ротация gRPC-сертификата Control Plane​

server.crt и server.key защищают основной gRPC-канал агентов и enrollment. HTTPS для REST/UI завершается отдельным reverse proxy или Ingress и использует его сертификат.

# Остановить CP, сгенерировать новый серт, подписанный существующим CA
systemctl stop qf-cp

# Серт регенерируется автоматически через LoadOrInit, если отсутствует
mv /etc/qf/pki/server.crt /etc/qf/pki/server.crt.bak
mv /etc/qf/pki/server.key /etc/qf/pki/server.key.bak

systemctl start qf-cp
# CP регенерирует server.crt/server.key при старте, если файлы отсутствуют

4. Обслуживание PostgreSQL​

4.1 Управление партициями​

Партициями управляет встроенный PartitionManager (работает в CP каждые 24 часа). Ретенция по умолчанию:

ТаблицаТип партицииРетенция
log_eventsдневная7 дней
flow_eventsдневная14 дней
counter_snapshotsнедельная30 дней
system_eventsбез партиций (DELETE)30 дней

При каждом запуске и затем раз в 24 часа PartitionManager создаёт дневные партиции на 7 дней вперёд и недельные на 2 недели вперёд. Если ожидаемая партиция отсутствует, сначала нужно проверить журнал CP и перезапустить одну реплику: менеджер выполняет проверку сразу при старте.

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

4.2 Мониторинг здоровья партиций​

# Дерево партиций и оценка числа строк
psql "$QF_DB_DSN" -c "
SELECT
relid::regclass AS partition,
level,
pg_size_pretty(pg_total_relation_size(relid)) AS size,
pg_stat_get_live_tuples(relid) AS live_rows
FROM pg_partition_tree('log_events')
ORDER BY level, relid::regclass::text;"

4.3 Тюнинг PostgreSQL​

PostgreSQL должен оставаться под autovacuum. Размер памяти, WAL и пороги autovacuum настраиваются по метрикам конкретного кластера; фиксированные значения без учёта RAM, числа реплик CP и объёма событий могут ухудшить работу базы.

Для диагностики сначала проверяются задержка записи, использование соединений, autovacuum и размер партиций. Параметр QF_INGEST_POOL_MAX управляет отдельным пулом записи событий; суммарное число соединений всех реплик должно оставаться ниже max_connections PostgreSQL.


5. Масштабирование qf-cp​

Основное состояние qf-cp хранится в PostgreSQL, но всем репликам также нужны одинаковые секреты и один набор PKI. Поэтому одного изменения replicaCount недостаточно.

5.1 Горизонтальное масштабирование (Kubernetes)​

Требования к схеме с несколькими репликами:

  • Один набор PKI: все реплики монтируют один QF_PKI_DIR через ReadWriteMany PVC. Для заранее созданного неизменяемого PKI чарт также поддерживает pki.existingSecret, который монтируется только для чтения.
  • Одинаковый QF_MASTER_KEY: должен быть идентичен на всех репликах.
  • Одинаковый QF_JWT_SECRET: должен быть идентичен, чтобы валидация JWT работала между репликами.
  • Одинаковый QF_JWT_PRIVATE_KEY: все реплики должны подписывать RS256 access-токены одним ключом.
  • Балансировщик: распределяет gRPC (8443) и enrollment (8444) по репликам; агенты переподключаются автоматически при обрыве.

Реплики обмениваются уведомлениями через PostgreSQL LISTEN/NOTIFY; отдельный Redis не требуется. Состояние обмена видно по метрикам qf_pgbus_notify_total и qf_pgbus_notify_suppressed_total.

# override values.yaml для 3 реплик
replicaCount: 3
pki:
persistentVolume:
storageClass: nfs-client # должен поддерживать ReadWriteMany
accessMode: ReadWriteMany

Перед апгрейдом генерируется стабильный RSA-ключ:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out jwt-private.pem
VERSION=0.26.0 # версия целевого релиза
helm upgrade qf-cp oci://ghcr.io/qzmi4meister/helm/qf-cp \
--namespace qf \
--version "$VERSION" \
-f values-prod.yaml \
--set-file secrets.jwtPrivateKey=jwt-private.pem

secrets.jwtSecret и secrets.masterKey должны быть заданы в values-prod.yaml или существующем Kubernetes Secret. Helm-чарт отклоняет multi-replica-конфигурацию без jwtSecret и jwtPrivateKey.

5.2 Вертикальное масштабирование​

Если растёт время доставки пакетов правил:

  • проверить CPU и память реплик через Kubernetes;
  • проверить насыщение пулов PostgreSQL по метрикам;
  • увеличить ресурсы через resources.requests и resources.limits Helm-чарта;
  • для очереди событий сверить QF_INGEST_POOL_MAX с max_connections PostgreSQL.

5.3 Мониторинг​

qf-cp отдаёт Prometheus-метрики на GET /metrics (promhttp-хендлер; исключён из access-логов вместе с /healthz и /version).

Эндпоинт закрыт по умолчанию (метрики несут host-лейблы qf_agent_*{host_id,hostname} — весь инвентарь флота). Он монтируется только если задан QF_METRICS_TOKEN; без токена — 404 (не смонтирован), с неверным токеном — 401. Скрейпер шлёт токен как Bearer. Один и тот же токен задаётся в переменной CP QF_METRICS_TOKEN и в scrape-конфиге Prometheus:

scrape_configs:
- job_name: qf-cp
authorization:
type: Bearer
credentials: "PASTE_QF_METRICS_TOKEN_HERE"
# ...

Grafana-дашборд: monitoring/grafana/qf-cp.json — импорт через Dashboards → Import → Upload JSON.

Стартовые пороги алертов:

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

МетрикаУсловиеSeverity
up (scrape)== 0 дольше 1 минcritical
qf_grpc_active_streamsпадение >50% за 5 минwarning
qf_bundle_push_errors_total rate> 0.1/с дольше 2 минwarning
qf_bundle_push_duration_seconds p99> 5сwarning
qf_ingester_events_dropped_total rate> 0 дольше 1 минwarning
qf_http_requests_total{status=~"5.."} rate> 0.5/с дольше 2 минcritical
qf_auth_failures_total rate> 5/с дольше 5 минwarning
qf_db_pool_acquired_conns / (qf_db_pool_acquired_conns + qf_db_pool_idle_conns)> 0.9warning
qf_pgbus_notify_total{channel="qf_events"} rateрастёт без кросс-под SSE-зрителейwarning (subreg-gate нездоров)

6. Диагностика​

Хост завис в enrolling​

# 1. Логи агента
journalctl -u qf-agent -n 50 --no-pager

# 2. Сеть: внешний enrollment-порт доступен с агента
# QF_ENDPOINT без порта использует 31444; с портом P — P+1
CP_HOST=cp.example.com
ENROLLMENT_PORT=31444
nc -zv "$CP_HOST" "$ENROLLMENT_PORT"

# 3. Токен не истёк
curl --fail --silent --show-error https://cp.example.com/tokens \
-H "Authorization: Bearer $JWT" | jq '.[] | {id, expires_at, uses_count, max_uses}'

# 4. Mismatch TLS SAN: QF_ENDPOINT на агенте должен резолвиться на тот же host, что QF_CP_HOST на CP
# Например, агент подключается к cp.example.com:31444, а SAN сертификата содержит localhost
grep QF_ENDPOINT /etc/qf/agent.conf
# Должен совпадать с hostname в env-переменной QF_CP_HOST на CP

# 5. Для systemd-поставки: проверить внутренний listener CP
ss -tlnp | grep 8444

Пакет правил не приходит на агента​

# 1. gRPC агента подключён?
journalctl -u qf-agent | grep -E "connected|bundle|stream"

# Ошибка dead-man rollback остаётся pending и повторяется с backoff;
# после reconnect она также видна в GET /hosts/{id}/system-events.
journalctl -u qf-agent | grep -E "watchdog rollback failed|bundle rollback complete"

# 2. Сертификат агента действителен
openssl x509 -in /etc/qf/agent.crt -noout -dates
# если истёк → переэнроллить (см. раздел 3.2)

# 3. Логи пушера CP
# На хосте или поде CP
journalctl -u qf-cp | grep -E "bundle|push|stream" | tail -20
# kubectl -n qf logs deployment/qf-cp | grep bundle

# 4. Статус хоста в БД
psql "$QF_DB_DSN" -c "
SELECT h.id, h.hostname, s.status
FROM hosts h JOIN host_statuses s ON s.host_id = h.id
WHERE h.hostname='web-01';"
# Для stale сначала проверить сервис и сеть; повторный энролмент нужен только при проблеме сертификата

# 5. Политика существует и компилируется
curl --fail --silent --show-error https://cp.example.com/policies \
-H "Authorization: Bearer $JWT" | jq '.[] | {id, name}'

Рестарт агента при недоступном CP​

Last-good policy хранится отдельно от PKI и конфигурации:

stat /var/lib/qf/policy.blob
sha256sum /var/lib/qf/policy.blob
journalctl -u qf-agent -b --no-pager | \
grep -E 'cached bundle applied before CP connect|no cached bundle|cached bundle unavailable'

При валидном cache журнал содержит cached bundle applied before CP connect, а явные правила действуют до reconnect. Отсутствующий или невалидный cache в дефолтной конфигурации означает fail-open до получения bundle от CP. Удаление policy.blob удаляет offline last-good и не является штатным способом диагностики связности.

События не появляются в UI​

# 1. Статус агента active?
curl --fail --silent --show-error https://cp.example.com/hosts \
-H "Authorization: Bearer $JWT" | jq '.[] | {hostname, status}'

# 2. Агент шлёт события?
journalctl -u qf-agent | grep -E "event|batch|ingest"

# 3. Логи ingest на CP
journalctl -u qf-cp | grep -E "ingest|log_event|insert" | tail -20

# 4. PostgreSQL: строки в сегодняшней партиции
HOST_ID=PASTE_HOST_UUID_HERE
psql "$QF_DB_DSN" -v host_id="$HOST_ID" -c "
SELECT COUNT(*) FROM log_events
WHERE created_at >= CURRENT_DATE AND host_id=:'host_id';"

# 5. Сегодняшняя партиция существует
psql "$QF_DB_DSN" -c "
SELECT relname FROM pg_class
WHERE relname = 'log_events_' || to_char(CURRENT_DATE, 'YYYY_MM_DD');"
# Если отсутствует: проверить логи PartitionManager и перезапустить одну реплику CP

TLS / mTLS-ошибки​

ОшибкаВероятная причинаФикс
certificate signed by unknown authorityУ агента неверный ca.crt или не задан способ доверия при первом запускеСверить CA по доверенному каналу и параметры QF_ENROLL_CA*
certificate has expiredСертификат агента истёкПовторно подключить агента (раздел 3.2)
connection refused :8443gRPC CP не слушаетПроверить старт CP, биндинг порта
remote error: tls: certificate requiredАгент не прислал сертификатПроверить, что agent.crt/agent.key есть в QF_PKI_DIR
no such hostDNS эндпоинта CP не резолвитсяПроверить QF_ENDPOINT в agent.conf

CP не стартует​

journalctl -u qf-cp -n 30 --no-pager

Частые ошибки:

СообщениеПричинаФикс
QF_MASTER_KEY must be 32-byte hexКлюч отсутствует или неверной длиныЗадать корректный 64-символьный hex-ключ
migrations: ...БД недоступна или нет правПроверить QF_DB_DSN, что PostgreSQL поднят
listen ...: address already in useКонфликт портаПроверить ss -tlnp, поправить QF_HTTP_ADDR / QF_GRPC_ADDR
PKI initialized нет в логахИнициализация CA упалаПроверить, что QF_PKI_DIR доступен на запись, есть место на диске

Высокое потребление памяти или CPU на CP​

qf-cp не публикует pprof через HTTP. Для диагностики используются метрики контейнера, qf_bundle_push_duration_seconds, метрики пулов PostgreSQL и журнал с сообщениями acquire timeout. В Kubernetes дополнительно проверяются requests, limits, throttling CPU и рестарты по OOM.


Доступ к приватным пакетам​

Образ контейнера и Helm-чарт находятся в GHCR. Для приватного репозитория нужна аутентификация.

Helm и Kubernetes​

Вход Helm в registry выполняется один раз на административной машине; учётные данные сохраняются в ~/.config/helm/registry/config.json:

GITHUB_USER="$(gh api user --jq .login)"
printf '%s' "$GITHUB_TOKEN" | \
helm registry login ghcr.io -u "$GITHUB_USER" --password-stdin

Для общего PAT, который также скачивает приватные GitHub Releases, нужны scope repo и read:packages.

Image pull secret создаётся в namespace qf и обновляется после смены PAT:

kubectl create secret docker-registry ghcr-secret \
--docker-server=ghcr.io \
--docker-username="$GITHUB_USER" \
--docker-password="$GITHUB_TOKEN" \
-n qf --dry-run=client -o yaml | kubectl apply -f -

Установка агента из приватного release​

Ansible-роль читает PAT из переменной окружения GITHUB_TOKEN:

: "${GITHUB_PAT:?set GITHUB_PAT with repo access}"
GITHUB_TOKEN="$GITHUB_PAT" make deploy-agent

7. Управление политиками​

Политики применяются к хостам по label-селектору (matchLabels). Политика активна на хосте, когда все пары key-value селектора присутствуют в эффективных лейблах хоста (собственные лейблы + унаследованные лейблы групп).

7.1 Назначить политику хостам​

Открыть Policies → [имя политики] → Редактирование. В блоке Селектор хостов используются таблицы Хосты и Группы.

Два режима назначения:

РежимКнопкаЭффект
Один хостДобавить строку в таблице ХостыДобавляет в selector блок _hostId; лейблы хоста не меняются
Host groupДобавить строку в таблице ГруппыДобавляет блок _groupId и текущие matchLabels группы

После изменения действие Зафиксировать изменение и посмотреть зону влияния создаёт черновик. Зона влияния показывает затронутые хосты и различия ruleset; enforcement меняется только после действия Применить.

7.2 Снять политику с хостов или host-групп​

Открыть Policies → [имя политики] → Редактирование и использовать действие Снять у нужной строки.

Два режима снятия:

РежимКнопкаЭффект
Один хостСнять в таблице ХостыУбирает блок _hostId из selector; лейблы хоста не меняются
Host groupСнять в таблице ГруппыУбирает блок _groupId из selector

Удаление последнего блока формирует пустой selector {}, который выбирает весь парк. Консоль потребует подтверждение вводом имени политики. Если политика не должна действовать ни на одном хосте, её нужно удалить либо через API задать {"anyOf":[]}.

Хост соответствует блоку группы. Если эффективные лейблы хоста соответствуют блоку selector с _groupId, прямое снятие с хоста заблокировано. Членство в этой группе не проверяется: _groupId служит метаданными, а Control Plane сопоставляет сохранённые matchLabels. UI покажет:

«Matched through group 'X' — remove host from the group or remove the group below.»

В этом случае можно:

  • снять блок группы с политики;
  • изменить эффективные лейблы хоста так, чтобы блок перестал совпадать;
  • добавить более точное правило с высоким приоритетом, если хост должен остаться в области политики, но ему требуется исключение.

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

7.3 Посмотреть политики, применённые к хосту​

Открыть Hosts → [hostname] → вкладка Policies.

  • Показывает все политики, матчащие этот хост (выводится из эффективного ruleset).
  • Поле поиска фильтрует по имени политики или лейблу селектора (key=value).
  • Клик по имени политики → переход в редактор политики.

7.4 Проверить назначение через API​

CP_URL=https://cp.example.com
HOST_ID=PASTE_HOST_UUID_HERE
POLICY_ID=PASTE_POLICY_UUID_HERE

# Список политик, матчащих хост (через эффективный ruleset)
curl -s "$CP_URL/hosts/$HOST_ID/ruleset" \
-H "Authorization: Bearer $JWT" \
| jq '[.rules[].policy_name] | unique'

# Посмотреть селектор политики
curl -s "$CP_URL/policies/$POLICY_ID" \
-H "Authorization: Bearer $JWT" \
| jq '.selector'

# Список лейблов хоста
curl -s "$CP_URL/hosts/$HOST_ID" \
-H "Authorization: Bearer $JWT" \
| jq '{labels, effective_labels}'

8. Известные ограничения​

Задокументированные ограничения датапаса/CP.

Датапас отслеживает связанные флоу: ICMP-ошибка, ссылающаяся на отслеживаемое соединение (dest-unreachable / packet-too-big для активной TCP/UDP-сессии), читается как CT_RELATED. Правила со state=related (или states=["...","related"]) принимаются API и энфорсятся на хосте. Для PMTUD при default-deny достаточно правила related (либо явного allow ICMPv4 type 3 code 4 / ICMPv6 type 2 packet-too-big).

8.2 IPv6 включается явно; цепочка extension headers ограничена​

IPv6 энфорсится наравне с IPv4 — CIDR, ipset и stateful conntrack матчат v6 — при QF_DROP_IPV6=false; по умолчанию весь v6 режется целиком (opt-in гейт). При включённом v6 правила proto/port/TCP-flags/CIDR/ipset/conntrack применяются к IPv6, включая пакеты с extension-заголовками: парсер обходит цепочку extension-заголовков (Hop-by-Hop, Routing, Destination Options, AH, Mobility; до IPV6_EXT_MAX_HOPS = 6 заголовков) до реального L4-заголовка. ⚠️ На dual-stack/Cilium-нодах перед включением разрешить ICMPv6 ND/RA, иначе нода потеряет связность.

⚠️ Радиус. Дефолтный drop отбрасывает и служебный v6 (ICMPv6 Neighbor Discovery, link-local fe80::/10). Dual-stack хост с v6-mgmt потеряет v6-связность. Нужен v6 → QF_DROP_IPV6=false.

Без проверки правил проходят фрагменты, цепочки длиннее шести extension headers и неподдерживаемые L4-протоколы. QF_DROP_FRAGMENTS=true блокирует IPv4- и IPv6-фрагменты целиком; перед включением нужно проверить легитимный фрагментированный трафик. Неподдерживаемые протоколы описаны в разделе 8.4.

8.3 Эфемерный JWT-секрет при рестарте​

Когда QF_JWT_SECRET не задан, CP генерирует случайный секрет для refresh-токенов при старте. Без QF_JWT_PRIVATE_KEY CP также генерирует временный RSA-ключ access-токенов. В multi-replica-схеме значения между подами различаются, поэтому проверка токенов зависит от реплики. Для production задаются стабильные QF_JWT_SECRET длиной не менее 32 байт и QF_JWT_PRIVATE_KEY. Helm-чарт требует оба значения при replicaCount > 1.

8.4 Неподдержанные L4 (SCTP/GRE/ESP) по умолчанию проходят без энфорсмента​

Датапас разбирает только TCP / UDP / ICMP / ICMPv6. SCTP, GRE, ESP, OSPF и другие IP-протоколы по умолчанию проходят без проверки правил и не видны в Verdicts, Flows и Counters. Создать правило для такого протокола нельзя.

Флаг агента QF_DENY_UNKNOWN_PROTO=true направляет пакет с неизвестным L4 в default action соответствующего направления: при DENY пакет блокируется, при ALLOW проходит. Флаг не меняется бандлом CP. Фрагменты, обрезанные пакеты и non-IP-трафик вроде ARP/LLDP продолжают проходить; для фрагментов есть отдельный QF_DROP_FRAGMENTS.

⚠️ Раскатывать осторожно. Fail-closed может заблокировать легитимный трафик: ESP = IPsec (вплоть до management-канала/VPN), GRE = VPN-туннели, SCTP = телеком-сигнализация. Включать только после проверки — вне qf, этот трафик в телеметрии не виден, — что таких протоколов на хосте нет.

Неподдерживаемый L4 не попадает в счётчики qf. Связность до и после включения флага проверяется через ss -a, сетевые flow-логи и health checks приложений.

8.5 Асимметрия по IP-версии​

На IPv4 брешь неподдержанного L4 (8.4) — основной радиус. На IPv6 такой трафик уже режется дефолтным QF_DROP_IPV6=true (8.2); брешь возникает только если оператор снял v6-drop.