Console passkey recovery (break-glass)¶
How an operator gets back to two working passkeys when the Passkeys page cannot do it: one or both lost, and the other operator not available (or not enrolled) to let them enrol a replacement. Also how the very first passkey on a Console is enrolled, when nobody yet has one to vouch with. The design is ADR-0040 ยง4; built in Console phase 4, step 2.
flowchart LR
A["SSH to the host<br/>over the tailnet"] --> B["Run the command<br/>in the Console container"]
B --> C["recover: old passkeys revoked<br/>15 minutes to enrol two"]
C --> D["Sign in through Access<br/>Passkeys page, enrol two"]
D --> E["History shows<br/>break-glass"]
| When to use it | When not to |
|---|---|
First passkey on this Console (passkey-invite): nobody has a passkey yet, so nobody can vouch |
Anyone already has a passkey: they use Let them enrol on the Passkeys page instead |
Start again with two fresh passkeys (passkey-recover): one or both lost, and the other operator cannot vouch for a replacement in time. It works however many passkeys are left, none, one or two: SSH to the host is the authority |
You have only ever had one passkey and it still works: Add your second passkey. The other operator is available: their Let them enrol |
| You suspect the account itself, not just the passkeys, is compromised: revoke the operator's Access first and treat it as an incident |
What it never does: approve a job, skip the Access sign-in, or work from anywhere but the host. It lets one named operator enrol up to two passkeys in the next 15 minutes, and nothing else.
Before you start¶
- You can SSH to the host carrying the Console over the tailnet (ADR-0031), or reach it on the Vultr web console. This is the break-glass: a person on the host, not a button in the Console.
- The operator can still sign in through Cloudflare Access with Entra SSO and MFA. If they cannot, fix that first in Entra; this runbook does not replace it.
- Tell the other operator what you are about to do, by any channel, even if they cannot act.
Steps¶
- SSH to the host over its Tailscale address, as your own admin user.
-
Run the command inside the Console container, naming the operator and yourself:
# First passkey on this Console: sudo docker exec console python -m app.cli passkey-invite --operator <entra email> --admin "$USER@$(hostname)" # Start again with two fresh passkeys (revokes every one the operator has, however many): sudo docker exec console python -m app.cli passkey-recover --operator <entra email> --admin "$USER@$(hostname)"It prints when the window closes and where to go, for example
el@octopodia.com.au may enrol two passkeys until 03:15 UTC (15 minutes): sign in at https://console.octopodia.com.au/passkeys.passkey-recoverfirst revokes every passkey that operator has, even one that still works, and says how many; then it opens the same 15 minutes for two.passkey-inviterefuses an operator who already has a passkey and points topasskey-recover. Both write History entries marked break-glass, with the--adminyou gave. -
The operator opens the Passkeys page (
/passkeys) in their browser, signs in through Access (Entra SSO and MFA), names the passkey (YubiKey, Laptop) and enrols it, then the second, within the 15 minutes. - Check History in the Console: the break-glass entries and the enrolments are all there.
- Tell the other operator it is done. They review the History entries when next available.
If something goes wrong¶
| Symptom | Cause | Fix |
|---|---|---|
| You may not enrol a passkey now | More than 15 minutes passed, or two were enrolled already | Run the command again; each run opens a fresh 15 minutes for two |
| The page refuses the operator | Signed in to Access as someone else | Sign out of Access and use the operator's own Entra account |
| is not an operator of this Console | The email is not in console_operator_emails |
Check the spelling; the list is set in the inventory |
already has 1 passkey(s) from passkey-invite |
passkey-invite is only for an operator with none |
Run passkey-recover instead: it revokes the one left and opens the window for two |
| The browser says the passkey is for another site | Passkeys belong to the Console's host name (console_public_url); one enrolled on lite-dev's address does not work on another's |
Enrol again on this Console |
| No break-glass entry in History | The command ran against the wrong container or failed part-way | Do not continue: rerun it and check its output |