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

Развёртывание qf

Руководство охватывает Control Plane в Kubernetes или на отдельном Linux-сервере, установку агентов через Ansible или вручную и развёртывание сервиса аналитики.

Репозиторий и пакеты qf закрыты. Перед установкой нужен доступ к репозиторию и пакетам организации qzmi4meister в GitHub и GHCR.

Компоненты qf

КомпонентЧто это
qf-cpControl Plane: REST API, Web UI, gRPC-сервер, PKI и компилятор политик. Требует PostgreSQL.
qf-agentАгент фаервола с eBPF/TC datapath. Работает на каждом защищаемом Linux-хосте и требует bpf_loop, ring buffer и BTF.

Матрица ядро → датапас

Агент выбирает режим attach по возможностям ядра (проверяет TCX, откат на classic); сборка матчера и лимит правил единые:

ЯдроAttachСборка матчераЛимит правил
≥6.6TCX (bpf_link)bpf_loop2048
<6.6 (с нужными eBPF-фичами)классический clsact+cls_bpfbpf_loop2048

Агент проверяет функции ядра при старте, а не только номер версии. Mainline-ядро 5.17 и новее содержит нужные функции; подходит и более старое ядро дистрибутива с соответствующими backport. Если функций нет, агент завершает запуск и называет их в ошибке. На ядре 6.6 и новее агент использует TCX. На ядре ниже 6.6 рядом с Cilium агент не запускается из-за конфликта общего clsact qdisc.


1. Control Plane в Kubernetes (Helm)

Предпосылки

  • Kubernetes 1.24+
  • PostgreSQL 14+, доступный из кластера
  • Helm 3.10+
  • CP нужен PersistentVolume под PKI-хранилище (CA, серты, ключ подписи бандлов)

1.1 Подготовить values-файл

Создать values-prod.yaml (не коммитить в git):

image:
repository: ghcr.io/qzmi4meister/qf-cp
pullPolicy: Always
imagePullSecrets:
- name: ghcr-secret

replicaCount: 1

secrets:
dbDSN: "postgres://qf:PASSWORD@postgres.qf.svc.cluster.local:5432/qf"
masterKey: "GENERATE_WITH: openssl rand -hex 32"
jwtSecret: "GENERATE_WITH: openssl rand -hex 32"
adminEmail: "admin@example.com"
adminPassword: "CHANGE_ME"
# metricsToken: "GENERATE_WITH: openssl rand -hex 32" # опц.: Bearer для /metrics; без него /metrics = 404
# oidcClientSecret: "..." # опц.: OIDC SSO (+ QF_OIDC_* в env)

env:
QF_CP_HOST: "qf.example.com" # hostname в TLS SAN; агенты подключаются по этому имени
QF_CP_ENDPOINT: "qf.example.com:31444" # адрес энролмента, анонсируемый агентам
QF_GRPC_ADDR: ":8443"
QF_ENROLL_ADDR: ":8444"
QF_HTTP_ADDR: ":8080"
QF_PKI_DIR: /etc/qf/pki
QF_LOG_LEVEL: info

pki:
persistentVolume:
storageClass: "longhorn" # или другой класс; "" = класс кластера по умолчанию
size: 1Gi

service:
type: ClusterIP

agentService:
enabled: true
type: NodePort
externalTrafficPolicy: Local
grpcNodePort: 31443
enrollNodePort: 31444

ingress:
enabled: true
className: "traefik" # или nginx
host: qf.example.com
tls:
- secretName: qf-cp-tls
hosts:
- qf.example.com

Master key и JWT-ключи генерируются до установки:

echo "masterKey: $(openssl rand -hex 32)"
echo "jwtSecret: $(openssl rand -hex 32)"
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 \
-out jwt-private.pem
chmod 600 jwt-private.pem

Стабильный QF_JWT_PRIVATE_KEY сохраняет access-токены после перезапуска даже одной реплики. Для нескольких реплик он обязателен вместе с общим PKI-хранилищем: RWX PVC либо pki.existingSecret. Требования и пример описаны в разделе о масштабировании.

1.2 Установка / апгрейд

Вход в закрытый OCI-реестр выполняется токеном с правом чтения пакетов:

GITHUB_USER="$(gh api user --jq .login)"
gh auth token | helm registry login ghcr.io -u "$GITHUB_USER" --password-stdin

Kubernetes использует отдельный image pull secret:

