Ручной энролмент нового хоста
Как вручную подключить новый Linux-хост к qf control-plane (CP) без Ansible. Для парковых выкатов используйте роль deploy/ansible/roles/qf-agent — этот гайд для единичного/ручного случая и для понимания механики.
Что происходит при энролменте
Агент при первом старте предъявляет CP bootstrap-токен, CP подписывает агенту mTLS-клиентский сертификат (enrollment gRPC, без mTLS), после чего агент держит постоянный mTLS-стрим (AgentService.Stream): heartbeat, приём политик, отправка телеметрии. Токен одноразово-многоразовый (по max_uses), сам сертификат — долгоживущий и ротируется агентом.
Предпосылки
- CP доступен с хоста по сети. Порты (при дефолтном
QF_ENDPOINTбез явного порта):- gRPC mTLS-стрим —
<cp>:31443 - enrollment gRPC —
<cp>:31444 - REST/UI —
https://<cp>(для получения CA и минта токена)
- gRPC mTLS-стрим —
- На хосте: systemd, ядро с eBPF-фичами bpf_loop + ring-buffer (mainline ≥5.17 или дистро-бэкпорт; TC датапас), root для установки пакета.
- Доступ к CP UI или REST с ролью
admin(для выпуска токена).
Проверить связь с хоста:
timeout 3 bash -c '</dev/tcp/<cp>/31443' && echo "31443 ok"
timeout 3 bash -c '</dev/tcp/<cp>/31444' && echo "31444 ok"
Шаг 1. Выпустить enrollment-токен
Токен несёт лейблы хоста (через label_template) и/или привязку к конкретному хосту. Два типа:
bulk— проще для ручного случая: хост энроллится под своим hostname, лейблы берутся изlabel_template. Один токен можно переиспользовать (max_uses).single_host— жёстко привязан к заранее созданной записи хоста (target_host_id); используйте, когда хост уже заведён в UI.
Через UI
CP → Tokens → New token → выбрать тип, указать TTL / max_uses / лейблы → скопировать token (показывается один раз).
Через REST
Логин (сохранить cookie), затем выпуск:
# 1) логин
curl -sk -c cookie.txt -X POST https://<cp>/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"<pass>"}'
# 2) bulk-токен с лейблами (напр. env=prod, role=web)
curl -sk -b cookie.txt -X POST https://<cp>/tokens \
-H 'Content-Type: application/json' \
-d '{"type":"bulk","label_template":{"env":"prod","role":"web"},"ttl_seconds":3600,"max_uses":1}'
# → {"token":"<ENROLL_TOKEN>", ...} — token показывается ТОЛЬКО при создании
ttl_seconds по умолчанию 3600, max_uses — 1. Для одного хоста max_uses:1 достаточно.
Шаг 2. Установить пакет агента
Пакеты публикуются в GitHub Release на каждый релиз vX.Y.Z. Репозиторий приватный — качать через gh (не прямым URL, он даст 404):
VER=0.9.55 # актуальную версию взять из CP: curl -sk https://<cp>/version
# Debian/Ubuntu
gh release download v$VER -R qzmi4meister/qf -p "qf-agent_${VER}_amd64.deb"
sudo dpkg -i qf-agent_${VER}_amd64.deb
# RHEL/Alma/Rocky
gh release download v$VER -R qzmi4meister/qf -p "qf-agent-${VER}-1.x86_64.rpm"
sudo rpm -i qf-agent-${VER}-1.x86_64.rpm
Пакет ставит: бинарь /usr/sbin/qf-agent, unit qf-agent.service, конфиг /etc/qf/agent.conf (config|noreplace — апгрейд не перетирает), создаёт /etc/qf/pki, systemctl enable qf-agent (не стартует — сначала настроить конфиг).
Шаг 3. Настроить /etc/qf/agent.conf
Минимум — две строки:
# CP endpoint (host или host:port). Без порта: gRPC :31443, enroll :31444, REST https://<host>.
QF_ENDPOINT=<cp>
# Токен из Шага 1
QF_ENROLL_TOKEN=<ENROLL_TOKEN>
Доверие к CA (выбрать один режим)
Агент проверяет серверный сертификат CP по CA. Приоритет: fingerprint > pinned PEM > REST-fetch (TOFU).
-
Fingerprint-пин (рекомендуется, airgap-safe). Забрать sha256 CA out-of-band и вписать:
curl -sk https://<cp>/pki/ca.sha256 # → hex-отпечатокQF_ENROLL_CA_FINGERPRINT=<sha256-hex>Агент тянет CA по REST и принимает только при совпадении отпечатка.
-
Пин PEM-файлом. Положить CA PEM на хост (
curl -sk https://<cp>/pki/ca.crt -o /etc/qf/ca.crt) и указать:QF_ENROLL_CA=/etc/qf/ca.crt -
Trust-on-first-use по REST. Только если у CP публично-доверенный TLS-серт:
QF_ENROLL_CA_FETCH=true
Опциональные ключи (дефолты разумные, добавлять по нужде)
# QF_IFACE=eth0 # по умолчанию — из default route
# QF_FAIL_CLOSED=false # true = fail-closed датапас
# 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/…) проходит без
# enforcement. =true → подчинить его default-action (дроп при DENY).
# ESP=IPsec / GRE=VPN / SCTP=телеком могут быть легитимны — сначала проверить
# QF_LOG_LEVEL=info
# QF_PKI_DIR=/etc/qf
Переменные окружения перекрывают значения из файла.
Шаг 4. Запустить и проверить
sudo systemctl start qf-agent
sudo systemctl status qf-agent
journalctl -u qf-agent -f # смотреть энролмент + attach датапаса
Признаки успеха в логе: подписан сертификат, установлен стрим, приложен bundle. На CP:
curl -sk -b cookie.txt "https://<cp>/hosts" | jq '.[] | {hostname,status,agent_version}'
Хост должен появиться со статусом active и текущей версией агента. В UI — на странице Hosts.
Устранение неполадок
| Симптом | Причина / действие |
|---|---|
token max uses reached | max_uses исчерпан — выпустить новый токен. |
Unimplemented ... AgentService при стриме | попал не в тот порт: стрим = :31443, enroll = :31444. Проверить QF_ENDPOINT. |
| CA verify / fingerprint mismatch | отпечаток не совпал с /pki/ca.sha256 — обновить QF_ENROLL_CA_FINGERPRINT. |
хост stale после старта | нет heartbeat >90с — проверить сетевую доступность :31443 и journalctl. |
| attach БПФ падает | ядру не хватает eBPF-фич (bpf_loop/ringbuf) или нет CAP_BPF/CAP_PERFMON — смотреть лог агента и capabilities unit'а. |
| статус не меняется | заведён single_host-токен на другой target_host_id — сверить hostname/host id. |
Снять хост
sudo systemctl disable --now qf-agent
sudo dpkg -r qf-agent # или: sudo rpm -e qf-agent
sudo rm -rf /etc/qf
На CP удалить запись хоста (UI Hosts → delete, или DELETE /hosts/{id}) — рвёт стрим и чистит per-host метрики.
См. также
deploy/ansible/roles/qf-agent— автоматизированный (парковый) энролмент.deploy/packaging/agent.conf— эталонный конфиг со всеми ключами.- Установка в закрытом контуре (air-gap) — install-airgapped.md.