# MFA and Yubikey Strategy * Status: proposed * 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 | ⚠️ In Progress | Configure enforcement policies | | 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** ```python # 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** ```python # 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** ```bash 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`: ```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`: ```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 * [Authentik WebAuthn Documentation](https://docs.goauthentik.io/docs/flow/stages/authenticator_webauthn/) * [Vaultwarden 2FA Configuration](https://github.com/dani-garcia/vaultwarden/wiki/Enabling-WebAuthn-authentication) * [WebAuthn Specification](https://www.w3.org/TR/webauthn-2/) * [FIDO Alliance](https://fidoalliance.org/) * [Yubico Developer Program](https://developers.yubico.com/)