Skip to content

feat(node): CA key rotation v1 - subordinate re-key#163

Merged
Bugs5382 merged 5 commits into
mainfrom
feat/subordinate-rekey
Jul 6, 2026
Merged

feat(node): CA key rotation v1 - subordinate re-key#163
Bugs5382 merged 5 commits into
mainfrom
feat/subordinate-rekey

Conversation

@Bugs5382

@Bugs5382 Bugs5382 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

What and why

Closes #162

CA key rotation v1 (subordinate re-key): an established intermediate/issuing node rotates its key without a chain break. ca rotate-key stages a new key + CSR in a rotation slot (node keeps signing with the current key); operator ferries the CSR to the parent (ca sign-subordinate); ca submit-rotation verifies the parent-signed chain roots to the pinned anchor AND binds to the staged rotation key, then atomically swaps key+identity (no awaiting-cert dip). Old-key-signed certs stay valid until expiry. Reuses the P3b enrollment machinery. Root cross-signed rollover deferred post-v1.0.0 (#161). Design: plan/2026-07-06-subordinate-rekey-design.md. api RPCs: api#40. CA-hardening (d) of 4 - closes the arc.

Five per-concern commits: grpc handlers/Rekeyer; store rotation slot; enroller AcceptRotation; run.go wiring; cryptosctl verbs.

Verification

  • Lint clean
  • Tests pass
  • Build succeeds

How this was verified

Unit: StageRotation refused with no identity / allowed+overwrite when established; CommitRotation swaps key+identity + clears the slot; AcceptRotation swaps on a good chain bound to the staged rotation key, rejects a wrong-anchor chain and a chain whose leaf carries a different key; handlers Unimplemented when nil + AuthorizeAdmin. Full gate: task ci (golangci-lint 0), CGO_ENABLED=1 go test ./internal/ceremony/ ./internal/tpm/, task license 202/202, staticcheck -checks all ./... clean, go mod tidy clean. Live re-key on ESXi folded into the hardening validation pass.

Bugs5382 added 5 commits July 6, 2026 12:08
Re-pin the api module to the revision that adds the BeginKeyRotation and
CompleteKeyRotation RPCs, and implement their handlers on the NodeService
server. A new Rekeyer interface (BeginRotation/CompleteRotation) backs them,
wired through ServerConfig.Rekeyer.

Both handlers are management-only: a nil Rekeyer (a root and the maintenance
servers) refuses with Unimplemented, and the caller is authorized against the
bootstrap admin trust before any key material is touched. CompleteKeyRotation
validates a non-empty chain before consulting the rekeyer; the security-critical
verification and the atomic swap live in the impl.
Add a rotation slot under /cryptos/identity/rotation/{csr,key-blob,key-public},
the established-node sibling of the subordinate staging slot, plus the store
methods that drive a CA key rotation:

- StageRotation stages the new CSR + key, guarded to the OPPOSITE state of
  StageSubordinate: it applies only when an identity already exists, so a root
  or a not-yet-established subordinate cannot begin a re-key. Overwrite is
  allowed so re-begin regenerates the key; a no-identity node returns
  ErrNoIdentity.
- RotationCSR and RotationKeyBlobs read the staged slot.
- CommitRotation atomically promotes the staged rotation key to the canonical
  CA-key location, swaps KeyIdentityChain to the new chain, mirrors the new leaf
  to KeyRootCert, and clears the slot, guarded on the slot existing (ErrNoRotation
  otherwise). The node stays established throughout; the old key is discarded.
Add SubordinateEnroller.AcceptRotation, the re-key sibling of AcceptCertificate.
It reuses the same trust logic (chain must root to the pinned parent anchor via
the existing pools + leaf.Verify) but binds the leaf public key to the STAGED
ROTATION key rather than the current identity key, then commits the atomic swap
via CommitRotation. It fails closed on any doubt: no rotation staged, a wrong
anchor, or a leaf carrying any other key are all rejected without touching the
store.
Add nodeRekeyer, the grpc.Rekeyer impl. BeginRotation generates a new CA key
through the same RootKeyBackend the ceremony provisions with, builds the
subordinate CSR (same subject as first-boot enrollment), and stages it in the
store's rotation slot while the node keeps serving with its current key; it
fails closed with FailedPrecondition on a node with no established identity.
CompleteRotation delegates the trust decision and the atomic swap to the
enroller's AcceptRotation.

Wire it in Boot alongside the subordinate enroller, on the local and mTLS
servers only: it is built only when a pinned parent anchor exists (a
subordinate), so a root and the maintenance servers leave it nil and the
rotation RPCs return Unimplemented there.
Add the operator ferry verbs for CA key rotation, mirroring the subordinate
enrollment verbs: rotate-key calls BeginKeyRotation and prints the new-key CSR
as PEM to hand to the parent's sign-subordinate; submit-rotation reads the
parent-signed chain and calls CompleteKeyRotation, printing the swapped
identity. Both register under the ca command.
@github-actions github-actions Bot added the enhancement New feature (feat). Minor version bump. label Jul 6, 2026
@Bugs5382

Bugs5382 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Closing summary: CA key rotation v1 - subordinate re-key. ca rotate-key stages a new key + CSR (node keeps signing with the current key); after the parent signs it, ca submit-rotation verifies the chain roots to the pinned parent + binds to the staged rotation key, then atomically swaps key+identity (no awaiting-cert dip). Old-key certs valid until expiry. Root cross-signed rollover deferred post-v1.0.0 (#161). Verified: rotation-slot guards, AcceptRotation swap + wrong-anchor/wrong-key rejection, handler gating; task ci (lint 0), CGO ceremony/tpm, license 202/202, staticcheck all clean. Closes the CA-hardening arc.

@Bugs5382 Bugs5382 merged commit 069a523 into main Jul 6, 2026
13 checks passed
@Bugs5382 Bugs5382 deleted the feat/subordinate-rekey branch July 6, 2026 16:14
@Bugs5382 Bugs5382 self-assigned this Jul 7, 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: CA key rotation v1 - subordinate re-key

1 participant