Эксплуатация qf
Руководство описывает резервное копирование, ротацию поддерживаемых ключей, обслуживание PostgreSQL, масштабирование и диагностику qf.
Содержание
- Бэкап и восстановление
- Ротация секретов
- Управление сертификатами
- Обслуживание PostgreSQL
- Масштабирование qf-cp
- Диагностика
- Управление политиками
- Известные ограничения
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.key | TLS-сертификат и ключ сервера 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.
Процедура:
-
Остановить все реплики qf-cp и переместить текущую пару в защищённую резервную копию:
install -d -m 700 /secure-backup/qf-bundle-signingmv /etc/qf/pki/bundle-signing.key /secure-backup/qf-bundle-signing/mv /etc/qf/pki/bundle-signing.pub /secure-backup/qf-bundle-signing/ -
Запустить одну реплику qf-cp. Она сгенерирует новую пару и зашифрует приватный ключ текущим
QF_MASTER_KEY. -
Переэнроллить каждый агент по процедуре из раздела 3.2.
ca.crtсохраняется, а новыйbundle-signing.pubприходит в ответе CP. -
После проверки всего парка запустить остальные реплики. При необходимости отката нужно остановить 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" \
-H "X-Tenant-ID: $TENANT_ID"
# 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.25.6 # версия целевого релиза
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.limitsHelm-чарта; - для очереди событий сверить
QF_INGEST_POOL_MAXсmax_connectionsPostgreSQL.
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.9 | warning |
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 :8443 | gRPC CP не слушает | Проверить старт CP, биндинг порта |
remote error: tls: certificate required | Агент не прислал сертификат | Проверить, что agent.crt/agent.key есть в QF_PKI_DIR |
no such host | DNS эндпоинта 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.
8.1 Связанный ICMP-трафик имеет состояние related
Датапас отслеживает связанные флоу: 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.