Rotate node token after initial enrollment

Task 9037 · Done · Coder · created · updated

The bootstrap body (Windows installer, cloud user-data, Ubuntu/macOS scripts) carries NODE_TOKEN in cleartext, and that token stays valid forever: `node_credentials.issue()` overwrites a single `node_token_hash`, and nothing rotates it after enrolment. Rotate it once the install's self-check passes, so the token that travelled in the fetched body or the user-data (IMDS-readable) is dead. Suggested design (Task 9004 recon): - Additive migration: `nodes.pending_node_token_hash` (nullable, partial unique index like `uq_nodes_node_token_hash`). - `POST /api/agent/token/rotate` (authenticated by the current token): mints a token, stores its digest as pending, returns the plaintext once. Both tokens resolve while pending is set. - `POST /api/agent/token/rotate/confirm` (authenticated by the NEW token): promotes pending to `node_token_hash`, clears pending. A lost response leaves the old token working. - `jaah auth rotate` in apps/jaahd/jaah_cli.py: call rotate, write node.env via `_write_node_env`, verify, confirm. The daemon already re-reads node.env on a 401 (`control_plane_api._refresh_node_token_from_env`). - Each installer runs it as its last step, after `Register-AndCheck`. Out of scope: an operator's console re-issue (unchanged); periodic rotation. Done means: a test proves a node's bootstrap token is refused after the install's final step, and the rotated token authenticates; a lost rotate response leaves the node authenticated; security review lane pass.

Outcome

Each installer now rotates the bootstrap node token after its self-check: pending token, confirm, `jaah auth rotate`, additive migration. Quick gate green, review lanes pass. PR #6662 queued; the lane runs the full gate. Deferred: draft #9098.

Pull request

Artifacts

Activity

  • Created
  • Run
  • Runner started
  • Runner ended (done)