kubectl create namespace qf --dry-run=client -o yaml | kubectl apply -f -
kubectl -n qf create secret docker-registry ghcr-secret \
--docker-server=ghcr.io \
--docker-username="$GITHUB_USER" \
--docker-password="$(gh auth token)"
VERSION=0.25.6 # нужная версия релиза
helm upgrade --install qf-cp \
oci://ghcr.io/qzmi4meister/helm/qf-cp \
--version "$VERSION" \
--namespace qf --create-namespace \
-f values-prod.yaml \
--set-file secrets.jwtPrivateKey=jwt-private.pem

Дождаться пода:

kubectl -n qf rollout status deployment/qf-cp
kubectl -n qf get pods

CP прогоняет миграции БД автоматически при первом старте. Логи:

kubectl -n qf logs -l app.kubernetes.io/name=qf-cp --tail=50

1.3 Опубликовать порты для агентов

Control Plane слушает gRPC на 8443, enrollment на 8444, а REST/UI на 8080. Внешние порты зависят от выбранного Service. В примере NodePort это 31443 и 31444; REST/UI публикуется через Ingress на 443.

Вариант A — NodePort для on-prem и bare metal:

Добавить в values:

agentService:
enabled: true
type: NodePort
externalTrafficPolicy: Local
grpcNodePort: 31443
enrollNodePort: 31444

Затем в env:

env:
QF_CP_ENDPOINT: "<agent-vip-or-dns>:31444"
QF_CP_HOST: "<agent-vip-or-dns>"

Вариант B — LoadBalancer для облака или MetalLB:

agentService:
enabled: true
type: LoadBalancer
externalTrafficPolicy: Local

После назначения EXTERNAL-IP QF_CP_HOST и QF_CP_ENDPOINT должны указывать на этот IP или DNS-имя. Подробности о сохранении исходного IP агента описаны в гайде по балансировщику.

Вариант C — отдельный Ingress для gRPC: TCP-passthrough Ingress/Gateway на порты 8443 и 8444 рядом с HTTP-Ingress под UI.

1.4 Проверка

# Health-check
curl https://qf.example.com/healthz

# Логин
curl -c cookies.txt -X POST https://qf.example.com/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"CHANGE_ME"}'

# Список хостов (изначально пуст)
curl -b cookies.txt https://qf.example.com/hosts | jq .

1.5 Апгрейд CP

NEW_VERSION=0.25.6
helm upgrade qf-cp \
oci://ghcr.io/qzmi4meister/helm/qf-cp \
--version "$NEW_VERSION" \
--namespace qf \
-f values-prod.yaml \
--set-file secrets.jwtPrivateKey=jwt-private.pem \
--set image.tag="$NEW_VERSION"

2. Control Plane на отдельной Linux-машине

Запуск qf-cp напрямую как systemd-сервис на любом Linux x86_64/arm64-сервере.

2.1 Предпосылки

  • Linux x86_64 или arm64
  • PostgreSQL 14+ (локальный или удалённый)
  • Доступ к исходникам qf
  • Go 1.25.12+
  • Node.js 22.13–22.x или 24.x
  • TLS reverse proxy на порту 443 для REST и UI

2.2 Установить PostgreSQL и создать базу

# Debian/Ubuntu
sudo apt install -y postgresql

sudo -u postgres psql <<'SQL'
CREATE USER qf WITH PASSWORD 'CHANGE_ME';
CREATE DATABASE qf OWNER qf;
SQL

2.3 Собрать бинарь из исходников

gh repo clone qzmi4meister/qf
cd qf
make ui-build build-cp
sudo install -m 0755 qf-cp /usr/local/bin/qf-cp

2.4 Создать конфигурацию

sudo mkdir -p /etc/qf/pki
sudo tee /etc/qf/cp.env <<'EOF'
QF_DB_DSN=postgres://qf:CHANGE_ME@localhost:5432/qf
QF_MASTER_KEY=GENERATE_WITH_openssl_rand_-hex_32
QF_JWT_SECRET=GENERATE_WITH_openssl_rand_-hex_32
QF_CP_HOST=qf.example.com
QF_CP_ENDPOINT=qf.example.com:8444
QF_HTTP_ADDR=127.0.0.1:8080
QF_GRPC_ADDR=:8443
QF_ENROLL_ADDR=:8444
QF_TRUSTED_PROXIES=127.0.0.1/32
QF_PKI_DIR=/etc/qf/pki
QF_LOG_LEVEL=info
QF_ADMIN_USERNAME=admin
QF_ADMIN_EMAIL=admin@example.com
QF_ADMIN_PASSWORD=CHANGE_ME
EOF
sudo chmod 600 /etc/qf/cp.env

