- 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)
416 lines
14 KiB
Markdown
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)
|