Files
harmony/examples/fleet_rpi_setup/Cargo.toml
Jean-Gabriel Gill-Couture ab98cbabf9 feat(fleet): per-device Zitadel bootstrap in fleet_rpi_setup
The Pi onboarding flow can now mint a per-device Zitadel machine user
on the operator's machine and ship the resulting JWT key to the Pi —
the agent then authenticates to NATS via JWT-bearer instead of shared
nats_user/nats_pass.

`FleetDeviceSetupConfig.auth: FleetDeviceAuth` replaces the previous
flat `nats_user` / `nats_pass` fields. Two variants:

- TomlShared { nats_user, nats_pass } — legacy / dev fallback.
- ZitadelJwt { machine_key_json, oidc_issuer_url, audience, ... } —
  per-device JWT-bearer. The Score:
    * Drops `machine_key_json` to /etc/fleet-agent/zitadel-key.json
      (mode 0640, owner fleet-agent — matches the agent's secret-mount
      conventions).
    * Renders [credentials] type = "zitadel-jwt" pointing at that
      keyfile + the issuer + audience the agent's CredentialSource
      needs.
  A change to either the keyfile content or the TOML triggers an
  agent restart, same as binary / unit drift.

`fleet_rpi_setup --bootstrap-token <PAT>` activates the Zitadel path.
The bootstrap PAT is held in the CLI's memory only; it never lands
on the Pi. New flags: --zitadel-issuer-url, --zitadel-project-id,
--zitadel-device-role (default `device`), --danger-accept-invalid-certs.

`zitadel_bootstrap` is a slim ManagementAPI client that, idempotently
per device:
1. Find-or-create machine user `device-${device_id}`.
2. Find-or-skip a project role grant (defaults to `device`).
3. Always mint a fresh JSON key and return its content. (Zitadel
   doesn't expose the private half of an existing key, so reusing
   isn't possible — stale keys remain valid until expiry, which is
   fine because each setup run overwrites the on-device keyfile.)

Three new render_toml tests cover the zitadel-jwt path; eleven
existing agent tests still pass.

Out of scope, tracked: device-join-request + admin-approve flow that
would replace bootstrap-PAT entirely (closer to the OKD
node-approval pattern). Long-lived admin PAT is acceptable for the
demo per product call.
2026-05-03 15:22:13 -04:00

24 lines
580 B
TOML

[package]
name = "example_fleet_rpi_setup"
version.workspace = true
edition = "2024"
license.workspace = true
[[bin]]
name = "fleet_rpi_setup"
path = "src/main.rs"
[dependencies]
harmony = { path = "../../harmony" }
harmony_cli = { path = "../../harmony_cli" }
harmony_secret = { path = "../../harmony_secret" }
harmony_types = { path = "../../harmony_types" }
tokio.workspace = true
log.workspace = true
anyhow.workspace = true
clap.workspace = true
reqwest = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
base64 = "0.22"