Přeskočit na obsah

Fleet Agent — Developer Reference

Sencai fleet vrstva se skládá z lehké Go binárky, sencai-agent, která běží na spravovaných hostech (VM/bare-metal), a Go control-plane služby, agent-gateway, která agenty enrolluje a přemosťuje jejich WebSocket-over-TLS (WSS) spojení do Strapi. Samostatná, nesouvisející komponenta, k8s-agent, obsluhuje Kubernetes clustery a nejde přes agent-gateway — viz Kubernetes fleet agent níže.

ssh-proxy-service (port 3400) není součástí této architektury — pouze proxíruje browser-based SSH terminálové relace na cloudové instance a nemá žádný vztah k sencai-agent ani agent-gateway.

Spravovaný host / VM
├── sencai-agent (Go binary)
│ ├── mTLS klient → agent-gateway (WSS, port 4400)
│ ├── patch management (internal/patch)
│ ├── software inventář + EOL detekce (internal/inventory)
│ ├── CIS/Lynis hardening scan (internal/cis)
│ ├── log shipping do Loki (internal/logshipper)
│ ├── Prometheus Pushgateway export, opt-in (internal/pushgateway)
│ └── runbook executor (internal/runbook)
agent-gateway (Go, port 4400)
├── POST /enroll — bootstrap, bez auth, ověřuje token proti Strapi
├── GET /ws — mTLS, heartbeat + server-push kanál
├── GET /health — liveness probe
├── POST /bulk-action — X-Service-Secret, jen server-side volající
├── GET /fleet-status — X-Service-Secret, jen server-side volající
├── POST /dispatch-runbook — X-Service-Secret, jen server-side volající
└── POST /dispatch-exec — X-Service-Secret, jen server-side volající

Zdroj: sencai-agent/ (agent binárka), agent-gateway/ (control-plane služba). Oba jsou nezávislé Go repozitáře, oddělené od k8s-agent/.

  1. Pro záznam sencai-agent v Strapi se vygeneruje enrollment token (stav pending, org-scoped).
  2. Agent pošle POST <gateway>/enroll s {enrollment_token, hostname, os, arch, version}. Tento endpoint nevyžaduje žádný klientský certifikát — je to bootstrap krok.
  3. agent-gateway ověří token proti Strapi (odpovídající záznam sencai-agent musí mít status: pending), a poté sám vygeneruje keypair i certifikát: self-signed CA (4096-bit RSA, 10 let platnosti, uložená pod /etc/agent-gateway/) vystaví per-agent leaf certifikát (2048-bit RSA, 1 rok platnosti, CommonName nastavené na Strapi documentId agenta). Agent si negeneruje vlastní keypair.
  4. Gateway aktivuje agenta ve Strapi (status: active, uloží cert_fingerprint) a vrátí {agent_id, cert, key, ca_cert, capabilities}.
  5. Agent zapíše certifikát, klíč a CA bundle do /etc/sencai-agent/certs/ a svoji konfiguraci do /etc/sencai-agent/config.yaml.
  6. sencai-agent start otevře mTLS WSS spojení na /ws a zahájí heartbeat smyčku.
Terminál
sencai-agent enroll --gateway https://agent-gateway:4400 --token <token>
sencai-agent start
Terminál
curl -fsSL https://agent.sencai.space/install.sh \
| SENCAI_GATEWAY_URL=https://agent-gateway:4400 SENCAI_TOKEN=<token> bash

Podepsané .deb/.rpm balíčky (sestavené přes nfpm, GPG-podepsané) jsou také publikovány k tagged GitHub releasům sencai-agent; na Debian/Ubuntu hostech instalátor nejprve nabídne cestu přes balíček, než spadne zpět na raw binárku.

Jakmile je agent připojen přes mTLS GET /ws kanál, posílá heartbeat každých 30 sekund s cpu_percent, memory_used_bytes a disk_used_bytes. Zápisy heartbeatu do Strapi (last_heartbeat_at) jsou na straně gateway rate-limitované jako ochrana proti connection stormům.

{
"type": "heartbeat",
"agent_id": "...",
"timestamp": 1718900000,
"cpu_percent": 12.5,
"memory_used_bytes": 1073741824,
"disk_used_bytes": 21474836480
}

agent-gateway naslouchá na jediném portu, 4400 (HTTPS/WSS). Neexistuje žádný oddělený metrics port.

