Placeholders 🔥 Triage Pre-flight Setup ✅ Core Check Lock + udev ✅ Matrix Diagnostics 🔁 Post-Lab Reset 🚨 Rollback
Lab Reference

FIDO2 Lab — Reference

Companion reference to fido-lab.html · Module 1 · Debian 13.5 / XFCE + LightDM · Diagnostics, triage, and rollback at a glance.

Placeholders — substitute before every command

PlaceholderMeaning
FIDO_USERLab account that will use the FIDO token
BREAKGLASS_USERSeparate sudo-capable, password-only account — never add to fido-login
/dev/hidraw1Token device path from fido2-token -L. The number may differ on your system (hidraw0, hidraw2, etc.) depending on other HID devices present — always confirm with fido2-token -L rather than assuming hidraw1.
096e / 0858Token vendor/model IDs from udevadm monitor — check per token
Lab SequenceOverview

Do not reorder. Do not skip §11 test service or §14 Core Checkpoint.

Pre-flight Packages PIN Pre-PAM test Group Mapping file PAM include ✅ Test svc §11 LightDM sudo ✅ Core check §14 [su opt] XFCE lock udev removal ✅ Extended matrix §18 passwd -l §19 [Keyring opt]
🔥 Triage — Student Says…Jump first
"Token doesn't show up / fido2-token -L returns nothing"
VirtualBox USB filter not set.
Fix: Power off VM → Settings → USB → Add USB Filter → select token → OK → start VM → reinsert token → rerun fido2-token -L.
Also try: wrong USB controller type (2.0 vs 3.0) — some tokens enumerate differently by host port.
"pamtester says: Authentication service cannot retrieve authentication info"
Check in order:
  1. User not in fido-login group → id FIDO_USER
  2. Group added but user hasn't logged out and back in (group change not effective in running sessions)
  3. Mapping file missing or wrong permissions:
sudo ls -l /etc/security/u2f_keys        # expect: -rw-r----- root fido-login
sudo grep "^FIDO_USER:" /etc/security/u2f_keys
"FIDO_USER still gets a password prompt"
1. id FIDO_USER — does fido-login appear?
2. Did they log out and back in after usermod?
3. Is @include fido2-local-auth in the right PAM file?
sudo grep fido2-local-auth /etc/pam.d/lightdm
sudo cat /etc/pam.d/fido2-local-auth
"Greeter shows blank lines, nothing happens"
Known GTK greeter quirk. Tell student to press Enter once. The FIDO PIN prompt follows. Documented in §12 of the lab — students still hit it every time.
"xflock4 does nothing / fails from terminal"
xflock4 needs graphical session env vars. Only works from the original XFCE terminal session — not from a su - shell or SSH.
echo "$DISPLAY"
echo "$XDG_RUNTIME_DIR"
echo "$DBUS_SESSION_BUS_ADDRESS"
If any are empty: student is in a nested shell. Exit and retry from the XFCE terminal.
"sudo stopped working after an apt upgrade"
The sudo package overwrote /etc/pam.d/sudo, removing the FIDO include.
— If password is not yet locked (§19 not done): FIDO_USER can sudo with password temporarily.
— If password is already locked: use BREAKGLASS_USER first, then:
sudo /usr/local/sbin/fido2-sudo-pam-guard
sudo grep fido2-local-auth /etc/pam.d/sudo
sudo -k && sudo -v
"Chrome / Chromium is asking for the keyring password"
Expected behavior. FIDO login doesn't unlock the GNOME Keyring automatically. Point student to §20. Options: enter the keyring password when prompted, or blank it in Seahorse. Blanking is optional — explain the tradeoff first.
"Removing the token doesn't switch to the greeter"
Check in order:
  1. Did the udev rule fire? → sudo journalctl -b --since "3 minutes ago" | grep -i fido-udev
  2. Are vendor/model IDs correct for this token? → run udevadm monitor again
  3. Does the helper script exist and is it executable? → ls -l /usr/local/sbin/fido-remove-switch-to-greeter
  4. Test helper manually: sudo /usr/local/sbin/fido-remove-switch-to-greeter
