From 3580ee12236c4b398887e0e4c8711252f3de4b80 Mon Sep 17 00:00:00 2001 From: "Billy D." Date: Wed, 4 Feb 2026 09:11:29 -0500 Subject: [PATCH] docs: add ADRs 0029-0030 for Authentik registration and MFA - 0029: User registration workflow with approval and group-based access - 0030: MFA and Yubikey strategy for Authentik and Vaultwarden --- ...29-authentik-user-registration-workflow.md | 340 ++++++++++++++++++ decisions/0030-mfa-yubikey-strategy.md | 339 +++++++++++++++++ 2 files changed, 679 insertions(+) create mode 100644 decisions/0029-authentik-user-registration-workflow.md create mode 100644 decisions/0030-mfa-yubikey-strategy.md diff --git a/decisions/0029-authentik-user-registration-workflow.md b/decisions/0029-authentik-user-registration-workflow.md new file mode 100644 index 0000000..2d50ed1 --- /dev/null +++ b/decisions/0029-authentik-user-registration-workflow.md @@ -0,0 +1,340 @@ +# Authentik User Registration and Approval Workflow + +* Status: accepted +* Date: 2026-02-04 +* Deciders: Billy +* Technical Story: Control access to homelab applications through vetted user registration + +## Context and Problem Statement + +The homelab hosts applications that should only be accessible to trusted users (family, close friends). While Authentik provides SSO, we need a controlled registration process where: +1. Users can self-register +2. An admin reviews and approves registrations +3. Approved users get access only to specific applications based on their role + +How do we implement a user registration workflow with approval and conditional application access? + +## Decision Drivers + +* Self-service registration - users shouldn't need admin to create accounts +* Admin approval - prevent unauthorized access +* Granular access control - different users get different applications +* Simple to manage - single admin shouldn't be overwhelmed +* Audit trail - know who approved whom and when + +## Considered Options + +1. **Authentik enrollment flow with admin approval + group-based access** +2. **Invite-only registration (no self-service)** +3. **Open registration with post-hoc restriction** + +## Decision Outcome + +Chosen option: **Option 1 - Authentik enrollment flow with admin approval + group-based access** + +Authentik's enrollment stages allow custom registration flows. Combined with group-based application access, this provides a complete workflow: +- Users self-register → account created but inactive/limited +- Admin receives notification → reviews and assigns groups +- User gets access only to applications their groups permit + +### Positive Consequences + +* Self-service reduces admin burden for initial registration +* Approval step prevents unauthorized access +* Group-based access scales well as applications grow +* Full audit log of approvals +* Can add MFA requirement during enrollment + +### Negative Consequences + +* Admin must actively monitor registration requests +* Users experience delay between registration and access +* Requires proper group/application mapping setup + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ REGISTRATION FLOW │ +│ │ +│ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ User │───▶│ Enrollment │───▶│ Account │───▶│ Pending │ │ +│ │ Requests │ │ Form │ │ Created │ │ Approval │ │ +│ └──────────┘ └──────────────┘ └──────────────┘ └──────┬───────┘ │ +│ │ │ +│ ┌────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────┐ │ +│ │ Admin Reviews │ │ +│ │ (Email/Dashboard) │ │ +│ └──────────┬──────────┘ │ +│ │ │ +│ ┌─────────────┼─────────────┐ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ Reject │ │ Approve │ │ Approve │ │ +│ │ │ │ Basic │ │ Full │ │ +│ └──────────┘ └────┬─────┘ └────┬─────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌──────────┐ ┌──────────┐ │ +│ │ homelab- │ │ homelab- │ │ +│ │ guests │ │ users │ │ +│ └──────────┘ └──────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────────────────────────┐ +│ APPLICATION ACCESS BY GROUP │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ homelab-admins │ │ +│ │ All applications + Authentik admin + Grafana admin │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ homelab-users │ │ +│ │ Affine, Kavita, Nextcloud, Immich, Kasm, ntfy, Vaultwarden │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ homelab-guests │ │ +│ │ Kavita only │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ pending-approval │ │ +│ │ No applications (can only access Authentik profile) │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +## Group Structure + +| Group | Purpose | Applications | +|-------|---------|--------------| +| `homelab-admins` | Full administrative access | All + Gitea, Flux UI, Authentik admin | +| `homelab-users` | Trusted users (family) | Affine, Immich, Kasm, Kavita, Nextcloud, ntfy, Vaultwarden | +| `homelab-guests` | Limited access (friends) | Kavita only | +| `pending-approval` | Newly registered, not yet vetted | None (Authentik profile only) | + +## Application Access Matrix + +External applications exposed via `envoy-external` gateway: + +| Application | admins | users | guests | pending | +|-------------|--------|-------|--------|---------| +| Authentik (admin) | ✅ | ❌ | ❌ | ❌ | +| Authentik (profile) | ✅ | ✅ | ✅ | ✅ | +| Affine | ✅ | ✅ | ❌ | ❌ | +| Gitea | ✅ | ❌ | ❌ | ❌ | +| Immich | ✅ | ✅ | ❌ | ❌ | +| Kasm | ✅ | ✅ | ❌ | ❌ | +| Kavita | ✅ | ✅ | ✅ | ❌ | +| Nextcloud | ✅ | ✅ | ❌ | ❌ | +| ntfy | ✅ | ✅ | ❌ | ❌ | +| Vaultwarden | ✅ | ✅ | ❌ | ❌ | +| Flux UI | ✅ | ❌ | ❌ | ❌ | + +## Implementation + +### Step 1: Create Groups + +In Authentik Admin → Directory → Groups, create: + +1. **homelab-admins** + - Superuser status: Yes (for Authentik admin) + +2. **homelab-users** + - Superuser status: No + +3. **homelab-guests** + - Superuser status: No + +4. **pending-approval** + - Superuser status: No + - This is the default group for new registrations + +### Step 2: Create Enrollment Flow + +In Authentik Admin → Flows & Stages: + +#### 2a. Create Stages + +1. **enrollment-invitation-check** (Invitation Stage) + - Continue without invitation: Yes + +2. **enrollment-user-form** (Prompt Stage) + - Fields: username, email, name, password, password_repeat + +3. **enrollment-user-write** (User Write Stage) + - Create users as inactive: No + - Group: `pending-approval` + +4. **enrollment-user-login** (User Login Stage) + - Session duration: default + +#### 2b. Create Flow + +1. **Name:** `homelab-enrollment` +2. **Slug:** `homelab-enrollment` +3. **Designation:** Enrollment +4. **Bind stages in order:** + - enrollment-invitation-check + - enrollment-user-form + - enrollment-user-write + - enrollment-user-login + +### Step 3: Configure Application Policies + +For each application, create a policy binding that requires group membership: + +#### 3a. Create Group Membership Policies + +In Authentik Admin → Policies: + +**Policy: require-homelab-users** +```python +# Expression Policy +return ak_is_group_member(request.user, name="homelab-users") or \ + ak_is_group_member(request.user, name="homelab-admins") +``` + +**Policy: require-homelab-guests-or-higher** +```python +# Expression Policy +return ak_is_group_member(request.user, name="homelab-guests") or \ + ak_is_group_member(request.user, name="homelab-users") or \ + ak_is_group_member(request.user, name="homelab-admins") +``` + +**Policy: require-homelab-admins** +```python +# Expression Policy +return ak_is_group_member(request.user, name="homelab-admins") +``` + +#### 3b. Bind Policies to Applications + +For each application in Applications → Applications: + +| Application | Policy to Bind | +|-------------|----------------| +| Affine | require-homelab-users | +| Gitea | require-homelab-admins | +| Immich | require-homelab-users | +| Kasm | require-homelab-users | +| Kavita | require-homelab-guests-or-higher | +| Nextcloud | require-homelab-users | +| ntfy | require-homelab-users | +| Vaultwarden | require-homelab-users | +| Flux UI | require-homelab-admins | + +### Step 4: Admin Notification + +Since no SMTP is configured (per ADR-0016), admin notification options: + +#### Option A: Dashboard Monitoring + +1. Periodically check Authentik Admin → Directory → Users +2. Filter by group: `pending-approval` +3. Review and move to appropriate group + +#### Option B: Webhook to Discord/Slack (Future Enhancement) + +Configure Authentik event to send webhook on user creation: + +```python +# Event Rule: new-user-notification +# Trigger: Model Created (User) +# Action: Webhook to Discord + +webhook_url = "https://discord.com/api/webhooks/..." +message = f"New user registration: {model.username} ({model.email})" +``` + +### Step 5: Approval Process + +When a new user registers: + +1. Admin receives notification (or checks dashboard) +2. Admin reviews user information: + - Do I know this person? + - What access level should they have? +3. Admin action: + - **Approve as user:** Remove from `pending-approval`, add to `homelab-users` + - **Approve as guest:** Remove from `pending-approval`, add to `homelab-guests` + - **Reject:** Delete user account +4. User can now access their permitted applications + +## User Experience + +### Registration Flow (User Perspective) + +1. User visits `https://auth.daviestechlabs.io/if/flow/homelab-enrollment/` +2. Fills out registration form (username, email, password) +3. Account created, logged in automatically +4. Sees Authentik user dashboard with no applications +5. Message displayed: "Your account is pending approval" +6. After admin approval, applications appear on dashboard + +### Custom Pending Message + +Create a custom user interface text in Authentik: + +In Admin → Flows → Default Interface → Customization: + +```html + +
+