EndpointAutentizaceÚčel
POST /enrollžádná (bootstrap)Výměna enrollment tokenu za mTLS certifikát
GET /wsmTLS klientský certifikátHeartbeat kanál + server-push (capabilities_update, revoke, execute_runbook)
GET /healthžádnáLiveness probe
POST /bulk-actionX-Service-SecretJen server-side — hromadné fleet operace
GET /fleet-statusX-Service-SecretJen server-side — agregovaný stav fleetu
POST /dispatch-runbookX-Service-SecretJen server-side — spouští vykonání runbooku na agentovi
POST /dispatch-execX-Service-SecretJen server-side — spouští whitelistovanou exec akci

Endpointy chráněné X-Service-Secret volají pouze jiné backendové služby (nikdy přímo agenti nebo prohlížeče).

V celém fleet stacku není žádná integrace osquery. Inventář sbírá internal/inventory v sencai-agent, gatovaný capability inventory:scan:

  • Čte nainstalované balíčky přes dpkg-query (Debian/Ubuntu) nebo rpm -qa (RHEL/Fedora/CentOS).
  • Porovnává verze balíčků proti API endoflife.date, aby označil balíčky blížící se nebo již za koncem životnosti, klasifikované jako critical, warning nebo info urgence.
  • Běží na staggered rozvrhu: start 10 minut po bootu agenta, poté opakování každých 24 hodin.

internal/cis spouští:

Terminál
lynis audit system --quiet --no-log --no-colors --cronjob

To vyžaduje, aby binárka lynis byla na hostu již přítomná — pokud nainstalovaná není, scan goroutine tiše skončí bez chyby (žádný výsledek scanu se nevyprodukuje). Start je 15 minut po bootu agenta, poté se opakuje každých 24 hodin. Neexistuje žádný pevný denní rozvrh (např. 02:00) ani endpoint /api/fleet/nodes/:nodeId/compliance-report — výsledky se parsují ze stdout výstupu Lynis (hardening score, počet testů, warningy) a zpracovává je interně agent/gateway pipeline.

internal/patch detekuje package manager hosta (apt, dnf nebo zypper), vypíše dostupné aktualizace (Scan(), gatováno capability patch_management:scan) a umí je nainstalovat (Apply(), gatováno capability patch_management:apply). InMaintenanceWindow() parsuje zjednodušený cron-like výraz (hodina + den v týdnu), aby omezil Apply() na nakonfigurovaná maintenance okna. Scan startuje 5 minut po bootu a opakuje se každých 24 hodin.

