Rotating a host's AK¶
A host's attestation key (AK) is derived from a fixed TPM template, so it is normally stable across reboots for the life of the host. But a TPM owner-clear, a firmware change that re-derives the AK, or a precautionary rotation will change it — and once the AK changes, every attestation fails as UnenrolledOrMismatchedAk. rotate-ak re-binds the host to its new AK without a full re-enrollment, preserving the baseline you have painstakingly reviewed, the TLS pin, and the PCR replay state.
The Endorsement Key is what makes this safe: it does not change, so it is the continuity anchor that proves the new AK still lives in the same TPM you already trust.
op rotate-ak is an operator action, authorized by the daemon
Like enrollment, re-binding a host's attestation key is gated by RBAC, not root. It goes to etminan-verifierd, which identifies you by kernel UID, requires the operator role, signs the rotation record itself, and writes the action to the audit log. The operator-key --key/--reason signing path was removed in 0.10 — there is no key file and no sudo. See RBAC.
When to rotate vs. re-enroll¶
| Situation | Action |
|---|---|
| TPM owner-cleared; firmware update re-derived the AK; precautionary rotation | rotate-ak — same TPM, new AK, baseline preserved |
| The host's EK changed (TPM physically replaced, motherboard swap with a new TPM) | Full re-enroll — a different EK means a different TPM, no cryptographic continuity |
Only the agent's TLS keypair was regenerated (etminan-agent keygen-tls) |
rotate-tls — the transport cert, not the AK |
The dividing line is the EK. A different EK has no cryptographic continuity with the trust you established at enrollment, so it is a deliberate, human-confirmed re-enroll by design — rotate-ak refuses it rather than silently accepting a new TPM.
The command¶
Run it as yourself, not as root — the daemon authorizes you by your kernel UID and holds the signing key.
| Argument | Required | Purpose |
|---|---|---|
<host> |
yes | Positional. The enrolled host whose AK changed. |
--reason <text> |
yes | Mandatory justification, folded into the signed rotation record. |
--ek-roots <dir> |
no | Also re-verify the manufacturer EK-cert chain, same as op enroll --ek-roots. |
No unsigned, unreasoned rotation is possible
op rotate-ak refuses without --reason, and the daemon refuses a caller whose UID holds no role permitting the action — the same bar as enrollment. There is no code path that performs a rotation without a signature and a reason: the daemon signs every applied rotation, and this is a hard invariant across every rotation kind (op rotate-ak, op rotate-tls).
How it works¶
rotate-ak re-runs the same EK credential-activation ceremony as enrollment, but against the already-pinned EK. The EK fingerprint must still match; the new AK must prove it resides in that same TPM. Only then is the pinned AK replaced.
sequenceDiagram
autonumber
participant Op as Operator
participant V as etminan-verifier
participant A as etminan-agent
participant TPM as Host TPM 2.0
Op->>V: op rotate-ak web-01 --reason ...
Note over V: etminan-verifierd — peer-UID identity, operator role, audit log
V->>A: Quote request (nonce) over pinned mTLS
A->>TPM: TPM2_Quote (new AK, fixed template)
TPM-->>A: signed attest + signature
A-->>V: quote + NEW AK public
V->>A: EK request
A->>TPM: read EK public
A-->>V: EK public
Note over V: EK fingerprint == the one pinned at enrollment?
alt EK differs
V-->>Op: REFUSE — different TPM, do a full re-enroll
else EK matches
Note over V: MakeCredential → challenge sealed to (EK, new AK Name)
V->>A: ActivateCredential(blob, secret)
A->>TPM: TPM2_ActivateCredential (needs EK + new AK in this TPM)
TPM-->>A: recovered secret
A-->>V: recovered secret
Note over V: recovered == challenge? else REFUSE (software AK)
V->>V: replace AK public + AK fingerprint<br/>PRESERVE baseline, TLS pin, cumulative PCR10, log offset
V->>V: sign canonical_ak_rotation_payload<br/>append action=ak_rotated to audit_log
V-->>Op: prints old + new AK fingerprint → confirm out of band
end
What changes and what is preserved¶
On success, rotate-ak replaces the host's ak_public_marshaled_hex and ak_fingerprint_sha256_hex, and advances the cumulative PCR 10 and log offset in lockstep with the fresh quote (exactly as a normal cycle does). It preserves:
- the approved baseline — you do not re-review anything,
- the TLS pin,
- the pinned EK (the continuity anchor, unchanged by definition).
The rotation is signed with canonical_ak_rotation_payload(host_id, old_fingerprint, new_fingerprint, reason) and appended to the hash-chained audit log as action="ak_rotated", recording both the old and new fingerprints, the signer, and the reason — so the full history of the rotation is immutable and independently verifiable by baseline verify-signatures.
What you see¶
Rotated attestation key for host 'web-01':
Old AK fingerprint (sha256): a1b2c3...
New AK fingerprint (sha256): 9f8e7d...
Proven to reside in the same TPM as the pinned EK (<ek-fingerprint>). TLS pin,
baseline, and PCR replay state are unchanged.
Signed by operator key <op-pubkey>. Reason: TPM cleared during motherboard swap
Confirm the new AK fingerprint out of band, the same discipline as enrollment — the residency proof establishes it's the same genuine TPM; the human step confirms nothing unexpected happened to the host in between.
Rotation is not a four-eyes action on this line
Dual control accepts exactly two action tokens on 0.10 — baseline-approve and exclude-create. rotate-ak is not one of them, and putting it in ETMINAN_DUAL_CONTROL_ACTIONS is a hard startup error rather than a silently-ignored setting, so a rotation always applies on the initiating operator's single authorization. See Dual control.
Related¶
- Enrolling a host — the credential-activation ceremony
rotate-akre-runs, and the EK that anchors it. - Mutual TLS —
op rotate-tls, the transport-cert analogue. - Troubleshooting — diagnosing an
AK fingerprint mismatchfinding before deciding to rotate.