Files
harmony/docs/guides/fleet-device-secrets.md
Jean-Gabriel Gill-Couture 5e3b0522e0
Some checks failed
Run Check Script / check (pull_request) Failing after 13s
docs(fleet): add operator and device guide
2026-07-27 10:34:31 -04:00

3.9 KiB

Fleet enrollment and device secrets

Enrollment gives a Linux device its Fleet identity and installs the agent. It does not, by itself, authorize group placement or make application secrets available.

Enroll a device

Use the example_fleet_device_enroll binary as the current enrollment driver. It runs locally or targets a device over SSH:

cargo run -p example_fleet_device_enroll -- \
  --target ssh://pi@10.0.0.42 \
  --issuer-url https://sso.example.com \
  --audience <fleet-project-id> \
  --nats-url wss://nats.example.com \
  --admin-oidc-client-id <numeric-client-id> \
  --agent-binary ./build/fleet-agent-aarch64 \
  --device-id warehouse-042 \
  --labels site=warehouse-east,arch=aarch64

The operator must authenticate to Zitadel through the browser flow or provide HARMONY_ZITADEL_ADMIN_TOKEN. Enrollment then finds or creates the device machine user, grants the requested project role, and mints a new JSON machine key. Zitadel does not return an existing private key, so a repeated enrollment mints another key.

FleetDeviceSetupScore installs Podman, the unprivileged fleet-agent user, the root updater, systemd units, the agent binary, and its configuration. When a machine key is supplied or minted, the Score writes it to /etc/fleet-agent/zitadel-key.json with mode 0640. The device cannot obtain that long-lived key from NATS or OpenBao automatically.

The device ID must be a valid Kubernetes RFC 1123 subdomain. Labels are self-reported placement data. Do not treat labels such as group=production as authorization.

Verify the installation on the device and control plane:

systemctl status fleet-agent harmony-fleet-updater
journalctl -u fleet-agent -u harmony-fleet-updater
kubectl -n <namespace> get devices.fleet.nationtech.io

Identity and tokens

The device stores only its Zitadel machine key. On each NATS connection, the agent signs a 60-second JWT assertion and exchanges it at Zitadel's token endpoint. Production requires the returned id_token: it is a JWT that the NATS callout and OpenBao can verify against Zitadel's JWKS. Zitadel's default access_token is opaque and is not suitable for this path.

The id_token is cached in memory and renewed before expiry. No refresh token is stored. Rotating a machine key requires installing the replacement on the device and deleting the old key in Zitadel.

Group placement and OpenBao

The intended authorization rule is:

admin-managed group membership AND self-reported target labels

allowedGroups names the authorized groups; targetSelector narrows placement within those groups. An empty group list authorizes no device.

The operator supports a Zitadel-backed group source and OpenBao policy grant synchronization, but the public production composition does not configure either one today. Without an explicit group source, deployments match no devices. Without OpenBao administrator configuration, changing allowedGroups does not create or attach OpenBao policies.

OpenBao use therefore requires an operator to configure all of the following:

  • the agent's OpenBao URL and secret prefix during device setup;
  • Zitadel group roles and device grants;
  • the operator group source;
  • OpenBao JWT auth, deployment policies, and group-policy attachments;
  • secret values under the configured deployment prefix.

Do not assume enrollment completed these steps. The E2E tests exercise explicit group and grant setup, not the default production composition.

Revocation

For a compromised device, remove or deactivate its Zitadel machine keys and revoke any active OpenBao identity or token. Rotate secrets the device could read. Group removal takes effect only after the device obtains a token carrying the new claims; policy detachment or secret rotation is the immediate control.

See ADR-025 for the intended group model. Treat its unwired portions as design until production composition adds and verifies the required configuration.