Skip to content

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

  1. SSH to the host over its Tailscale address, as your own admin user.
  2. 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-recover first 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-invite refuses an operator who already has a passkey and points to passkey-recover. Both write History entries marked break-glass, with the --admin you gave.

  3. 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.

  4. Check History in the Console: the break-glass entries and the enrolments are all there.
  5. 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