Authentik SSO Infrastructure¶
Authentik is the central identity provider for the homelab, providing: - LDAP authentication for legacy apps (Jellyfin, arr stack) - OIDC/OAuth2 for modern web applications - Forward authentication for services without native auth
Architecture¶
┌─────────────────────────────────────┐
│ Authentik (authentik ns) │
│ ┌─────────┐ ┌──────────┐ │
│ │ Server │ │ Worker │ │
│ └────┬────┘ └────┬─────┘ │
│ │ │ │
│ ┌────▼───────────▼────┐ │
│ │ Redis (bundled) │ │
│ └─────────────────────┘ │
└──────────┬─────────────────────────┘
│
▼
┌────────────────────────────────────────┐
│ PostgreSQL (supabase/postgres-0) │
│ Schema: authentik │
└────────────────────────────────────────┘
Current Configuration¶
Services¶
| Service | Type | IP/DNS | Port |
|---|---|---|---|
| Authentik Web UI | Ingress | auth.internal |
80 |
| LDAP Outpost | LoadBalancer | 10.89.97.218 |
389/636 |
LDAP Provider¶
| Setting | Value |
|---|---|
| Name | jellyfin-ldap |
| Base DN | DC=ldap,DC=goauthentik,DC=io |
| Bind Mode | cached |
| Search Mode | cached |
Applications¶
| App | Slug | Provider | Allowed Groups |
|---|---|---|---|
| Jellyfin | jellyfin |
jellyfin-ldap |
authentik Admins, authentik Read-only |
Key Concepts¶
Provider¶
The technical backend that handles authentication protocols.
| Provider Type | Protocol | Use Case |
|---|---|---|
| LDAP | LDAP/LDAPS | Legacy apps (Jellyfin, arr stack) |
| OAuth2/OIDC | OAuth 2.0 / OpenID Connect | Modern web apps |
| Proxy | Forward Auth | Apps without native auth |
Application¶
The "front door" that users see. Links to exactly one provider and controls access via policy bindings.
Outpost¶
A separate microservice that speaks non-HTTP protocols (LDAP, RADIUS) or handles proxy auth.
Critical: Outposts must have applications explicitly assigned to function.
Policy Bindings¶
Controls who can access an application. Bind groups (not individual users) to applications for access control.
LDAP Authentication Flow¶
1. User enters username/password in Jellyfin
│
▼
2. Jellyfin LDAP plugin BINDS to outpost (as service account)
│
▼
3. Jellyfin SEARCHES for user in LDAP directory
│
▼
4. Jellyfin attempts to BIND AS USER with provided password
│
▼
5. LDAP Outpost validates against Authentik's user database
│
▼
6. Success → Jellyfin creates/updates local user profile
Adding a New LDAP Application¶
Step 1: Create LDAP Provider¶
- Go to http://auth.internal → Login as akadmin
- Navigate to Providers → Create
- Select LDAP Provider
- Configure:
- Name:
<app-name>-ldap - Authorization flow:
default-authentication-flow - Base DN:
DC=ldap,DC=goauthentik,DC=io(or unique per app) - Bind mode:
Cached binding(recommended) - Search mode:
Cached querying(recommended)
Step 2: Create Application¶
- Navigate to Applications → Create
- Configure:
- Name: Display name (e.g., "Jellyfin")
- Slug: URL-safe identifier (e.g., "jellyfin")
- Provider: Select the LDAP provider you created
- Add Policy Bindings:
- Click Policy / Group / User Bindings
- Create Binding → Select Group
- Add groups that should have access (e.g.,
authentik Admins)
Step 3: Assign to LDAP Outpost¶
- Navigate to Applications → Outposts
- Click ldap-outpost → Edit
- In Applications, add your new application
- Click Update
The outpost will automatically reload configuration.
Step 4: Configure Client Application¶
Use these LDAP settings in your application:
| Setting | Value |
|---|---|
| LDAP Server | 10.89.97.218 |
| LDAP Port | 389 (or 636 for LDAPS) |
| Secure LDAP | No (for port 389) |
| Bind User DN | cn=akadmin,ou=users,dc=ldap,dc=goauthentik,dc=io |
| Bind Password | akadmin's password |
| Base DN | dc=ldap,dc=goauthentik,dc=io |
| User Filter | (objectClass=user) |
| User Attributes | uid, cn, mail, displayName |
| Name Attribute | cn |
Security: MFA Setup¶
Since Authentik is internet-facing at auth.bogocat.com, MFA is essential.
MFA Options Comparison¶
| Method | Pros | Cons |
|---|---|---|
| WebAuthn/Passkeys | Fastest (tap & done), phishing-resistant, no codes | Requires compatible device |
| TOTP | Universal support, works anywhere | Code entry friction, clock sync issues |
Recommendation: Use WebAuthn as primary, TOTP as fallback.
Current Configuration¶
The authentication flow uses:
default-authentication-flow:
Order 10: identification-stage (username/email)
Order 20: password-stage
Order 30: mfa-validation (TOTP + WebAuthn)
Step 1: Create TOTP Setup Stage¶
- Go to http://auth.internal → Flows & Stages → Stages
- Click Create → Authenticator TOTP Setup Stage
- Configure:
- Name:
totp-setup - Friendly Name:
Authenticator App Setup - Digits:
6(default) - Click Create
Step 2: Create WebAuthn Setup Stage¶
- Still in Stages, click Create → Authenticator WebAuthn Setup Stage
- Configure:
- Name:
webauthn-setup - Friendly Name:
Passkey / Security Key - User verification:
Preferred - Resident key requirement:
Preferred - Authenticator attachment:
Any - Click Create
Step 3: Create Authenticator Validation Stage¶
- Still in Stages, click Create → Authenticator Validation Stage
- Configure:
- Name:
mfa-validation - Device Classes: Check BOTH:
- ✅ TOTP Authenticators
- ✅ WebAuthn Authenticators
- Not configured action:
Configure(forces setup if no device) - Configuration Stages: Select BOTH
totp-setupANDwebauthn-setup - WebAuthn user verification:
Preferred - Click Create
Step 4: Add to Authentication Flow¶
- Go to Flows & Stages → Flows
- Find default-authentication-flow
- Click on it, then go to Stage Bindings
- Click Bind Stage
- Configure:
- Stage:
mfa-validation - Order:
30(after password validation, usually order 20) - Click Create
Step 5: Test WebAuthn Enrollment¶
- Open incognito window
- Go to https://auth.bogocat.com
- Login with username/password
- At MFA prompt, choose Passkey / Security Key
- Follow browser prompts to register your device (fingerprint, Face ID, or security key)
- Future logins: tap fingerprint/key instead of entering TOTP code
Enrolling Additional WebAuthn Devices¶
Users can add more devices via self-service:
- Log into Authentik
- Click user icon → User Settings
- Go to MFA Devices
- Click Enroll → WebAuthn Device
- Name the device (e.g., "MacBook Touch ID", "YubiKey")
- Follow browser prompts
Passkey Storage Options¶
Where your passkey is stored determines which devices can use it:
| Browser | Storage Location | Syncs To |
|---|---|---|
| Chrome | Google Password Manager | All devices signed into Google |
| Safari | iCloud Keychain | All Apple devices |
| Firefox | macOS local keychain | This Mac only |
For multi-device access, either: - Enroll a separate passkey from each device, OR - Use Chrome/Safari to store in a syncing provider (Google/iCloud)
To add a phone as a passkey device: 1. During enrollment, choose "Use a phone or tablet" or scan QR 2. Complete enrollment on the phone 3. Phone's biometrics now work for login
Troubleshooting WebAuthn¶
"This browser doesn't support WebAuthn" - Use Chrome, Firefox, Safari, or Edge (modern versions) - Ensure HTTPS (WebAuthn requires secure context)
"User verification failed" - Device may not have biometrics configured - Try a hardware security key instead
"Registration failed"
- Check browser console for errors
- Ensure auth.internal or auth.bogocat.com is the RP ID
WebAuthn loops / "Authenticating" spins forever - Check for duplicate MFA stage bindings in the authentication flow:
kubectl exec -n authentik deploy/authentik-server -- ak shell -c "
from authentik.flows.models import Flow, FlowStageBinding
flow = Flow.objects.get(slug='default-authentication-flow')
for b in FlowStageBinding.objects.filter(target=flow).order_by('order'):
print(f'Order {b.order}: {b.stage.name}')
"
Additional Security Recommendations¶
- Strong akadmin password: Change immediately after initial setup
- Create personal admin account: Don't use akadmin for daily use
- Rate limiting: Built-in (5 failed attempts = temporary lockout)
- Consider disabling akadmin: After creating another admin user
- Enroll multiple WebAuthn devices: Have a backup (e.g., phone + laptop)
User Management¶
Creating Users¶
- Directory → Users → Create
- Set username, email, name
- Set Password via user actions menu
Adding Users to Groups¶
- Directory → Groups → Select Group
- Click Users tab
- Add existing user
Default Groups¶
| Group | Purpose |
|---|---|
authentik Admins |
Full admin access to Authentik + all apps |
authentik Read-only |
Read-only Authentik access + configured apps |
Troubleshooting¶
"No provider found for request"¶
Cause: Application not assigned to outpost.
Fix: Applications → Outposts → ldap-outpost → Edit → Add application.
"Invalid password" but password works in Authentik UI¶
Cause: Session cache from previous failed attempt.
Fix: Restart the LDAP outpost:
LDAP service reverts to ClusterIP¶
Cause: Authentik controller recreates service with default type.
Fix: Update outpost config in database:
kubectl exec -n supabase postgres-0 -- psql -U postgres -d postgres -c "
SET search_path TO authentik;
UPDATE authentik_outposts_outpost
SET _config = jsonb_set(_config::jsonb, '{kubernetes_service_type}', '\"LoadBalancer\"')
WHERE name = 'ldap-outpost';
"
Or manually create/patch service:
Testing LDAP Connection¶
# Test bind as service account
ldapsearch -x -H ldap://10.89.97.218:389 \
-D "cn=akadmin,ou=users,dc=ldap,dc=goauthentik,dc=io" \
-w "<password>" \
-b "dc=ldap,dc=goauthentik,dc=io" "(objectClass=user)"
# Test bind as specific user
ldapsearch -x -H ldap://10.89.97.218:389 \
-D "cn=<username>,ou=users,dc=ldap,dc=goauthentik,dc=io" \
-w "<password>" \
-b "dc=ldap,dc=goauthentik,dc=io" "(cn=<username>)"
Checking Outpost Logs¶
Deployment¶
Initial Deployment¶
This script: 1. Creates namespace 2. Sets up PostgreSQL user/schema in Supabase 3. Creates Kubernetes secrets 4. Deploys via Helm chart
Post-Deployment¶
- Access initial setup:
http://auth.internal/if/flow/initial-setup/ - Set akadmin password
- Create LDAP provider and application
- Create LDAP outpost
- Manually create LoadBalancer service (see below)
Manual LDAP Service (Required)¶
The Authentik-managed service defaults to ClusterIP. Create LoadBalancer manually:
apiVersion: v1
kind: Service
metadata:
name: ak-outpost-ldap-outpost
namespace: authentik
spec:
type: LoadBalancer
ports:
- name: ldap
port: 389
targetPort: 3389
- name: ldaps
port: 636
targetPort: 6636
- name: metrics
port: 9300
targetPort: 9300
selector:
goauthentik.io/outpost-name: ldap-outpost
Files Reference¶
| File | Purpose |
|---|---|
/root/tower-fleet/manifests/authentik/deploy.sh |
Automated deployment script |
/root/tower-fleet/manifests/authentik/values.yaml |
Helm chart values |
/root/tower-fleet/manifests/authentik/namespace.yaml |
Namespace definition |
/root/tower-fleet/manifests/authentik/migrations/ |
PostgreSQL migrations |