Сгенерировать ключи перед правкой:

openssl rand -hex 32 # вставить как QF_MASTER_KEY
openssl rand -hex 32 # вставить как QF_JWT_SECRET

Опциональные переменные (добавить в cp.env по необходимости):

ПеременнаяНазначение
QF_METRICS_TOKENBearer-токен для GET /metrics. Не задан → эндпоинт не смонтирован (404). Скрейпер шлёт тот же токен (см. ops-runbook §5.3).
QF_MASTER_KEY_FILEПуть к файлу с master-key (например на tmpfs). Имеет приоритет над QF_MASTER_KEY и рекомендуется для production.
QF_OIDC_ISSUER / QF_OIDC_CLIENT_ID / QF_OIDC_CLIENT_SECRET / QF_OIDC_REDIRECT_URLSSO через OIDC. Для включения обязательны первые три переменные; redirect по умолчанию — /auth/oidc/callback. CP требует email_verified, находит учётную запись по email и после первого входа привязывает её к стабильному sub.
QF_TRUSTED_PROXIESCIDR-список прокси, которым доверять X-Real-IP/X-Forwarded-For (иначе rate-limit по адресу прокси).
QF_INGEST_SHARDS / QF_INGEST_LOG_WORKERSЧисло очередей ingest и воркеров на очередь (по умолчанию 1×4).
QF_HEARTBEAT_FLUSH_INTERVAL_MS / QF_HEARTBEAT_BATCH_MAXТюнинг батчинга heartbeat-записей под большим флотом.
QF_UI_POLL_INTERVAL / QF_LIVENESS_INTERVALИнтервалы UI-poll и host-liveness lane.

2.5 Создать systemd-юнит

sudo tee /etc/systemd/system/qf-cp.service <<'EOF'
[Unit]
Description=qf Control Plane
After=network.target postgresql.service
Requires=network.target

[Service]
Type=simple
User=root
EnvironmentFile=/etc/qf/cp.env
ExecStart=/usr/local/bin/qf-cp
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now qf-cp
sudo systemctl status qf-cp

2.6 Проверка

# Проверить, что миграции прошли
sudo journalctl -u qf-cp -n 30

# Health-эндпоинт
curl http://localhost:8080/healthz

# Web UI через TLS reverse proxy
# https://qf.example.com/app/

2.7 Фаервол

Наружу открываются два agent-порта и HTTPS reverse proxy. Внутренний HTTP-порт 8080 остаётся доступен только на loopback:

# ufw (Debian/Ubuntu)
sudo ufw allow 8443/tcp comment "qf gRPC"
sudo ufw allow 8444/tcp comment "qf enrollment"
sudo ufw allow 443/tcp comment "qf REST and UI"

3. Агент: развёртывание через Ansible

Ansible-роль скачивает пакет из GitHub Releases, ставит его и запускает/включает systemd-сервис. Поддерживает и apt (Debian/Ubuntu), и dnf/yum (RHEL/Fedora/Rocky).

3.1 Настроить инвентори

Отредактировать deploy/ansible/inventory.yml:

all:
children:
qf_agents:
hosts:
web1:
ansible_host: 192.168.1.10
web2:
ansible_host: 192.168.1.11
db1:
ansible_host: 192.168.1.20
vars:
ansible_user: root
ansible_ssh_private_key_file: ~/.ssh/id_ed25519
qf_endpoint: qf.example.com
qf_enroll_token: "PASTE_BULK_TOKEN_HERE"
qf_ca_src: files/qf-ca.crt
# Сохраняет безопасное значение агента по умолчанию: весь IPv6 закрыт.
qf_drop_ipv6: true

3.2 Настроить агента перед деплоем

Роль пишет /etc/qf/agent.conf, когда заданы qf_endpoint и qf_enroll_token в inventory. Без них роль сохраняет конфигурацию из пакета.

Базовые параметры /etc/qf/agent.conf:

QF_ENDPOINT=qf.example.com
QF_ENROLL_TOKEN=PASTE_ENROLL_TOKEN_HERE

Для первого подключения также нужен один из вариантов доверия к CA, описанных ниже.

QF_ENDPOINT=host выводит gRPC :31443, enrollment :31444 и REST https://host. Это соответствует стандартному NodePort. Для отдельного сервера используется QF_ENDPOINT=host:8443, а enrollment будет на 8444. Интерфейс определяется по default route; для явного выбора используется QF_IFACE или QF_IFACES.