Account Pending Approval

+

Your registration is being reviewed. You'll receive access to applications once approved.

+

Contact the homelab admin if you need expedited access.

+
+``` + +## MFA Enforcement + +See [ADR-0030: MFA and Yubikey Strategy](0030-mfa-yubikey-strategy.md) for MFA configuration details. + +## Audit Trail + +Authentik logs all changes: + +- User creation (with registration timestamp) +- Group membership changes (who approved, when) +- Application access attempts (granted/denied) + +View in Admin → Events → Logs + +## Security Considerations + +### Rate Limiting + +- Enrollment flow should have rate limiting to prevent spam registrations +- Configure in Flow settings or via reverse proxy + +### Account Cleanup + +- Periodically review `pending-approval` group +- Delete accounts that have been pending > 30 days without approval + +### Group Escalation + +- Only `homelab-admins` can modify group memberships +- Consider requiring MFA for admin actions + +## Future Enhancements + +1. **Email notifications** - When SMTP is configured, notify users of approval +2. **Invitation codes** - Pre-approve specific registrations with invite links +3. **Self-service group requests** - Users can request access to additional apps +4. **Time-limited access** - Guest access expires after N days +5. **Application-specific approval** - Per-app access requests + +## References + +* [Authentik Flows Documentation](https://docs.goauthentik.io/docs/flow/) +* [Authentik Policies](https://docs.goauthentik.io/docs/policies/) +* [Authentik Groups](https://docs.goauthentik.io/docs/user-group-role/groups/) diff --git a/decisions/0030-mfa-yubikey-strategy.md b/decisions/0030-mfa-yubikey-strategy.md new file mode 100644 index 0000000..89f8c3d --- /dev/null +++ b/decisions/0030-mfa-yubikey-strategy.md @@ -0,0 +1,339 @@ +# 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 | ✅ Working | Configure enforcement policies | +| Vaultwarden | ✅ Native | ⚠️ Partial | Enable in admin settings | + +## 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 enabled by default, but admin panel configuration may be needed. + +### Required Configuration + +#### 1. Enable WebAuthn in Admin Panel + +Access admin panel at `https://vaultwarden.daviestechlabs.io/admin`: + +``` +Settings → Advanced: + Enable Web Vault: true (already set) + +Two-Factor Authentication: + Enable WebAuthn: true (verify this is set) +``` + +#### 2. 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: 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 + +#### 3. User Setup (WebAuthn - Already Available) + +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") + +## 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/)