sudo journalctl -b --since "5 minutes ago" | \
  grep -Ei 'fido-switch-greeter|dm-tool|udev'
"fido2-cred fails / pre-PAM test fails"
FIDO_ERR_PIN_INVALID = wrong PIN.
FIDO_ERR_PIN_REQUIRED = missing -t pin=true flag — rerun with it.
No token found = back to USB passthrough check.
Check PIN state: fido2-token -I /dev/hidraw1 (look for PIN management = true).
"Locked out of sudo / can't run any privileged commands"
Use BREAKGLASS_USER — this is what it's for.
If breakglass also fails: boot recovery mode or live USB, mount the system, and remove fido2-local-auth includes from PAM files. See Emergency Rollback.
"pam_u2f: user not found in mapping file"
Username in mapping file must match exactly (case-sensitive).
sudo grep "^FIDO_USER:" /etc/security/u2f_keys
sudo cat /etc/security/u2f_keys   # check whole file
id FIDO_USER                       # verify exact username
Pre-flight & Token Management§4–6

Token visibility

lsusb
fido2-token -L     # expect: /dev/hidraw1: VENDOR MODEL FIDO+U2F

Token management

fido2-token -L                    # list tokens; note /dev/hidraw1
fido2-token -I /dev/hidraw1       # inspect: firmware, PIN state, retries
fido2-token -S /dev/hidraw1       # set a new PIN
fido2-token -C /dev/hidraw1       # change existing PIN

New-token acceptance test — fido-token-test.sh

Interactive utility to vet a new FIDO2 key before it enters the lab: detect, dump identity, live touch/crypto round-trip, set PIN, and factory-reset. Reset is fail-closed — it refuses any token it can't uniquely identify and any token on the protected list. Register your own real auth key first (menu option [7]) so the tool can never wipe it.

Save it, then on the lab box: chmod +x fido-token-test.sh && ./fido-token-test.sh. Non-interactive sweep: ./fido-token-test.sh --batch --report. Full option list: ./fido-token-test.sh --help. Requires fido2-tools (optionally ykman, udevadm).

⬇ Download fido-token-test.sh

Install packages

sudo apt update
sudo apt install libpam-u2f pamu2fcfg fido2-tools pamtester

Pre-PAM token test (§7) — do this before touching PAM

rm -rf /tmp/fido2-login-test && mkdir -p /tmp/fido2-login-test && cd /tmp/fido2-login-test

echo "debian local login test challenge" | openssl sha256 -binary | base64 > cred_param
echo "localhost" >> cred_param
echo FIDO_USER >> cred_param
dd if=/dev/urandom bs=32 count=1 status=none | base64 >> cred_param

fido2-cred -M -t pin=true -i cred_param /dev/hidraw1 | fido2-cred -V -o cred

ls -l cred
cd && rm -rf /tmp/fido2-login-test

Expected: PIN prompt → LED flash → touch → cred file created. FIDO_ERR_PIN_INVALID = wrong PIN. FIDO_ERR_PIN_REQUIRED = missing -t pin=true.

Setup Sequence — Group → Mapping → PAM → Test Service§8–11
Instructor note: students who forget to log out after usermod will get a password prompt in pamtester and assume PAM is broken. Check group membership with id FIDO_USER first.

Create fido-login group (§8)

sudo groupadd --force fido-login
sudo usermod -aG fido-login FIDO_USER
id FIDO_USER              # fido-login must appear
getent group fido-login   # FIDO_USER must be listed

Central PAM mapping file (§9)

# Enroll primary token
pamu2fcfg -N -u FIDO_USER > /tmp/u2f_keys-FIDO_USER
cat /tmp/u2f_keys-FIDO_USER    # line must start with FIDO_USER:

