All checks were successful
Update README with ADR Index / update-readme (push) Successful in 6s
- 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)
349 lines
13 KiB
Markdown
349 lines
13 KiB
Markdown
# 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**
|
|
```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/)
|