fleet-manager: checkpoint 7 — deployment (compose, PKI, deploy runbook, design-doc updates)

Complete the deployment story for the server-side control plane:

- docker-compose.yml: private InfluxDB (published to loopback only) + a
  host-networked fleet-manager (reaches the loopback enrol-broker on
  127.0.0.1:9443 and binds a routable gRPC agent listener, like the broker).
- .env.example: RP origin (diskless-server.bitasylum.org), Influx token/DB
  password, cert/PKI paths; the Influx token wires influxdb + manager with
  one value.
- server-pki.sh: issue the agent-plane server cert from the fleet CA (step)
  and stage the fleet root as the client-verify CA.
- DEPLOY.md: end-to-end runbook — three-cert PKI (fleet-CA agent cert, broker
  admin-client cert, Let's Encrypt DNS-01 UI cert), register-admin passkey
  bootstrap, compose up, hardware-free smoke test, backups.
- .dockerignore: keep host node_modules/dist/pki/secrets out of the build
  context so the embedded SPA is the one built in-image.
- .gitignore: never commit .env / issued PKI / data.

Design docs marked as-built: fleet-management-design.md as-built note
(resolved forks — gRPC bidi transport, InfluxDB metrics, SPA graphs, passkey
admin auth, broker fold-in "alongside"); tpm-enrolment-design.md (broker
fold-in + agent transport fork resolved); TODO.md #14 → partly built, with
the on-host agent + per-host boot-version assignment (#6) called out as the
remaining work.
This commit is contained in:
Audiobuzz
2026-07-04 08:44:31 +00:00
parent f62c668ce3
commit 2ef0c1747d
10 changed files with 523 additions and 10 deletions
+51 -2
View File
@@ -1,7 +1,56 @@
# Fleet management plane — design note
**Status:** DESIGN (not built). Captured 2026-06-28. Build phased — see "Phasing"
and `docs/TODO.md` #14.
**Status:** PARTLY BUILT. Captured 2026-06-28; **server-side control plane built
2026-07** (`serving/fleet-manager/`). The sections below are the original design;
the **as-built note** immediately following records what shipped and how the
open forks resolved. Build still phased — see "Phasing" and `docs/TODO.md` #14.
## As-built note (2026-07) — `serving/fleet-manager/`
The **server-side control plane + admin SPA is built** and supersedes several
"open questions" below. What shipped:
- **One service, two planes** (`serving/fleet-manager/`, Go): an **admin plane**
(HTTPS + SPA + JSON API) and an **agent plane** (gRPC/mTLS). Runs as its own
container on the mgmt host (`docker-compose.yml`: private InfluxDB +
host-networked manager). Deployment runbook: `serving/fleet-manager/DEPLOY.md`.
- **Resolved forks (were "open questions / decisions"):**
- *Steady-state transport (agent ↔ manager)* → **bespoke gRPC bidi stream**
(`proto/fleet_agent.proto`: `FleetAgent.Session`), host-dialed mTLS, health/
metrics up + desired-state down. Chosen over OTLP+Wazuh+control-stream: one
stream, no polling, no inbound host port. Enrolment stays plain HTTP/JSON.
- *Metrics store* → **InfluxDB 2.x** (was VictoriaMetrics/Prometheus). The
manager is the **only** reader/writer (host holds no Influx credential);
writes are line-protocol tagged with the mTLS-authenticated identity, reads
are a Flux query behind a **field whitelist + hex-serial guard**. Graphs are
rendered **in the SPA** (bespoke SVG), **not Grafana**.
- *Dashboards* → the **LYSANDRA restricted-violet SPA** (Svelte 5 + Tailwind 4,
embedded via `go:embed`): Monitor / Authorise / Lifecycle. Not Grafana/Wazuh
dashboards for this plane (Wazuh stays the audit/SIEM path).
- *Desired-state store* → a single **JSON file** (atomic writes) behind a
`Store` interface — not git/DB yet; a DB is a drop-in. Server-authoritative.
- *Admin/browser identity* → **WebAuthn/passkey (YubiKey)**, not browser mTLS:
usernameless discoverable login, UV required, server-side session + CSRF.
Bootstrap is a closed CLI (`register-admin`, one-time HMAC enrol link). The
UI cert is **Let's Encrypt DNS-01** (mgmt-net host, hot-reloaded). The
*manager→broker* hop stays mTLS (service-to-service, broker admin CA).
- *Authorise pillar* → the **enrol-broker folded in** "alongside": the broker
stays the state machine + step-ca signer; the manager drives its admin plane
over mTLS (Accept-gated JSON approve) and **births the inventory record** on
approve, recording the human passkey admin as approver.
- **Agent identity confirmed:** the agent reuses the **same TPM identity cert**
as 802.1X EAP-TLS / NVMe hostnqn — "one identity, three stages". Peer cert
serial is authoritative; an unenrolled serial is refused.
- **NOT yet built (recorded intent):** per-host **boot-version assignment**.
`desired.target_version` is stored + pushed, but changing what a host *boots*
needs dnsmasq cohort steering + multi-version UKI serving (`docs/TODO.md` #6).
Kiosk URL / reboot / drain reconcile via the agent today; the version pin is
labelled "recorded intent" in the Lifecycle UI. The **on-host agent** that
feeds this plane (extend `fleet-hostapi` with the gRPC report/reconcile client)
is likewise still to build.
Everything below is the original design context; treat the as-built note as
authoritative where they differ.
Centralised monitoring + maintenance of the diskless kiosk fleet: **health,
version, usage, and maintenance actions**. This note is the authoritative design;