sudo install -o root -g fido-login -m 0640 /tmp/u2f_keys-FIDO_USER /etc/security/u2f_keys
rm -f /tmp/u2f_keys-FIDO_USER
sudo python3 -c 'from pathlib import Path; p=Path("/etc/security/u2f_keys"); p.write_text(p.read_text().rstrip()+"\n")'
sudo ls -l /etc/security/u2f_keys            # expect: -rw-r----- root fido-login
sudo grep "^FIDO_USER:" /etc/security/u2f_keys

Backup token enrollment (§9)

sudo cp /etc/security/u2f_keys /tmp/u2f_keys-FIDO_USER.new
sudo chown FIDO_USER:fido-login /tmp/u2f_keys-FIDO_USER.new
chmod 0640 /tmp/u2f_keys-FIDO_USER.new
printf ':' >> /tmp/u2f_keys-FIDO_USER.new
pamu2fcfg -N -n >> /tmp/u2f_keys-FIDO_USER.new
sudo install -o root -g fido-login -m 0640 /tmp/u2f_keys-FIDO_USER.new /etc/security/u2f_keys
rm -f /tmp/u2f_keys-FIDO_USER.new
sudo python3 -c 'from pathlib import Path; p=Path("/etc/security/u2f_keys"); p.write_text(p.read_text().rstrip()+"\n")'

Shared PAM include file (§10)

sudo test -e /etc/pam.d/fido2-local-auth && \
  sudo cp -a /etc/pam.d/fido2-local-auth /etc/pam.d/fido2-local-auth.bak || true

sudo tee /etc/pam.d/fido2-local-auth >/dev/null <<'EOF'
{{EMBED:config/fido2-local-auth}}
EOF

Test service + verify (§11) — do before editing any real PAM service

sudo test -e /etc/pam.d/fido2-test && \
  sudo cp -a /etc/pam.d/fido2-test /etc/pam.d/fido2-test.bak || true

sudo tee /etc/pam.d/fido2-test >/dev/null <<'EOF'
{{EMBED:config/fido2-test}}
EOF

sudo pamtester fido2-test FIDO_USER authenticate       # FIDO PIN + touch
sudo pamtester fido2-test BREAKGLASS_USER authenticate # password only

If pamtester fails → see Triage: "cannot retrieve authentication info".

LightDM + sudo Configuration§12–13

LightDM (§12)

sudo cp -a /etc/pam.d/lightdm /etc/pam.d/lightdm.bak
sudo editor /etc/pam.d/lightdm
# Insert @include fido2-local-auth immediately above @include common-auth

Optional — username-first greeter

sudo mkdir -p /etc/lightdm/lightdm.conf.d
sudo tee /etc/lightdm/lightdm.conf.d/50-manual-login.conf >/dev/null <<'EOF'
{{EMBED:config/50-manual-login.conf}}
EOF

Pre-logout test

sudo pamtester lightdm FIDO_USER authenticate       # FIDO PIN + touch
sudo pamtester lightdm BREAKGLASS_USER authenticate # password
GTK greeter quirk: blank lines before the PIN prompt are expected. Press Enter once — the FIDO prompt follows.

sudo (§13)

sudo cp -a /etc/pam.d/sudo /etc/pam.d/sudo.bak
sudo editor /etc/pam.d/sudo
# Insert @include fido2-local-auth immediately above @include common-auth

sudo -k && sudo -v   # test from FIDO_USER session

sudo PAM guard — install before §19

Critical: if §19 (passwd -l) is done and apt later overwrites /etc/pam.d/sudo, FIDO_USER loses all sudo access. BREAKGLASS_USER becomes the only recovery path. Install and verify the guard before §19.
sudo tee /usr/local/sbin/fido2-sudo-pam-guard >/dev/null <<'EOF'
{{EMBED:scripts/fido2-sudo-pam-guard}}
EOF
sudo chmod 755 /usr/local/sbin/fido2-sudo-pam-guard

sudo tee /etc/apt/apt.conf.d/99-fido2-sudo-pam-guard >/dev/null <<'EOF'
{{EMBED:config/99-fido2-sudo-pam-guard}}
EOF

