Skip to content

feat(agent): add fog-agent enrollment (POST /agent/v1/enroll, tokens, approvals) - #186

Merged
fog-workflows[bot] merged 1 commit into
masterfrom
feat/agent-enroll
Nov 10, 2026
Merged

fog-workflows[bot] merged 1 commit into
masterfrom
feat/agent-enroll

Conversation

@mastacontrola

Copy link
Copy Markdown
Member

What

fog-agent enrolls with fog-node exactly as with FOG 1.6, so one agent binary serves both servers. Builds on the agent PKI (#184) and the root guard (#185).

  • POST /agent/v1/enroll (public): the 1.6 state machine. 200 issued, 202 pending (retry_after 300), 403 denied, 426 protocol, 400 csr, 503 signing; reasons unknown-host, known-host-no-agent, rebind, identity-conflict, reissue, no-mac. Host resolution: SMBIOS (guid/serial/asset, 1.6 placeholder list), then MAC. Pending hosts are created as 1.6 creates them.
  • Auto-approval with an enroll token or in the deploy window. Tokens are stored only as a sha256 hash; a use is reserved atomically and given back when signing fails.
  • Admin JSON routes: GET /api/v1/agent/enrollments, POST /api/v1/agent/enrollment/:id/approve|deny, GET /api/v1/agent/tokens, POST /api/v1/agent/token, DELETE /api/v1/agent/token/:id. Each checks its host permission and carries apiCsrfGuard.
  • Host gets server-owned agent fields (agentFingerprint, agentNotAfter, agentVersion, agentCheckin, agentId); create, update, save and the edit page cannot write them. Indexes are made at bootstrap (no migration).
  • Hardening beyond 1.6 (no wire change): the public route caps csr_pem at 16384 characters and stores only the contract's identity keys, each capped; text fields are cut to the 1.6 column widths.
  • An approved certificate is handed over once even if its host was deleted since, as 1.6 does; the agent then gets 401 later and re-enrolls.

Real fog-agent, end to end

fog-agent built from fog-agent at dbdf5bd, this branch lifted on :1340 with its own database and pki:

$ fog-agent enroll --server http://127.0.0.1:1340 --ca <pki>/root.pem --dir <state> --once
generated a new key for this machine's identity
pending approval on the server (unknown-host); waiting
{"status":"pending","reason":"unknown-host","retry_after":300}

$ POST /api/v1/agent/enrollment/<id>/approve   (signed in as Administrator, X-Requested-With)
{"id":"…","hostID":"…","state":"issued"}  http=200

$ fog-agent enroll --server http://127.0.0.1:1340 --ca <pki>/root.pem --dir <state> --once
enrolled as host 44, certificate valid until 2027-11-10 13:16:21

$ openssl verify -purpose sslclient -CAfile root.pem -untrusted agent-ca.pem cert.pem
cert.pem: OK      (subject CN=fog-agent host 44, O=FOG Project, OU=FOG Agent; CA:FALSE; clientAuth)

Verification

  • npm test: 326 passing (67 new). npm run test:install: all checks pass.
  • Gates: with the six new controllers stubbed to 404, 56 of the new tests fail; with master's create/update/save/edit, the server-owned field tests fail; without the token give-back, the 503 test fails; with the old deleted-host line, its test fails.

Reviews (opus): spec clean after round 2 (fixed the deleted-host collection path); standards findings fixed (size limits, token hash in one place, shared leaf parsing, named caps).

🤖 Generated with Claude Code

… approvals)

A fog-agent enrolls with fog-node exactly as with FOG 1.6: it sends a CSR
and its identity, waits while pending, and collects a certificate when an
admin approves it, or at once with a valid enroll token or in the deploy
window. Statuses, JSON keys and reasons match 1.6, so one agent binary
serves both servers.

- POST /agent/v1/enroll (public): the 1.6 state machine (200 issued,
  202 pending, 403 denied, 426 protocol, 400 csr, 503 signing); host
  resolution by SMBIOS then MAC; pending hosts as 1.6 creates them.
- Enroll tokens: stored as sha256 only; a use is reserved atomically and
  given back if signing fails.
- Admin JSON routes: list enrollments, approve/deny, list/mint/revoke
  tokens, each with its host permission and apiCsrfGuard.
- Host gets server-owned agent fields (agentFingerprint, agentNotAfter,
  agentVersion, agentCheckin, agentId); create, update, save and the edit
  page cannot write them. Indexes are made at bootstrap.
- The public route caps the CSR (16384 chars) and stores only the
  contract's identity keys, each capped.
- An approved certificate is handed over once even if its host was
  deleted since, as 1.6 does; the agent then re-enrolls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fog-workflows
fog-workflows Bot enabled auto-merge November 10, 2026 13:28
@fog-workflows
fog-workflows Bot added this pull request to the merge queue Nov 10, 2026
Merged via the queue into master with commit bc67671 Nov 10, 2026
6 checks passed
@fog-workflows
fog-workflows Bot deleted the feat/agent-enroll branch November 10, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant