Files
diskless-workstation/files/agent/README.md
T
Audiobuzz 3284a2713b docs: reflect chrony/uptime metrics, agent-cert renewer, and the chrony-access findings
Recent work added telemetry fields and a cert path not yet in the docs:
- files/agent/README.md: add uptime to the health list; document that chrony is
  read from /var/log/chrony/tracking.log (not the command socket) and needs BOTH
  the chrony supplementary group (DAC) and the fleet_agent_chronylog SELinux allow
  (TE), plus a reachable lab NTP (123/udp firewall); note the chrony group on the
  unit row.
- serving/fleet-manager/README.md: add uptime + chrony detail to the health list;
  note the cert-auto agent-cert-renewer sidecar + GetCertificate hot-reload
  (certreload.go) in the bring-up and checkpoint 7.
- docs/TODO.md #14: probe list gains uptime + chrony-from-tracking.log; new dated
  block records uptime/reboot-pending, the 4-layer chrony-access chase (all
  silent-failing; inventory.json is the isolator), and the agent-plane cert
  auto-renewal, all bench-verified 2026-07-10.
- docs/fleet-management-design.md: health record gains uptime + chrony
  source/stratum/offset detail.
2026-07-10 22:45:00 +00:00

84 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# fleet-agent — on-host control-plane client (telemetry v1)
The workstation-side half of the fleet management plane. It dials the
**fleet-manager** agent plane, identifies with the host's **TPM**, and streams
health telemetry up. It is the client peer of the server hub in
`serving/fleet-manager/` (`agent.go` + `proto/fleet_agent.proto`).
Baked into the immutable verity image, so — like `fleet-hostapi` and `fleet-enrol`
— it is a **static, reproducible, `CGO_ENABLED=0`** Go binary built **out of the
build VM** by CI (deps proxy-fetched, `go.sum`-pinned) and file-provisioned in.
## What it does (v1 — telemetry only)
1. **Reuses the TPM identity for mTLS.** Loads the signing key at the persistent
handle (`0x81000100`) as a `crypto.Signer` (`tpmkey.LoadSigner` — the private
key never leaves the TPM) and the enrolment cert from NV (`0x01800100`), and
presents them as the mTLS client cert. The manager verifies the chain to the
fleet CA and derives the **cert serial = inventory key** — a host can only ever
report as itself (`agent.go`: `peerSerial`).
2. **Opens the long-lived `Session` bidi stream** (host dials out; no inbound host
port — works across the OPNsense L3 hop from the host VLAN to the mgmt plane).
3. **Sends `Hello`** (image version, kernel, kiosk URL, root hash, starship name —
the fleet-generic facts, pulled from `fleet-hostapi` on `127.0.0.1:9099`).
4. **Sends a `HealthSnapshot` every `FLEET_AGENT_INTERVAL`** — load/cpu/temp/mem/
tmpfs, **uptime**, nvme-root, 802.1X-macsec, selinux, kiosk-active — probed
read-only from `/proc`+`/sys`, plus **chrony** clock health (sync state +
source/stratum/offset relative to the NTP source; see note). Each probe
degrades gracefully; a failure never kills the stream. `uptime` lets the
manager derive **reboot-pending** (boot_time = last_seen − uptime vs the
requested `reboot_at`, all in the manager's clock frame).
5. **Receives `DesiredState`/`Command` and LOGS them.** This build does **not**
act on them. The reconcile actuators (apply kiosk URL, reboot, drain) are a
deliberate follow-up (see `docs/TODO.md` #14) — the receive path is wired now so
adding them needs no protocol change.
A dropped stream is retried with jittered backoff; the manager treats stream loss
as the host going **offline**.
> **Note — the chrony probe reads a file, not the command socket.** chronyd's unix
> command socket is owner-write-only and STIG sets `cmdport 0` (no UDP command
> port), so the sandboxed agent can't query it. Instead the image enables
> `log tracking`, and the agent reads the last line of
> `/var/log/chrony/tracking.log`. That read needs **two** access layers, both wired
> in: the **`chrony` supplementary group** (DAC — `/var/log/chrony` is `0750`) and
> a minimal **SELinux allow** for the `chronyd_var_log_t` label
> (`files/stig/fleet-agent-chronylog.te`). The lab NTP server must also be
> reachable (a `123/udp` host-firewall rule on the serving host) or the log only
> carries `unsynced` lines. See `docs/stig-deviations.md` #16.
## Prerequisite: the host must already be enrolled
The manager rejects the stream (`PermissionDenied`) until an inventory record
exists for the host's cert serial — that record is born when an admin **approves**
the host's TPM enrolment (`fleet-enrol` → enrol-broker → manager). Enrolment
precedes the agent; this ordering is expected.
## Config (`/etc/fleet-agent/agent.env`)
All values are **fleet-generic** (invariant #1). See the file for the full list:
`FLEET_AGENT_MANAGER` (manager `host:port`), `FLEET_AGENT_CA` (fleet root, reused
from `/etc/dot1x/radius-ca.pem`), `FLEET_AGENT_HANDLE` / `FLEET_AGENT_NVINDEX` (TPM
identity, mirror `rd.dot1x.*`), `FLEET_AGENT_INTERVAL`, `FLEET_AGENT_HOSTAPI`.
## Layout
| file | purpose |
|------|---------|
| `main.go` | config, connect loop (jittered backoff), `Session` send/recv |
| `identity.go` | TPM-backed mTLS client cert + fleet-CA-verifying `tls.Config` |
| `snapshot.go` | read-only `/proc`+`/sys` health probes + hostapi facts fetch |
| `tpmkey/` | **copy** of `files/enrol/tpmkey` + read-only `LoadSigner` (see its header) |
| `proto/` | **copy** of the generated `fleet.agent.v1` package (one `.proto` contract, two peers) |
| `fleet-agent.service` | hardened unit (`tss` group for the TPM, `chrony` group to read chrony's tracking.log, INET egress) |
## Build / test
```bash
cd files/agent
CGO_ENABLED=0 go build -trimpath -buildvcs=false -ldflags '-s -w -buildid=' -o bin/fleet-agent .
go test ./...
# TPM handshake end-to-end (go-tpm software simulator, CGO — isolated module):
( cd simtest && CGO_ENABLED=1 go test ./... )
```