# Verify the guard works:
sudo grep fido2-local-auth /etc/pam.d/sudo
sudo sed -i '/fido2-local-auth/d' /etc/pam.d/sudo
sudo /usr/local/sbin/fido2-sudo-pam-guard
sudo grep fido2-local-auth /etc/pam.d/sudo
sudo -k && sudo -v
✅ Core FIDO Login Checkpoint — §14All 5 must pass
Do not log out, reboot, or run passwd -l until all five pass. Keep a root-capable shell open.
sudo pamtester fido2-test FIDO_USER authenticate       # FIDO PIN + touch
sudo pamtester fido2-test BREAKGLASS_USER authenticate # password
sudo pamtester lightdm FIDO_USER authenticate          # FIDO PIN + touch
sudo pamtester lightdm BREAKGLASS_USER authenticate    # password
sudo -k && sudo -v                                     # FIDO PIN + touch (as FIDO_USER)
TestExpected
Test service — FIDO userFIDO PIN + touch
Test service — breakglassPassword only
LightDM — FIDO userFIDO PIN + touch
LightDM — breakglassPassword only
Real sudo (as FIDO_USER)FIDO PIN + touch
Configure su — Optional (§15)Optional
sudo cp -a /etc/pam.d/su /etc/pam.d/su.bak
sudo editor /etc/pam.d/su
# Keep pam_rootok.so FIRST, then @include fido2-local-auth above @include common-auth

su - FIDO_USER       # FIDO if token present
su - BREAKGLASS_USER # password
XFCE Lock + Token Removal Greeter Switch§16–17
Design rule: the raw xfce4-screensaver unlock dialog is not the primary FIDO unlock surface. The LightDM greeter is. Keep screensaver PAM valid as a fallback only.

XFCE screensaver PAM (§16) — keep valid as fallback

sudo cp -a /etc/pam.d/xfce4-screensaver \
     /etc/pam.d/xfce4-screensaver.bak.before-fido2-local-auth
sudo editor /etc/pam.d/xfce4-screensaver
# Content:
# auth include fido2-local-auth
# auth include common-auth
# auth optional pam_gnome_keyring.so
# account required pam_permit.so

sudo pamtester xfce4-screensaver FIDO_USER authenticate
sudo pamtester xfce4-screensaver FIDO_USER acct_mgmt
sudo pamtester xfce4-screensaver BREAKGLASS_USER authenticate
sudo pamtester xfce4-screensaver BREAKGLASS_USER acct_mgmt

Point xflock4 at the LightDM greeter (§16) — from FIDO_USER XFCE session

xfconf-query -c xfce4-session -p /general/LockCommand \
  --create -t string -s "dm-tool switch-to-greeter"
xfconf-query -c xfce4-session -p /general/LockCommand  # verify output
xflock4                                                  # test manual lock
pgrep -a 'xfce4-screensaver|light-locker|xscreensaver|xsecurelock'  # active lockers?

Find token udev IDs (§17)

sudo udevadm monitor --udev --environment
# Remove token → look for hidraw remove event with ID_FIDO_TOKEN=1
# Record ID_VENDOR_ID and ID_MODEL_ID

Logging test rule first — adjust vendor/model IDs

sudo tee /etc/udev/rules.d/85-fido-lock-on-remove-test.rules >/dev/null <<'EOF'
{{EMBED:config/85-fido-lock-on-remove-test.rules}}
EOF
sudo udevadm control --reload-rules

# Remove token, then:
sudo journalctl -b --since "2 minutes ago" | grep -i fido-udev-test

# If confirmed, remove test rule:
sudo rm -f /etc/udev/rules.d/85-fido-lock-on-remove-test.rules
sudo udevadm control --reload-rules

Real udev rule (after helper script from §17.3 is in place)

sudo tee /etc/udev/rules.d/85-fido-lock-on-remove.rules >/dev/null <<'EOF'
{{EMBED:config/85-fido-lock-on-remove.rules}}
EOF
sudo udevadm control --reload-rules