CA энролмента: роль настраивает один из трёх способов проверки CA. Для этого задаётся одна из переменных роли: qf_ca_src (путь к CA PEM CP на control-ноде, файл копируется на хост), qf_enroll_ca_fingerprint (SHA-256, 64 hex-символа) или qf_enroll_ca_fetch: true при TLS-сертификате REST API, которому доверяет системное хранилище агента. См. install-airgapped.md.

Получить enrollment-токен: CP Web UI → TokensNew token → скопировать значение.

Либо через API:

curl -s -b cookies.txt -X POST https://qf.example.com/tokens \
-H 'Content-Type: application/json' \
-d '{"type":"bulk","label_template":{"env":"prod"},"ttl_seconds":86400,"max_uses":100}' | jq -r .token

Поле target_group_id bulk-токена может содержать UUID группы. Подключённые этим токеном хосты становятся членами группы и наследуют её лейблы. См. fleet-management.md.

3.3 Деплой

# Установить pip-зависимости (однократно)
pip install ansible
: "${GITHUB_PAT:?set GITHUB_PAT with repo access}"

# Установка на все хосты группы qf_agents
GITHUB_TOKEN="$GITHUB_PAT" \
ansible-playbook -i deploy/ansible/inventory.yml deploy/ansible/deploy-agent.yml

# Тот же запуск через Makefile с явным inventory
GITHUB_TOKEN="$GITHUB_PAT" \
make deploy-agent ANSIBLE_INVENTORY=deploy/ansible/inventory.yml

# Установка из внутреннего репозитория не требует GitHub PAT:
ansible-playbook -i deploy/ansible/inventory.yml deploy/ansible/deploy-agent.yml \
-e qf_agent_source=repo

# Установка на один хост
ansible-playbook -i deploy/ansible/inventory.yml deploy/ansible/deploy-agent.yml --limit web1

Роль:

  1. Получает .deb или .rpm из выбранного источника.
  2. Устанавливает пакет через apt, dnf или yum.
  3. Формирует конфигурацию enrollment и запускает qf-agent.service.
  4. Проверяет активность сервиса. Агент сам проверяет функции eBPF при старте.

3.4 Переопределить версию пакета

ansible-playbook -i deploy/ansible/inventory.yml deploy/ansible/deploy-agent.yml \
-e qf_agent_version=0.25.6

4. Агент: ручная установка на Linux

Работает на любом Linux x86_64-хосте — bare metal, VM или k8s-нода. Нужно ядро с eBPF-фичами bpf_loop + ring-buffer (mainline ≥5.17 или дистро-бэкпорт).

4.1 Установить пакет

VERSION=0.25.6 # нужная версия релиза

# Debian / Ubuntu
gh release download v${VERSION} -R qzmi4meister/qf -p "qf-agent_${VERSION}_amd64.deb"
sudo dpkg -i qf-agent_${VERSION}_amd64.deb

# RHEL / Fedora / Rocky
gh release download v${VERSION} -R qzmi4meister/qf -p "qf-agent-${VERSION}-1.x86_64.rpm"
sudo rpm -i qf-agent-${VERSION}-1.x86_64.rpm

Пакет ставит:

  • /usr/sbin/qf-agent — бинарь
  • /etc/qf/agent.conf — шаблон конфига
  • /lib/systemd/system/qf-agent.service — systemd-юнит

После первого успешного применения политики агент создаёт runtime-state /var/lib/qf/policy.blob: подписанный last-good bundle для рестарта без CP.

4.2 Настроить

sudo nano /etc/qf/agent.conf
# CP endpoint (host или host:port). Выводит gRPC :31443, enroll :31444,
# REST https://host. С явным портом P: gRPC :P, enroll :P+1.
QF_ENDPOINT=qf.example.com

# Bootstrap-токен — из CP UI (страница Tokens) или API.
QF_ENROLL_TOKEN=tok_xxxxxxxxxxxx

# ── Проверка CA энролмента: выбрать один вариант для первого подключения ──
# Enrollment gRPC использует внутренний CA CP, поэтому агент обязан ему доверять.
# QF_ENROLL_CA=/etc/qf/ca.crt # заранее положенный CA PEM (скопировать с CP)
# QF_ENROLL_CA_FINGERPRINT=PASTE_SHA256_HEX_HERE # получает CA по REST и сверяет SHA-256 DER
# QF_ENROLL_CA_FETCH=true # только если TLS-сертификат REST API системно доверен

