Files
homelab-design/decisions/0030-mfa-yubikey-strategy.md
Billy D. 3a46a98be3
All checks were successful
Update README with ADR Index / update-readme (push) Successful in 6s
docs: add ADR index workflow, standardize all ADR formats
- Add Gitea Action to auto-update README badges and ADR table on push
- Standardize 8 ADRs from heading-style to inline metadata format
- Add shields.io badges for ADR counts (total/accepted/proposed)
- Replace static directory listing with linked ADR table in README
- Accept ADR-0030 (MFA/YubiKey strategy)
2026-02-09 17:25:27 -05:00

13 KiB

MFA and Yubikey Strategy

  • Status: accepted
  • Date: 2026-02-04
  • Deciders: Billy
  • Technical Story: Enable hardware security key (Yubikey) authentication across homelab applications

Context and Problem Statement

Password-only authentication is vulnerable to phishing and credential theft. Hardware security keys (Yubikeys) provide phishing-resistant authentication through WebAuthn/FIDO2. The homelab uses multiple authentication points:

  1. Authentik - Central SSO for most applications
  2. Vaultwarden - Password manager (independent auth)

How do we enable Yubikey support across these authentication points while maintaining usability?

Decision Drivers

  • Phishing-resistant authentication for sensitive applications
  • Flexibility - support both hardware keys and TOTP
  • User experience - single Yubikey works everywhere
  • Fallback options - don't lock users out if key is lost
  • Future-proof - support passkeys and newer standards

Considered Options

  1. WebAuthn/FIDO2 for both Authentik and Vaultwarden
  2. Yubikey OTP only
  3. TOTP only (no hardware key support)

Decision Outcome

Chosen option: Option 1 - WebAuthn/FIDO2 for both Authentik and Vaultwarden

WebAuthn provides the best security (phishing-resistant) and user experience (touch to authenticate). Both Authentik and Vaultwarden support WebAuthn natively.

Positive Consequences

  • Phishing-resistant authentication
  • Single Yubikey works for all applications
  • No external dependencies (unlike Yubikey OTP)
  • Supports passkeys for future passwordless auth
  • Hardware-bound credentials can't be copied

Negative Consequences

  • Requires modern browser with WebAuthn support
  • Users must have physical access to key
  • Key loss requires recovery process

Implementation Status

Application WebAuthn Support Current Status Action Required
Authentik Native Implemented Blueprint deployed via ConfigMap
Vaultwarden Native Implemented None - WebAuthn enrolled

Authentik Configuration

Current State

Authentik has WebAuthn support built-in. Users can already enroll devices via:

  • Settings → MFA Devices → Enroll WebAuthn Device

Required Configuration

1. Create WebAuthn Authenticator Stage

In Admin → Flows & Stages → Stages:

Stage: authenticator-webauthn-setup

Type: Authenticator WebAuthn Setup Stage
Name: authenticator-webauthn-setup
User verification: preferred
Resident key requirement: preferred
Authenticator attachment: cross-platform

Stage: authenticator-webauthn-validation

Type: Authenticator Validation Stage
Name: authenticator-webauthn-validation
Device classes: WebAuthn, TOTP (allow both)
Not configured action: skip

2. Bind to Authentication Flow

Edit the default authentication flow to include MFA:

Flow: default-authentication-flow
Stages (in order):
1. identification (username/email)
2. password
3. authenticator-webauthn-validation  ← Add this
4. user-login

3. Create MFA Enforcement Policies

Policy: enforce-mfa-admins

# Expression Policy
# Requires WebAuthn for admins
if ak_is_group_member(request.user, name="homelab-admins"):
    from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
    if not WebAuthnDevice.objects.filter(user=request.user).exists():
        ak_message("Administrators must enroll a hardware security key (Yubikey)")
        return False
return True

Policy: enforce-mfa-users

# Expression Policy
# Requires any MFA for users
if ak_is_group_member(request.user, name="homelab-users"):
    from authentik.stages.authenticator_totp.models import TOTPDevice
    from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
    has_totp = TOTPDevice.objects.filter(user=request.user, confirmed=True).exists()
    has_webauthn = WebAuthnDevice.objects.filter(user=request.user).exists()
    if not (has_totp or has_webauthn):
        ak_message("Please enroll an MFA device (authenticator app or security key)")
        return False
return True

4. User Enrollment Flow

  1. User logs into Authentik
  2. Navigates to Settings → MFA Devices
  3. Clicks "Enroll WebAuthn Device"
  4. Inserts Yubikey and touches when prompted
  5. Names the device (e.g., "Yubikey 5 NFC - Primary")
  6. Optionally enrolls backup device or TOTP

Vaultwarden Configuration

Current State

Vaultwarden deployment has WebAuthn support built-in. Configuration is done via the admin panel.

Required Configuration

1. Enable WebAuthn in Admin Panel

Access admin panel at https://vaultwarden.daviestechlabs.io/admin:

  1. Navigate to Settings section
  2. Find Yubikey settings:
    • For WebAuthn/FIDO2: No additional configuration needed (enabled by default)
    • For Yubikey OTP: Requires Client ID and Secret Key from Yubico
  3. Find Two-Factor Authentication or General settings:
    • Verify WebAuthn is not disabled
  4. Click Save if any changes made