# Test helper manually:
sudo /usr/local/sbin/fido-remove-switch-to-greeter

# Post-removal log:
sudo journalctl -b --since "3 minutes ago" | \
  grep -Ei 'fido-switch-greeter|fido-udev|dm-tool|lightdm|greeter'
✅ Extended Test Matrix — §18 (before reboot)Before reboot
sudo pamtester fido2-test FIDO_USER authenticate
sudo pamtester fido2-test BREAKGLASS_USER authenticate
sudo pamtester lightdm FIDO_USER authenticate
sudo pamtester lightdm BREAKGLASS_USER authenticate
sudo -k && sudo -v
TestCommand / ActionExpected
Test service — FIDOsudo pamtester fido2-test FIDO_USER authenticateFIDO PIN + touch
Test service — breakglasssudo pamtester fido2-test BREAKGLASS_USER authenticatePassword
LightDM — FIDOsudo pamtester lightdm FIDO_USER authenticateFIDO PIN + touch
LightDM — breakglasssudo pamtester lightdm BREAKGLASS_USER authenticatePassword
Real sudosudo -k then sudo -vFIDO PIN + touch
su (if configured)su - FIDO_USER / su - BREAKGLASS_USERFIDO / password
Manual lockxflock4 from XFCE sessionSwitches to LightDM greeter
Idle timeoutWait for configured timeoutGreeter switch; no flicker loop
Token removalRemove token from active sessionSwitches to LightDM greeter
sudo reboot   # only after all tests pass
Lock Unix Password — §19Point of no return
Only after the full Extended Test Matrix passes and BREAKGLASS_USER is confirmed working. After this step, console TTY password recovery for FIDO_USER is gone. BREAKGLASS_USER is the only password path.
sudo passwd -l FIDO_USER              # lock password
sudo passwd -S FIDO_USER              # verify: field begins with !
sudo getent shadow FIDO_USER
id FIDO_USER                          # still in fido-login?

# Re-test after locking:
sudo -k && sudo -v
xflock4
sudo pamtester lightdm FIDO_USER authenticate
sudo pamtester lightdm BREAKGLASS_USER authenticate
sudo pamtester xfce4-screensaver FIDO_USER authenticate

# Restore if needed:
sudo passwd -u FIDO_USER
GNOME Keyring — §20Optional

Chrome/Chromium prompting for keyring password after FIDO-only login is expected behavior — FIDO login doesn't unlock the keyring automatically.

sudo apt install seahorse
seahorse   # Passwords and Keys → Login → Change Password → leave new password blank
Blanking the keyring password is optional. Explain the tradeoff to students before they do it — it reduces protection for secrets stored in the keyring.
Diagnostics Quick-ReferenceInstructor
# Token
fido2-token -L
lsusb

# Group / mapping
id FIDO_USER
getent group fido-login
sudo ls -l /etc/security/u2f_keys
sudo grep "^FIDO_USER:" /etc/security/u2f_keys

# PAM state
sudo cat /etc/pam.d/fido2-local-auth
sudo grep fido2-local-auth /etc/pam.d/lightdm
sudo grep fido2-local-auth /etc/pam.d/sudo

# Graphical session env — run from FIDO_USER XFCE terminal
echo "$DISPLAY" && echo "$XDG_RUNTIME_DIR" && echo "$DBUS_SESSION_BUS_ADDRESS"

# Active screen lockers
pgrep -a 'xfce4-screensaver|light-locker|xscreensaver|xsecurelock'

# dm-tool present?
command -v dm-tool

# Log tail — token removal / greeter / PAM events
sudo journalctl -b --since "5 minutes ago" | \
  grep -Ei 'fido-switch-greeter|fido-udev|dm-tool|lightdm|pam_u2f|greeter'

# sudo guard log
sudo journalctl -b | grep fido2-sudo-pam-guard
🔁 Post-Lab Token ResetAfter every session

