Skip to content

feat(adopt): confirm the installed node's fingerprint before trusting it - #150

Merged
Bugs5382 merged 1 commit into
mainfrom
feat/144-adopt-fingerprint-confirm
Oct 1, 2026
Merged

Bugs5382 merged 1 commit into
mainfrom
feat/144-adopt-fingerprint-confirm

Conversation

@Bugs5382

@Bugs5382 Bugs5382 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

What and why

When the manager adopts a node, the node installs, reboots and comes back with a new management certificate. Nothing links that certificate to the maintenance certificate the operator confirmed with PreviewAdoption. Until now the manager pinned it on first use and only showed its fingerprint in the progress. Now the adoption pauses until an admin confirms the installed node's fingerprint against the Mgmt SHA-256 line on the node's console. This uses the contract from CryptOS-PKI/api#133.

  • Pause: once the node answers in running mode, the manager captures the certificate it presents, without pinning it. It streams awaiting-fingerprint-confirmation with presented_cert_sha256. Every streamed message carries adoption_id.
  • Confirm: ConfirmAdoptionFingerprint{adoption_id, cert_sha256} resumes the adoption when the fingerprint equals the presented one (case, colons and whitespace ignored). Only then is that certificate written as the node's server.crt and the node dialed against it. After the ceremony a root's chain is recorded as before, over that verified connection.
  • Mismatch: the call and the adoption fail with InvalidArgument. Nothing is pinned, no CA chain is recorded, the node isn't registered and there is no node-adopted event.
  • No confirmation within 15 minutes (DeadlineExceeded), or the client going away (the wizard's cancel): the adoption stops with nothing recorded.
  • Unknown or already-used adoption_id: NotFound.
  • Audit: node-adoption-fingerprint-confirmed (ok) and node-adoption-fingerprint-rejected (denied), with the operator who sent it, the node, the endpoint and both fingerprints.
  • Admin-only. It's excluded from the MCP catalog: a person confirms a node's identity from its console.
  • Retries: a retried adoption confirms again. A root that already ran its ceremony presents its CA-signed certificate by then (feat(mgmt): sign the management certificate with the node's CA cryptos-node#318), and that is the one confirmed.
  • The admin credential minted for the adoption stays after a failed confirm, as it does for other failures, so adopting again resumes.

Replicas: the waiting adoption is held in memory by the replica running the stream, like the protocol-switch reboot tracker. With replicaCount above 1, a confirmation that lands on another replica gets NotFound and has to be sent again. The chart defaults to one replica. docs/node-trust.md states this in a warning.

Existing tests changed: TestRunAdoption_Root_PinsRunningCertAndRecordsCAChain and TestRunAdoption_Subordinate_PinsRunningCertWithoutChain covered the trust-on-first-use pin that this change removes. With it they block waiting for a confirmation, so they now confirm through a small confirmingSink helper and still check the same pin and chain results. TestCatalog_MatchesTheFleetServiceContract failed on the new RPC until the catalog row was added.

Closes #144
Refs #135

Merge order: this re-pins api for the manager to api main (29b56ec), which has CryptOS-PKI/api#133 (this contract) and api#135 (Go 1.26.8). It sits on manager main after #149 and #152. Merge this together with the docs companion CryptOS-PKI/docs#71 and with CryptOS-PKI/cryptos-web#143 (the wizard's confirm step). Without web#143, the current wizard stalls on the new phase until the 15-minute wait expires.

Docs: docs/node-trust.md ("What adoption records"), docs/mcp.md and the README. Docs site companion: CryptOS-PKI/docs#71.

Verification

  • Lint clean
  • Tests pass
  • Build succeeds
  • Documentation updated (if behavior or API changed)

How this was verified

  • The new tests in internal/fleet/adopt_confirm_test.go were written first. They cover:
    • the pause with no pin, no dial and no registration until the confirm, then the pin equal to the confirmed certificate, established, and both audit events;
    • a mismatch: InvalidArgument, error phase, no server.crt, no ca.crt, no node, no dial, the rejected audit event, and NotFound on a second confirm;
    • the confirm timeout and the client going away, each with nothing recorded;
    • the RPC refusals: non-admin, empty fields, unknown ID;
    • adoption_id on every streamed message, with presented_cert_sha256 only on the paused phase.
  • The two existing capture tests hung on the pause (the 120 s test timeout) before they were updated to confirm.
  • After a rebase onto main (cd3a52f), task ci (gofmt, vet, golangci-lint with 0 issues, go test ./...) passes with a throwaway Postgres 18 and MANAGER_TEST_DATABASE_URL set, so the store integration tests ran too. task license passes. go test -race -count=3 on the adoption tests passed before the rebase.

After a node installs and reboots during adoption it presents a new
management certificate that nothing links to the maintenance certificate
the operator confirmed. Adoption no longer pins it on first use. It
captures the certificate, streams the awaiting-fingerprint-confirmation
phase with its SHA-256 and the adoption ID, and waits for
ConfirmAdoptionFingerprint.

A matching fingerprint (case, colons and spaces ignored) resumes the
adoption, and only then is the certificate pinned and the node dialed
against it. A mismatch, no confirmation within 15 minutes, or the client
going away fails the adoption with nothing pinned, recorded or
registered. Confirmations and refusals are audited with the operator.

The new RPC is excluded from the MCP catalog.

Closes #144
Refs #135

Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
@Bugs5382
Bugs5382 force-pushed the feat/144-adopt-fingerprint-confirm branch from 377d8d1 to ef26df3 Compare October 1, 2026 13:31
@Bugs5382
Bugs5382 marked this pull request as ready for review October 1, 2026 13:31
@github-actions github-actions Bot added the enhancement New feature (feat). Minor version bump. label Oct 1, 2026
@Bugs5382

Bugs5382 commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Merging for the v0.1.0 test run: CI green and mergeable at merge time.

@Bugs5382
Bugs5382 merged commit 296a2ba into main Oct 1, 2026
23 of 26 checks passed
@Bugs5382
Bugs5382 deleted the feat/144-adopt-fingerprint-confirm branch October 1, 2026 13:36
@Bugs5382 Bugs5382 added this to the v0.1.0 milestone Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature (feat). Minor version bump.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(adopt): confirm the installed node's fingerprint before trusting it

1 participant