internal/logshipper čte systemd journal (journalctl --output=short-iso) a logové soubory odpovídající konfigurovatelným glob patternům (výchozí: /var/log/syslog, /var/log/messages, /var/log/*.log). Před odesláním redaguje z každého řádku PII — emailové adresy, IPv4 adresy, MAC adresy, key/value páry vypadající jako credentials a čísla platebních karet — a poté posílá dávky na Loki push API (tagované X-Scope-OrgID pro tenant izolaci). Gatováno capabilities log_shipping:system / log_shipping:app, ve výchozím stavu vypnuté, dokud se v konfiguraci agenta nenastaví log_shipping_enabled a loki_url.

internal/pushgateway posílá tři gauge metriky (sencai_agent_cpu_percent, sencai_agent_memory_used_bytes, sencai_agent_disk_used_bytes) na Prometheus Pushgateway při každém heartbeatu. Je to zcela opt-in: jediný způsob, jak to zapnout, je nastavit proměnnou prostředí SENCAI_PUSHGATEWAY_URL. Bez ní se agent chová přesně jako předtím, bez jakéhokoliv dodatečného síťového provozu.

Vykonávání runbooků je pevný whitelist, ne obecný YAML/shell-command formát. Jediné povolené akce jsou:

  • restart_service
  • clear_disk_space
  • kill_process
  • run_approved_script — omezeno na skripty pod /etc/sencai-agent/scripts/

Dispatch flow:

  1. Volající (pouze server-side) pošle POST /dispatch-runbook na agent-gateway s hlavičkou X-Service-Secret.
  2. agent-gateway odešle WS zprávu cílovému agentovi: {type: "execute_runbook", execution_id, actions, dry_run}.
  3. Agent (internal/runbook) prosazuje rate limit 5 vykonání za hodinu na akci, sledovaný v paměti a resetovaný při restartu agenta.
  4. Výsledek se vrací jako WS zpráva {type: "runbook_result", ...} s výsledkem per-akce a celkovým success flagem.

Neexistuje žádný HTTP POST /runbook-result endpoint — výsledek putuje zpět přes stejné WS spojení.

Karanténa je iniciovaná serverem a probíhá na úrovni OS — agent se nekarantinuje sám. Když je karanténa spuštěna z Fleet dashboardu (podložena Strapi content-typy quarantine-policy / quarantine-event), spustí se handler internal/quarantine v agent-gateway, který na cílovém hostu (pouze Linux, vyžaduje passwordless sudo) provede:

Terminál
sudo iptables -I OUTPUT -j DROP -m comment --comment sencai-quarantine-<agentID>

Uvolnění karantény odstraní tagované pravidlo (nalezené podle komentáře sencai-quarantine-<agentID>). V tomto flow neexistuje žádný krok “Action Gateway approval” — samostatná Action Gateway služba neexistuje; viz dev/gateway.md pro to, co skutečně existuje.

sencai-agent update zkontroluje novou podepsanou verzi a aplikuje ji:

  • Stažené binárky a checksumy jsou podepsané cosign sign-blob (keyless, přes GitHub OIDC); agent před nahrazením sebe sama ověří SHA-256 checksum.
  • Výměna binárky je atomická (os.Rename), předchozí binárka zůstává jako .bak soubor pro rollback, pokud nová verze selže na health checku.
  • Rollout je rozdělen do ringů (10 % / 50 % / 100 %) přiřazovaných deterministicky podle agent_id agenta, takže vadný release zasáhne jen zlomek fleetu, než se rozšíří dál.
  • Binárky: cross-compilované pro linux/amd64 a linux/arm64, podepsané cosign sign-blob (keyless, na základě GitHub OIDC), spolu se SPDX SBOM vygenerovaným anchore/sbom-action.
  • Balíčky: .deb/.rpm balíčky jsou sestavené přes nfpm a GPG-podepsané na tagged releasech.
Terminál
cosign verify-blob \
--certificate sencai-agent-linux-amd64.pem \
--signature sencai-agent-linux-amd64.sig \
sencai-agent-linux-amd64

k8s-agent je samostatný, čistě deployment repozitář — neobsahuje žádný Go zdrojový kód. Obsahuje Helm chart (helm/sencai-agent/) a plochý manifest, oba nasazují stejný předpřipravený image, ghcr.io/sencai/sencai-agent:dev-latest, publikovaný z release pipeline repozitáře sencai-agent.

Nejde o „DaemonSet variantu stejné binárky” jdoucí přes agent-gateway — Kubernetes clustery používají zcela oddělenou cestu. In-cluster agent reportuje read-only cluster metriky (počty node/pod/namespace, verzi clusteru) každých 30 sekund přes:

POST /api/k8s-agents/<id>/heartbeat
X-K8s-Agent-Token: <token>

přímo do Strapi. S agent-gateway vůbec nekomunikuje. Konfigurace (sencai.apiUrl, sencai.registrationToken, sencai.organisationId) se dodává přes Helm values.yaml nebo ConfigMap/Secret plochého manifestu.

Agent se neobjevuje ve Fleet dashboardu po enrollmentu

Section titled “Agent se neobjevuje ve Fleet dashboardu po enrollmentu”

Ověřte, že je gateway URL dostupné z hostu a že záznam sencai-agent ve Strapi je stále pending (již použitý nebo expirovaný token vrátí z /enroll 401). Zkontrolujte logy agenta:

Terminál
journalctl -u sencai-agent -n 50

Selhání mTLS handshake / problémy s certifikátem

Section titled “Selhání mTLS handshake / problémy s certifikátem”

Znovu proveďte enrollment, abyste získali nový certifikát:

Terminál
sencai-agent enroll --gateway <url> --token <new-token>

CLI sencai-agent v současnosti u enroll vystavuje pouze --gateway a --token — žádný zdokumentovaný přepínač --force neexistuje; před spoléháním se na něj ověřte se service ownerem.

Binárka lynis musí být na hostu již nainstalovaná — internal/cis ji neinstaluje a scan tiše přeskočí, pokud chybí.