# ── Дополнительные параметры ──
# QF_IFACE= # пусто = автодетект из дефолтного маршрута
# QF_PKI_DIR=/etc/qf
# QF_LOG_LEVEL=info
# QF_FAIL_CLOSED=false
# QF_DROP_IPV6=true # ДЕФОЛТ: режет весь IPv6 (opt-in гейт).
# =false → полный v6-энфорс (CIDR/ipset/conntrack); на dual-stack/Cilium разрешить ICMPv6 ND/RA
# QF_DENY_UNKNOWN_PROTO=false # ДЕФОЛТ: неподдержанный L4 (SCTP/GRE/ESP/…) проходит
# без энфорсмента. =true → подчиняется default-action (дроп при
# DENY). ESP=IPsec / GRE=VPN / SCTP=телеком могут быть легитимны —
# сначала проверить. См. ops-runbook §8.4.

Интерфейс автодетектится из дефолтного маршрута. Переопределить или проверить:

ip route get 8.8.8.8 | awk '{print $5; exit}'

4.3 Запустить агента

sudo systemctl enable --now qf-agent
sudo systemctl status qf-agent

При первом старте агент:

  1. Подключается к вычисленному адресу enrollment (QF_ENDPOINT:31444) и предъявляет bootstrap-токен.
  2. Отправляет CSR; CP подписывает его и возвращает mTLS-сертификаты.
  3. Хранит сертификаты в QF_PKI_DIR; при следующих запусках повторное подключение не требуется.
  4. Подключается к вычисленному адресу gRPC (QF_ENDPOINT:31443) по mTLS и ждёт пакет правил.

4.4 Проверить энролмент

# Логи агента
journalctl -u qf-agent -f

# На CP — хост должен появиться со статусом "active"
curl --fail --silent --show-error -b cookies.txt \
https://qf.example.com/hosts | jq '.[] | {hostname, status}'

4.5 Апгрейд агента

VERSION=NEW_VERSION

# Debian / Ubuntu — сохраняет /etc/qf/agent.conf
sudo dpkg -i qf-agent_${VERSION}_amd64.deb

# RHEL
sudo rpm -U qf-agent-${VERSION}-1.x86_64.rpm

sudo systemctl restart qf-agent

4.6 Сетевые топологии

  • Один интерфейс — агент выбирает интерфейс default route.
  • Bond — в QF_IFACE указывается логический интерфейс, например bond0.
  • Несколько активных интерфейсовQF_IFACES=eth0,eth1 включает отдельные срезы правил и conntrack для каждого интерфейса. Область политики описана в отдельном руководстве.
  • Хост с Cilium — на ядре 6.6 и новее qf использует TCX рядом с Cilium. На ядре ниже 6.6 загрузчик останавливается из-за конфликта clsact qdisc.

5. Сервис аналитики (Helm)

qf-analytics — отдельный read-only сервис отчётов по событиям фаервола (обзор работы — analytics.md). Разворачивается своим чартом, вне релиза CP.