2. User Setup (WebAuthn)

  1. Log into Vaultwarden web vault
  2. Go to Settings → Security → Two-step Login
  3. Click Manage next to "FIDO2 WebAuthn"
  4. Click "Register new key"
  5. Insert Yubikey and touch when prompted
  6. Name the key (e.g., "Yubikey 5 NFC")

3. Optional: Enable Yubikey OTP

If users want Yubikey OTP as an additional option (the 44-character string feature):

Step 1: Get Yubico API Credentials

Visit: https://upgrade.yubico.com/getapikey/

Step 2: Enter credentials in Admin Panel

In the Vaultwarden admin panel → Yubikey section:

  • Enter Client ID
  • Enter Secret Key
  • Click Save

Alternatively, for GitOps management:

Step 2b: Store credentials in Vault

vault kv put kv/vaultwarden-yubico \
  client_id="YOUR_CLIENT_ID" \
  secret_key="YOUR_SECRET_KEY"

Step 3: Create ExternalSecret

Add to /home/billy/homelab-k8s2/kubernetes/apps/security/vaultwarden/app/externalsecret.yaml:

---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: vaultwarden-yubico
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: vault
  target:
    name: vaultwarden-yubico
    creationPolicy: Owner
  data:
    - secretKey: YUBICO_CLIENT_ID
      remoteRef:
        key: kv/data/vaultwarden-yubico
        property: client_id
    - secretKey: YUBICO_SECRET_KEY
      remoteRef:
        key: kv/data/vaultwarden-yubico
        property: secret_key

Step 4: Update Deployment

Add to /home/billy/homelab-k8s2/kubernetes/apps/security/vaultwarden/app/deployment.yaml:

envFrom:
  - secretRef:
      name: vaultwarden-db-credentials
  - secretRef:
      name: vaultwarden-yubico  # Add this

Status: NOT IMPLEMENTED - Requires Yubico API credentials

MFA Requirements by User Group

Group MFA Requirement Allowed Methods
homelab-admins Required WebAuthn only
homelab-users Required WebAuthn or TOTP
homelab-guests Optional WebAuthn or TOTP
pending-approval Not required N/A

Recovery Procedures

Lost Yubikey - Authentik

  1. Admin accesses Authentik Admin → Directory → Users
  2. Find affected user
  3. Go to user's MFA Devices tab
  4. Delete lost device
  5. User can enroll new device on next login

Lost Yubikey - Vaultwarden

  1. User uses backup recovery code (generated at 2FA setup)
  2. Or admin accesses Vaultwarden admin panel
  3. Users → Find user → Disable 2FA
  4. User can re-enable with new device

Best Practice: Backup Keys

Recommend users enroll:

  • Primary Yubikey (carried daily)
  • Backup Yubikey (stored securely at home)
  • TOTP as fallback (if allowed by policy)

Supported Yubikey Models

Model WebAuthn NFC USB-C Recommended For
Yubikey 5 NFC Mobile + Desktop (USB-A)
Yubikey 5C NFC Modern laptops
Yubikey 5 Nano Always-in desktop
Security Key NFC Budget option

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         User Login                               │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                        Authentik SSO                             │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐         │
│  │   Password  │───▶│  WebAuthn   │───▶│   Session   │         │
│  │    Stage    │    │ Validation  │    │  Created    │         │
│  └─────────────┘    └─────────────┘    └─────────────┘         │
│                            │                                     │
│                     ┌──────┴──────┐                             │
│                     │  Yubikey    │                             │
│                     │   Touch     │                             │
│                     └─────────────┘                             │
└──────────────────────────────┬──────────────────────────────────┘
                               │ SSO (no MFA needed again)
          ┌────────────────────┼────────────────────┐
          ▼                    ▼                    ▼
    ┌──────────┐         ┌──────────┐        ┌──────────┐
    │  Affine  │         │ Nextcloud│        │  Immich  │
    └──────────┘         └──────────┘        └──────────┘

┌─────────────────────────────────────────────────────────────────┐
│                     Vaultwarden (Separate)                       │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐         │
│  │  Master     │───▶│  WebAuthn   │───▶│   Vault     │         │
│  │  Password   │    │  (2FA)      │    │  Unlocked   │         │
│  └─────────────┘    └──────┬──────┘    └─────────────┘         │
│                            │                                     │
│                     ┌──────┴──────┐                             │
│                     │  Yubikey    │                             │
│                     │   Touch     │                             │
│                     └─────────────┘                             │
└─────────────────────────────────────────────────────────────────┘

Testing Checklist

Authentik WebAuthn

  • Enroll Yubikey as admin user
  • Verify login requires Yubikey touch
  • Test SSO to Affine with Yubikey
  • Test recovery with backup device
  • Verify non-admin can use TOTP instead

Vaultwarden WebAuthn

  • Enable WebAuthn in admin panel
  • Enroll Yubikey for test user
  • Verify vault unlock requires Yubikey
  • Test with browser extension
  • Test recovery code flow

Future Enhancements

  1. Passkeys - Enable passwordless login with resident keys
  2. Conditional access - Require hardware key for sensitive operations only
  3. Device attestation - Verify Yubikey authenticity
  4. Session binding - Bind sessions to hardware key

References