Files
homelab-design/decisions/0028-authentik-sso-strategy.md
Billy D. b43c80153c docs: add ADRs 0025-0028 for infrastructure patterns
- 0025: Observability stack (Prometheus + ClickStack + OTEL)
- 0026: Tiered storage strategy (Longhorn + NFS)
- 0027: Database strategy (CloudNativePG for PostgreSQL)
- 0028: Authentik SSO strategy (OIDC/SAML identity provider)
2026-02-04 08:55:15 -05:00

416 lines
14 KiB
Markdown

# Authentik Single Sign-On Strategy
* Status: accepted
* Date: 2026-02-04
* Deciders: Billy
* Technical Story: Centralize authentication across all homelab applications
## Context and Problem Statement
A growing homelab with many self-hosted applications creates authentication sprawl - each app has its own user database, passwords, and session management. This creates poor user experience and security risks.
How do we centralize authentication while maintaining flexibility for different application requirements?
## Decision Drivers
* Single sign-on (SSO) for all applications
* Centralized user management and lifecycle
* MFA enforcement across all applications
* Open-source and self-hosted
* Low resource requirements for homelab scale
## Considered Options
1. **Authentik as OIDC/SAML provider**
2. **Keycloak**
3. **Authelia + LDAP**
4. **Per-application local auth**
## Decision Outcome
Chosen option: **Option 1 - Authentik as OIDC/SAML provider**
Authentik provides modern identity management with OIDC, SAML 2.0, LDAP, and SCIM support. Its flow-based authentication engine allows customizable login experiences.
### Positive Consequences
* Clean, modern UI for users and admins
* Flexible flow-based authentication
* Built-in MFA (TOTP, WebAuthn, SMS, Duo)
* Proxy provider for legacy apps
* SCIM for user provisioning
* Active development and community
### Negative Consequences
* Python-based (higher memory than Go alternatives)
* PostgreSQL dependency
* Some enterprise features require outpost pods
## Architecture
```
┌─────────────────────┐
│ User │
└──────────┬──────────┘
┌─────────────────────┐
│ Ingress/Traefik │
└──────────┬──────────┘
┌──────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ auth.lab.io │ │ app.lab.io │ │ app2.lab.io │
│ (Authentik) │ │ (OIDC-enabled) │ │ (Proxy-auth) │
└─────────────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ ┌──────────────────┘ │
│ │ OIDC/OAuth2 │
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Authentik │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Server │ │ Worker │ │ Outpost │◄───────────┤
│ │ (API) │ │ (Tasks) │ │ (Proxy) │ Forward │
│ └──────┬──────┘ └──────┬──────┘ └─────────────┘ Auth │
│ │ │ │
│ └────────┬───────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ Redis │ │
│ │ (Cache) │ │
│ └─────────────┘ │
│ │
└─────────────────────────────┬──────────────────────────────────┘
┌──────▼──────┐
│ PostgreSQL │
│ (CNPG) │
└─────────────┘
```
## Provider Configuration
### OIDC Applications
| Application | Provider Type | Claims Override | Notes |
|-------------|---------------|-----------------|-------|
| Gitea | OIDC | None | Admin mapping via group |
| Affine | OIDC | `email_verified: true` | See ADR-0016 |
| Companions | OIDC | None | Custom provider |
| Grafana | OIDC | `role` claim | Admin role mapping |
| ArgoCD | OIDC | `groups` claim | RBAC integration |
| MLflow | Proxy | N/A | Forward auth |
| Open WebUI | OIDC | None | LLM interface |
### Provider Template
```yaml
# Example OAuth2/OIDC Provider
apiVersion: authentik.io/v1
kind: OAuth2Provider
metadata:
name: gitea
spec:
name: Gitea
authorizationFlow: default-authorization-flow
clientId: ${GITEA_CLIENT_ID}
clientSecret: ${GITEA_CLIENT_SECRET}
redirectUris:
- https://git.lab.daviestechlabs.io/user/oauth2/authentik/callback
signingKey: authentik-self-signed
propertyMappings:
- authentik-default-openid
- authentik-default-email
- authentik-default-profile
```
## Authentication Flows
### Default Login Flow
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Login │────▶│ Username │────▶│ Password │────▶│ MFA │
│ Stage │ │ Stage │ │ Stage │ │ Stage │
└─────────────┘ └─────────────┘ └─────────────┘ └──────┬──────┘
┌─────────────┐
│ Session │
│ Created │
└─────────────┘
```
### Flow Customization
- **Admin users:** Require hardware key (WebAuthn)
- **API access:** Service account tokens
- **External users:** Email verification + MFA enrollment
## Group-Based Authorization
### Group Structure
```
authentik-admins → Authentik admin access
├── cluster-admins → Full cluster access
├── gitea-admins → Git admin
├── monitoring-admins → Grafana admin
└── ai-platform-admins → AI/ML admin
authentik-users → Standard user access
├── developers → Git write, monitoring read
├── ml-engineers → AI/ML services access
└── guests → Read-only access
```
### Application Group Mapping
```yaml
# Grafana OIDC config
grafana:
auth.generic_oauth:
role_attribute_path: |
contains(groups[*], 'monitoring-admins') && 'Admin' ||
contains(groups[*], 'developers') && 'Editor' ||
'Viewer'
```
## Outpost Deployment
### Embedded Outpost (Default)
- Runs within Authentik server
- Handles LDAP and Radius
- Suitable for small deployments
### Standalone Outpost (Proxy)
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: authentik-outpost-proxy
spec:
replicas: 2
template:
spec:
containers:
- name: outpost
image: ghcr.io/goauthentik/proxy
ports:
- containerPort: 9000
name: http
- containerPort: 9443
name: https
env:
- name: AUTHENTIK_HOST
value: "https://auth.lab.daviestechlabs.io/"
- name: AUTHENTIK_TOKEN
valueFrom:
secretKeyRef:
name: authentik-outpost-token
key: token
```
### Forward Auth Integration
For applications without OIDC support:
```yaml
# Traefik ForwardAuth middleware
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: authentik-forward-auth
spec:
forwardAuth:
address: http://authentik-outpost-proxy.authentik.svc:9000/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-groups
- X-authentik-email
```
## MFA Enforcement
### Policies
| User Group | MFA Requirement |
|------------|-----------------|
| Admins | WebAuthn (hardware key) required |
| Developers | TOTP or WebAuthn required |
| Guests | MFA optional |
### Device Registration
- Self-service MFA enrollment
- Recovery codes generated at setup
- Admin can reset user MFA
## SCIM User Provisioning
### When to Use
- Automatic user creation in downstream apps
- Group membership sync
- User deprovisioning on termination
### Supported Apps
Currently using SCIM provisioning for:
- None (manual user creation in apps)
Future consideration for:
- Gitea organization sync
- Grafana team sync
## Secrets Management Integration
### Vault Integration
```yaml
# External Secret for Authentik DB credentials
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: authentik-db-credentials
namespace: authentik
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
name: authentik-db-credentials
data:
- secretKey: password
remoteRef:
key: kv/data/authentik
property: db_password
- secretKey: secret_key
remoteRef:
key: kv/data/authentik
property: secret_key
```
## Monitoring
### Prometheus Metrics
Authentik exposes metrics at `/metrics`:
- `authentik_login_duration_seconds`
- `authentik_login_attempt_total`
- `authentik_outpost_connected`
- `authentik_provider_authorization_total`
### Grafana Dashboard
- Login success/failure rates
- Active sessions
- Provider usage
- MFA adoption rates
### Alerts
```yaml
- alert: AuthentikHighLoginFailures
expr: rate(authentik_login_attempt_total{result="failure"}[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: High login failure rate detected
- alert: AuthentikOutpostDisconnected
expr: authentik_outpost_connected == 0
for: 5m
labels:
severity: critical
```
## Backup and Recovery
### What to Backup
1. PostgreSQL database (via CNPG)
2. Media files (if custom branding)
3. Blueprint exports (configuration as code)
### Blueprints
Export configuration as YAML for GitOps:
```yaml
# authentik-blueprints/providers/gitea.yaml
version: 1
metadata:
name: Gitea OIDC Provider
entries:
- model: authentik_providers_oauth2.oauth2provider
identifiers:
name: gitea
attrs:
authorization_flow: !Find [authentik_flows.flow, [slug, default-authorization-flow]]
# ... provider config
```
## Integration Patterns
### Pattern 1: Native OIDC
Best for: Modern applications with OIDC support
```
App ──OIDC──▶ Authentik ──▶ App (with user info)
```
### Pattern 2: Proxy Forward Auth
Best for: Legacy apps without SSO support
```
Request ──▶ Traefik ──ForwardAuth──▶ Authentik Outpost
│ │
│◀──────Header injection─────┘
App (reads X-authentik-* headers)
```
### Pattern 3: LDAP Compatibility
Best for: Apps requiring LDAP
```
App ──LDAP──▶ Authentik Outpost (LDAP) ──▶ Authentik
```
## Resource Requirements
| Component | CPU Request | Memory Request |
|-----------|-------------|----------------|
| Server | 100m | 500Mi |
| Worker | 100m | 500Mi |
| Redis | 50m | 128Mi |
| Outpost (each) | 50m | 128Mi |
## Future Enhancements
1. **Passkey/FIDO2** - Passwordless authentication
2. **External IdP federation** - Google/GitHub as upstream IdP
3. **Conditional access** - Device trust, network location policies
4. **Session revocation** - Force logout from all apps
## References
* [Authentik Documentation](https://goauthentik.io/docs/)
* [Authentik GitHub](https://github.com/goauthentik/authentik)
* [OIDC Specification](https://openid.net/specs/openid-connect-core-1_0.html)