Wipes all credentials and removes the PIN, returning the FIDO2 application to factory state for the next student. Uses the CTAP2 authenticatorReset command.

What gets resetWhat does NOT get reset
All FIDO2/U2F credentials (resident + non-resident)YubiKey OTP slots, PIV certificates, OATH credentials — separate applications, unaffected
FIDO2 PIN
Credential counter

Option A — Generic: fido2-token -R (any FIDO2 token)

Power-up timing requirement (CTAP2 spec): the reset command must reach the token within ~10 seconds of power-up, and the button press to confirm must also happen within that same 10-second window. Both are measured from when the token is inserted. Most common failure: token was already plugged in → power-up window has passed → FIDO_ERR_NOT_ALLOWED. Fix: unplug first, run the command, then replug and press promptly.
# Step 1: unplug the token
# Step 2: run the command — it blocks waiting for the device to appear
fido2-token -R /dev/hidraw1
# Step 3: plug the token back in — do this and the next step within 10 seconds
# Step 4: press the button when the LED flashes (same 10-second window from power-up)

Verify the reset

fido2-token -L                 # confirm device path
fido2-token -I /dev/hidraw1    # PIN management should show no PIN set

Option B — YubiKey only: ykman fido reset

ykman handles the power-up timing requirement and prompts for physical confirmation. Easier to use than fido2-token -R when available.

ykman fido reset           # interactive: prompts to unplug/replug, then touch
ykman fido reset --force   # skip confirmation prompt (still requires physical touch)

VM cleanup

MethodSteps
Snapshot restore (preferred)Restore VM to pre-lab snapshot — resets everything cleanly
Manual rollbackRun Emergency Rollback section, then the commands below

Manual mapping file + group cleanup (if not using snapshot)

sudo rm -f /etc/security/u2f_keys
sudo gpasswd -d FIDO_USER fido-login   # remove user from group
sudo passwd -u FIDO_USER               # unlock password if locked in §19
🚨 Emergency RollbackLockout recovery
Use when PAM changes break authentication and a root-capable shell is still open. If no shell is available: boot recovery or live USB → mount system → edit PAM files to remove fido2-local-auth includes.

Step 1 — Remove all FIDO includes and artifacts

sudo sed -i '/fido2-local-auth/d' \
  /etc/pam.d/lightdm \
  /etc/pam.d/sudo \
  /etc/pam.d/su \
  /etc/pam.d/xfce4-screensaver 2>/dev/null || true

sudo rm -f /etc/pam.d/fido2-local-auth
sudo rm -f /etc/pam.d/fido2-test
sudo rm -f /etc/lightdm/lightdm.conf.d/50-manual-login.conf
sudo rm -f /etc/udev/rules.d/85-fido-lock-on-remove.rules
sudo rm -f /etc/udev/rules.d/85-fido-lock-on-remove-test.rules
sudo rm -f /usr/local/sbin/fido-remove-switch-to-greeter
sudo rm -f /usr/local/sbin/lock-on-fido-remove
sudo rm -f /etc/apt/apt.conf.d/99-fido2-sudo-pam-guard
sudo rm -f /usr/local/sbin/fido2-sudo-pam-guard
sudo udevadm control --reload-rules

Step 2 — Restore backed-up PAM files

sudo test -e /etc/pam.d/lightdm.bak && \
  sudo cp -a /etc/pam.d/lightdm.bak /etc/pam.d/lightdm || true
sudo test -e /etc/pam.d/sudo.bak && \
  sudo cp -a /etc/pam.d/sudo.bak /etc/pam.d/sudo || true
sudo test -e /etc/pam.d/su.bak && \
  sudo cp -a /etc/pam.d/su.bak /etc/pam.d/su || true
sudo test -e /etc/pam.d/xfce4-screensaver.bak.before-fido2-local-auth && \
  sudo cp -a /etc/pam.d/xfce4-screensaver.bak.before-fido2-local-auth \
             /etc/pam.d/xfce4-screensaver || true

Step 3 — Restart display manager

sudo systemctl restart lightdm