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

Ручной энролмент нового хоста

Как вручную подключить новый 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/UIhttps://<cp> (для получения CA и минта токена)
  • На хосте: 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 → TokensNew 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 reachedmax_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.