5.1 Предпосылки

  • Доступ к источнику данных read-only: PostgreSQL (CP-primary или RO-реплика) либо OpenSearch (SIEM-tee фаервол-событий).
  • JWKS-эндпоинт CP (https://<cp>/.well-known/jwks.json) — по нему офлайн проверяются операторские токены; без него сервис не стартует.
  • Публикация под тем же родительским доменом, что CP (путь /analytics), чтобы cookie qf_token доходила и SSO работало из консоли CP.

5.2 Установка / апгрейд

VERSION=0.25.6 # нужная версия релиза
helm upgrade --install qf-analytics \
oci://ghcr.io/qzmi4meister/helm/qf-analytics \
--version "$VERSION" \
--namespace qf \
--set 'imagePullSecrets[0].name=ghcr-secret' \
--set env.ANALYTICS_JWKS_URL=https://qf.example.com/.well-known/jwks.json \
--set env.ANALYTICS_SOURCE=postgres \
--set-string 'env.ANALYTICS_ROLES=admin\,auditor' \
--set secrets.sourceDSN='postgres://ro_user:PASSWORD@postgres.qf.svc.cluster.local:5432/qf' \
--set traefikRoute.enabled=true \
--set traefikRoute.host=qf.example.com \
--set traefikRoute.tlsSecret=qf-cp-tls

Для источника opensearch: --set env.ANALYTICS_SOURCE=opensearch, --set secrets.sourceDSN='https://user:PASSWORD@opensearch:9200', --set env.ANALYTICS_SOURCE_INDEX='qf-events*'. Syslog-tee несёт только rule_id, поэтому для имён правил и политик в отчёте задаётся PostgreSQL DSN с правами только на чтение: --set-string secrets.enrichDSN='postgres://ro_user:PASSWORD@postgres.qf.svc.cluster.local:5432/qf'. Чарт помещает строку подключения в Kubernetes Secret и подключает её к контейнеру через secretRef. Полный справочник переменных — analytics-config.md.

Периметровый basic-auth (для контуров без SSO, напр. дев-стенд) включается секретом с htpasswd: --set traefikRoute.basicAuthSecret=SECRET_NAME — middleware закрывает весь /analytics (UI+API) первым в цепочке. По умолчанию выключен; в проде перед сервисом — SSO-домен CP.

Проверка:

kubectl -n qf rollout status deployment/qf-analytics
curl https://qf.example.com/analytics/healthz

5.3 Непрерывный мониторинг в SIEM

Встроенный поиск аномалий выполняется только при построении отчёта. Для постоянных уведомлений используется механизм мониторинга установленной версии SIEM. В OpenSearch детектор ограничивается документами kind.keyword=rule, группируется по hostname.keyword и анализирует число событий с msgid.keyword=deny. qf-analytics не создаёт и не обслуживает такие детекторы.


Аутентификация и RBAC

  • RBAC-роли: admin (всё, включая управление пользователями), editor (мутации), auditor (только чтение). API-токены для автоматизации/CI.
  • OIDC SSO опционально (любой провайдер) рядом с локальными аккаунтами.
  • Первичный admin создаётся при первом старте из QF_ADMIN_EMAIL / QF_ADMIN_PASSWORD.

Устранение неполадок

Хост завис в enrolling

  1. journalctl -u qf-agent -n 50 — искать TLS- или connection-ошибки
  2. nc -zv qf.example.com 31444 — проверить доступность порта энролмента (голый QF_ENDPOINT выводит :31444; standalone CP использует :8444)
  3. Проверить, что токен валиден и не истёк: CP UI → Tokens
  4. Проверить, что QF_CP_HOST на CP совпадает с hostname/IP в QF_ENDPOINT — mismatch TLS SAN валит handshake
  5. Отсутствует/неверный CA энролмента → certificate signed by unknown authority: задать QF_ENROLL_CA / QF_ENROLL_CA_FINGERPRINT (см. install-airgapped.md)

Бандл не приходит

  1. В логах агента должны быть bundle received и bundle applied
  2. Проверить, что mTLS-серты есть: ls -la /etc/qf/agent.crt, agent.key, ca.crt должны присутствовать
  3. Проверить доступность gRPC-порта: nc -zv qf.example.com 31443 (standalone CP: :8443)
  4. Проверить срок серта: openssl x509 -in /etc/qf/agent.crt -noout -dates
  5. Если сертификат истёк, выполнить повторный enrollment с резервной копией текущей пары и новым single_host-токеном.

Агент не стартует — BPF-ошибки

journalctl -u qf-agent -n 20
  • BPF load failed: permission denied — не хватает capabilities; проверить, что в systemd-юните есть AmbientCapabilities=CAP_NET_ADMIN CAP_BPF CAP_PERFMON (CAP_SYS_ADMIN не требуется)
  • kernel lacks required eBPF features: bpf_loop helper (...) — ядру не хватает eBPF-хелпера/мапы для датапаса; нужен bpf_loop + ring-buffer (mainline ≥5.17 или дистро-бэкпорт). Агент называет отсутствующую фичу в логе.
  • interface not foundQF_IFACE не совпадает с реальным именем интерфейса; проверить ip link

CP не стартует — ошибки БД

  • migrations failed — PostgreSQL недоступен либо роль не владеет пустой базой и схемой. Для выделенной базы роль qf назначается владельцем базы; для отдельной схемы ей нужны USAGE и CREATE.
  • connection refused — проверить host/port в QF_DB_DSN и что PostgreSQL запущен
  • master key must be 32 bytesQF_MASTER_KEY должен быть 64-символьной hex-строкой (openssl rand -hex 32)

TLS-ошибки

ОшибкаПричинаРешение
certificate signed by unknown authorityУ агента неверный ca.crt или изменился CA Control PlaneСверить CA через доверенный канал; при смене CA выполнить повторный enrollment
certificate has expiredАвтоматическое продление 90-дневного сертификата не завершилосьВыполнить повторный enrollment с single_host-токеном
x509: certificate is valid for X, not YMismatch QF_CP_HOSTЗадать QF_CP_HOST на hostname, который используют агенты