- Documents recent infrastructure cleanup (8 CTs destroyed, 35 DNS records removed, Headscale cleanup) - Adds 24 new runbooks covering Authentik, PeerTube, Meshtastic, RECON, Proxmox, Mailcow, Internet Archive, GPU routing - Adds project documentation for headscale, vaultwarden, peertube, matrix, mmud, advbbs, arr stack - Updates services.md, environment.md, caddy.md, authentik.md to match live infrastructure - Removes 4 deprecated runbook duplicates (canonical versions live in projects/) - Adds .gitignore for binary archives and editor temp files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
373 lines
14 KiB
Markdown
373 lines
14 KiB
Markdown
# Authentik SSO Configuration
|
|
|
|
## Location
|
|
|
|
- **Server:** Contabo (5.189.158.149 / 100.64.0.1)
|
|
- **URL:** https://auth.echo6.co
|
|
- **Internal Port:** 9000
|
|
|
|
## API Access
|
|
|
|
API token stored in `/home/zvx/projects/.ref/credentials` as `AUTHENTIK_API_TOKEN`
|
|
|
|
```bash
|
|
# Test API access
|
|
curl -s "https://auth.echo6.co/api/v3/core/applications/" \
|
|
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" | python3 -m json.tool
|
|
```
|
|
|
|
## Flow UUIDs
|
|
|
|
Required for OAuth2 provider creation:
|
|
|
|
| Flow | UUID |
|
|
|------|------|
|
|
| Authorization (implicit) | `86051292-389f-4bd9-b0f9-53cd32f197fd` |
|
|
| Authorization (explicit) | `6f9f5c89-9f98-4776-9e0d-a72a8ad17963` |
|
|
| Invalidation | `ed861c0d-2c81-4c3d-819b-946a21c4296a` |
|
|
| Provider Invalidation | `1eb91626-19a3-4f45-b384-d699c6189197` |
|
|
|
|
## Signing Key
|
|
|
|
| Key | UUID |
|
|
|-----|------|
|
|
| authentik Self-signed Certificate | `09f508f0-6b8e-4031-8563-0d3cebf86868` |
|
|
|
|
## Property Mappings (Standard OIDC Set)
|
|
|
|
Include these three in every provider:
|
|
|
|
| Mapping | UUID |
|
|
|---------|------|
|
|
| OpenID Connect scope: openid | `c6426dad-0d85-4daa-89fe-cb850ad4bfd7` |
|
|
| Echo6 OAuth: email (verified=true) | `02c22323-da89-457a-bc12-7f4dd6a3d8ab` |
|
|
| OpenID Connect scope: profile | `113ab791-fa04-4e8c-b103-09d706bc21b4` |
|
|
|
|
**Note:** The default email scope (`096b0d6f`) was changed in 2025.10 to return `email_verified: false`. Our custom scope (`02c22323`) overrides this to always return `true`. All 14 OAuth2 providers were migrated to use the custom scope on 2026-02-16.
|
|
|
|
## Current OAuth2 Providers
|
|
|
|
| PK | Name | Client ID | Application | Redirect URI |
|
|
|----|------|-----------|-------------|--------------|
|
|
| 1 | Mailcow | mailcow | Mailcow | `https://mail.echo6.co/sso/oidc` |
|
|
| 2 | Forgejo | forgejo | Forgejo | `https://forge.echo6.co/user/oauth2/Authentik/callback` |
|
|
| 3 | Vaultwarden | vaultwarden | Vaultwarden | `https://vault.echo6.co/identity/connect/callback` |
|
|
| 4 | Proxmox | proxmox | Proxmox VE | `https://proxmox.echo6.co` (regex) |
|
|
| 5 | Headscale | headscale | Headscale VPN | `https://vpn.echo6.co/oidc/callback` |
|
|
| 6 | Headplane | headplane | Headplane | `https://vpn.echo6.co/admin/oidc/callback` |
|
|
| 8 | Nextcloud | nextcloud | Nextcloud | `https://nextcloud.echo6.co/apps/oidc_login/oidc` |
|
|
| 9 | Immich | immich | Immich | `https://immich.echo6.co/auth/login`, `/api/oauth/mobile-redirect` |
|
|
| 10 | jellyfin | jellyfin | Jellyfin | `https://jellyfin.echo6.co/sso/OID/redirect/Authentik` |
|
|
| 11 | jellyseer | jellyseer | Jellyseer | `https://requests.echo6.co/(login\|api/v1/auth/oidc-callback).*` (regex) |
|
|
| 12 | PeerTube | peertube | PeerTube | `https://stream.echo6.co/plugins/auth-openid-connect/...` |
|
|
| 13 | WATCHTOWER | watchtower | WATCHTOWER | Forward auth (proxy provider) |
|
|
| 14 | Open WebUI | open-webui | Open WebUI | `https://ai.echo6.co/oauth/oidc/callback` |
|
|
| 15 | Matrix | 93kCoZkBlnJyD9EcAm7E4btKflecOcBm9DGONB5T | Matrix | `https://matrix.echo6.co/_synapse/client/oidc/callback` |
|
|
| 16 | Files Forward Auth | — | Files | Forward auth (proxy provider) |
|
|
| 17 | LiveSync Provisioner | ZBoLdYmxlSUyMqgekswIPS4YuaeBn5uCr8GtWm5H | LiveSync | Forward auth (proxy provider) |
|
|
|
|
## Groups
|
|
|
|
| Name | PK | Superuser | Members | Used By |
|
|
|------|----|-----------|---------|----- ---|
|
|
| authentik Admins | `9944e153-f860-4443-81d1-ae544f611806` | Yes | akadmin, matt | All apps (admin access) |
|
|
| media-users | `0820b2b8-6c54-4c20-9a0a-872820e6d9ea` | No | jodie, matt | Jellyfin, Jellyseer, PeerTube |
|
|
| ai-users | `0631b273-cfd8-4ed1-afa6-e262d0dc5a69` | No | matt | Open WebUI |
|
|
| cloud-users | `db3cbf5d-8057-4e33-8e8d-95bfdb35fbac` | No | — | Immich, Nextcloud |
|
|
| communication-users | `31bce176-cd86-4aea-8db3-a57e03d5c2d1` | No | — | Mailcow, Matrix |
|
|
| productivity-users | `698d80c7-7c29-43cd-b5d4-9eb24c85a6cc` | No | — | — |
|
|
| security-users | `f345a043-c2a4-4906-a43b-9860eae86ee1` | No | — | — |
|
|
| proxmox_admins | `d85a868d-7d1e-4585-92a8-b8bb86771b53` | No | akadmin, matt | Proxmox VE |
|
|
| proxmox_users | `cf26703a-a824-47dd-9550-30b848a8ce5f` | No | — | Proxmox VE |
|
|
| authentik Read-only | `ce03664b-46f3-43b7-9967-5f65d591fdb6` | No | — | RBAC read-only role |
|
|
| livesync-users | `8e575a86-326e-4df8-8828-8379d8ab861f` | No | matt | LiveSync |
|
|
|
|
## Application Access Pattern
|
|
|
|
Every application uses `policy_engine_mode: "any"` with two group bindings:
|
|
|
|
1. **authentik Admins** — gives admin/superuser access to all apps
|
|
2. **Service-specific group** — controls which regular users can access the app
|
|
|
|
Users must be in at least one bound group to access the application.
|
|
|
|
## Create New API Token
|
|
|
|
```bash
|
|
ssh root@100.64.0.1 'docker exec -i authentik-server ak shell' <<'PYEOF'
|
|
from authentik.core.models import Token, TokenIntents, User
|
|
user = User.objects.get(username="akadmin")
|
|
Token.objects.filter(identifier="token-name").delete()
|
|
t = Token(identifier="token-name", user=user, intent=TokenIntents.INTENT_API, expiring=False, managed=None)
|
|
t.save()
|
|
print(t.key)
|
|
PYEOF
|
|
```
|
|
|
|
## Full OAuth2 App Setup (Provider + Application + Group + Bindings)
|
|
|
|
### Step 1: Create the OAuth2 provider
|
|
|
|
```bash
|
|
curl -s -X POST "https://auth.echo6.co/api/v3/providers/oauth2/" \
|
|
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "AppName",
|
|
"authorization_flow": "86051292-389f-4bd9-b0f9-53cd32f197fd",
|
|
"invalidation_flow": "ed861c0d-2c81-4c3d-819b-946a21c4296a",
|
|
"property_mappings": [
|
|
"c6426dad-0d85-4daa-89fe-cb850ad4bfd7",
|
|
"02c22323-da89-457a-bc12-7f4dd6a3d8ab",
|
|
"113ab791-fa04-4e8c-b103-09d706bc21b4"
|
|
],
|
|
"client_type": "confidential",
|
|
"client_id": "appname",
|
|
"client_secret": "<generate-a-long-random-secret>",
|
|
"redirect_uris": [{"matching_mode": "strict", "url": "https://app.echo6.co/callback"}],
|
|
"sub_mode": "user_username",
|
|
"signing_key": "09f508f0-6b8e-4031-8563-0d3cebf86868",
|
|
"include_claims_in_id_token": true
|
|
}'
|
|
# Note the "pk" from the response — needed for Step 2
|
|
```
|
|
|
|
### Step 2: Create the application
|
|
|
|
```bash
|
|
curl -s -X POST "https://auth.echo6.co/api/v3/core/applications/" \
|
|
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "AppName",
|
|
"slug": "appname",
|
|
"provider": PROVIDER_PK,
|
|
"meta_launch_url": "https://app.echo6.co",
|
|
"policy_engine_mode": "any"
|
|
}'
|
|
# Note the "pk" from the response — needed for Step 4
|
|
```
|
|
|
|
### Step 3: Create the user group
|
|
|
|
```bash
|
|
curl -s -X POST "https://auth.echo6.co/api/v3/core/groups/" \
|
|
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "appname-users",
|
|
"is_superuser": false,
|
|
"users": [7]
|
|
}'
|
|
# user 7 = matt. Note the "pk" from the response.
|
|
```
|
|
|
|
### Step 4: Bind groups to the application
|
|
|
|
```bash
|
|
# Bind authentik Admins
|
|
curl -s -X POST "https://auth.echo6.co/api/v3/policies/bindings/" \
|
|
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"target": "APPLICATION_PK",
|
|
"group": "9944e153-f860-4443-81d1-ae544f611806",
|
|
"order": 0, "enabled": true, "negate": false, "timeout": 30
|
|
}'
|
|
|
|
# Bind service-specific group
|
|
curl -s -X POST "https://auth.echo6.co/api/v3/policies/bindings/" \
|
|
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"target": "APPLICATION_PK",
|
|
"group": "GROUP_PK",
|
|
"order": 0, "enabled": true, "negate": false, "timeout": 30
|
|
}'
|
|
```
|
|
|
|
### Step 5: Verify
|
|
|
|
```bash
|
|
# OIDC discovery should return endpoints
|
|
curl -s "https://auth.echo6.co/application/o/appname/.well-known/openid-configuration" | python3 -m json.tool
|
|
|
|
# Check bindings
|
|
curl -s "https://auth.echo6.co/api/v3/policies/bindings/?target=APPLICATION_PK" \
|
|
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN"
|
|
```
|
|
|
|
## Common Redirect URI Patterns
|
|
|
|
| Application Type | Redirect URI Pattern |
|
|
|------------------|---------------------|
|
|
| Web app (generic) | `https://app.echo6.co/callback` |
|
|
| Web app (oauth path) | `https://app.echo6.co/oauth/callback` |
|
|
| Open WebUI (OIDC) | `https://app.echo6.co/oauth/oidc/callback` |
|
|
| Forgejo | `https://app.echo6.co/user/oauth2/Authentik/callback` |
|
|
| Jellyfin (SSO plugin) | `https://app.echo6.co/sso/OID/redirect/Authentik` |
|
|
| Caddy forward auth | `https://app.echo6.co/outpost.goauthentik.io/callback` |
|
|
|
|
## Users
|
|
|
|
| PK | Username | Name | Email |
|
|
|----|----------|------|-------|
|
|
| 6 | akadmin | authentik Default Admin | root@example.com |
|
|
| 7 | matt | Matt Johnson | matt@echo6.co |
|
|
| 9 | jodie | Jodie | johnsonsinidaho@gmail.com |
|
|
|
|
## Email Invitation System
|
|
|
|
### SMTP Configuration
|
|
|
|
Authentik sends email via Mailcow:
|
|
|
|
| Setting | Value |
|
|
|---------|-------|
|
|
| SMTP Host | mail.echo6.co |
|
|
| SMTP Port | 587 (STARTTLS) |
|
|
| Username | no-reply@echo6.co |
|
|
| From | no-reply@echo6.co |
|
|
|
|
**Important:** The no-reply@echo6.co mailbox MUST have `authsource=mailcow` in the Mailcow database (not `generic-oidc`). If it gets reset to `generic-oidc`, SMTP auth will fail because Dovecot tries to authenticate via Authentik SSO instead of the local password. Fix with:
|
|
```sql
|
|
docker exec mailcowdockerized-mysql-mailcow-1 mysql -u mailcow -p<DBPASS> mailcow \
|
|
-e "UPDATE mailbox SET authsource='mailcow' WHERE username='no-reply@echo6.co'"
|
|
```
|
|
|
|
### Enrollment Flow
|
|
|
|
| Component | PK |
|
|
|-----------|-----|
|
|
| Flow (invitation-enrollment) | `184a9e20-f5c5-4b44-8775-266a568439c0` |
|
|
| Invitation stage (enrollment-invitation) | `994252a6-a659-4304-b9aa-a6591857a53b` |
|
|
| Prompt stage (enrollment-credentials) | `a5e2a2f1-95c9-4627-a3dd-e0915ff64cda` |
|
|
| User write stage (enrollment-user-write) | `05f565e0-9d64-4ecc-b738-8ffc697ad829` |
|
|
| User login stage (enrollment-user-login) | `f23958ea-47bd-4fb2-a657-b6f85fc6331a` |
|
|
|
|
Stage order: Invitation (10) → Prompt (20) → User Write (30) → User Login (40)
|
|
|
|
Flow settings: `authentication=require_unauthenticated`, `continue_flow_without_invitation=false`
|
|
|
|
User write creates users under `users/enrolled` path.
|
|
|
|
### Email Automation
|
|
|
|
Uses the community pattern from [authentik/discussions/13305](https://github.com/goauthentik/authentik/discussions/13305):
|
|
|
|
| Component | PK |
|
|
|-----------|-----|
|
|
| Expression policy (invitation-email-sender) | `770188de-2b60-4e87-820a-fa64d419ed89` |
|
|
| Notification rule (invitation-email-trigger) | `a145a8c9-b3eb-440d-8b77-27139c346a17` |
|
|
|
|
When an invitation is created with an `email` field in custom attributes, the expression policy triggers `ak_send_email()` to send the enrollment link to the invitee automatically.
|
|
|
|
### How to Use
|
|
|
|
**Invite via email** (Admin UI → Directory → Invitations → Create):
|
|
1. Name it, select **Invitation Enrollment** flow, toggle **Single use** on, set expiry
|
|
2. Custom attributes:
|
|
```yaml
|
|
name: Jane Smith
|
|
email: jane@example.com
|
|
```
|
|
3. Click Create — email is sent automatically
|
|
|
|
**Invite via link** (no email):
|
|
1. Same as above but leave custom attributes empty (or omit `email` field)
|
|
2. Click Create → expand the row → copy the invitation link
|
|
3. Send the link manually
|
|
|
|
---
|
|
|
|
## Branding & Theming
|
|
|
|
Echo6 cyberpunk branding applied to Authentik 2025.12.4 via System → Brands.
|
|
|
|
### Brand Settings
|
|
|
|
| Setting | Value |
|
|
|---------|-------|
|
|
| Brand title | `echo6` |
|
|
| Theme | `dark` (forced, not automatic) |
|
|
| Logo | `/media/custom/echo6-logo.png` (uploaded via Customization → Files) |
|
|
| Favicon | `/media/custom/echo6-favicon.png` (uploaded via Customization → Files) |
|
|
| Custom CSS | Echo6 Authentik CSS (~200 rules, applied via Brand → Custom CSS field) |
|
|
|
|
### Flow Titles
|
|
|
|
| Flow | Title |
|
|
|------|-------|
|
|
| Authentication | `echo6 // login` |
|
|
| Invalidation | `echo6 // logout` |
|
|
| Recovery | `echo6 // recovery` |
|
|
| User Settings | `echo6 // settings` |
|
|
|
|
### Custom CSS Highlights
|
|
|
|
- **Font:** JetBrains Mono globally (with `:not()` exclusions for FontAwesome/PatternFly icon fonts)
|
|
- **Colors:** Cyan `#28C0E8` primary accent, Yellow `#F0D848` secondary, dark backgrounds `#0a0e17`/`#111827`/`#1a2332`
|
|
- **Login card:** Dark background, cyan-glow focus on inputs, branded submit button
|
|
- **Admin sidebar:** Dark with cyan hover/active states
|
|
- **User dashboard:** 3-column grid layout, dark cards with cyan border on hover
|
|
- **Application icons:** Custom SVG icons uploaded for all 15 services
|
|
|
|
### CSS Storage
|
|
|
|
The custom CSS is stored in the Brand model's `branding_custom_css` field. To update:
|
|
|
|
```bash
|
|
# Copy CSS to Contabo
|
|
scp /path/to/echo6-authentik.css root@100.64.0.1:/opt/authentik/branding/custom.css
|
|
|
|
# Load into Brand model via ak shell
|
|
ssh root@100.64.0.1 'docker exec -i authentik-server ak shell' <<'PYEOF'
|
|
from authentik.brands.models import Brand
|
|
b = Brand.objects.get(domain="auth.echo6.co")
|
|
b.branding_custom_css = open("/media/custom/custom.css").read()
|
|
b.save()
|
|
PYEOF
|
|
|
|
# Restart to apply
|
|
ssh root@100.64.0.1 'cd /opt/authentik && docker compose restart server worker'
|
|
```
|
|
|
|
### SSO Launch URL Pattern
|
|
|
|
All authenticated service links use Authentik's application launch URL:
|
|
|
|
```
|
|
https://auth.echo6.co/application/launch/<app-slug>/
|
|
```
|
|
|
|
This provides seamless SSO: authenticated users pass through to the app, unauthenticated users get the login page then redirect to the app. Used by the SearXNG waffle menu and nav bar.
|
|
|
|
| App Slug | Service | Launch URL |
|
|
|----------|---------|-----------|
|
|
| open-webui | Aurora (AI) | `https://auth.echo6.co/application/launch/open-webui/` |
|
|
| peertube | Stream | `https://auth.echo6.co/application/launch/peertube/` |
|
|
| files | Files | `https://auth.echo6.co/application/launch/files/` |
|
|
| watchtower | Watchtower | `https://auth.echo6.co/application/launch/watchtower/` |
|
|
| immich | Photos | `https://auth.echo6.co/application/launch/immich/` |
|
|
| mailcow | Mail | `https://auth.echo6.co/application/launch/mailcow/` |
|
|
| nextcloud | Cloud | `https://auth.echo6.co/application/launch/nextcloud/` |
|
|
| jellyfin | Jellyfin | `https://auth.echo6.co/application/launch/jellyfin/` |
|
|
| jellyseer | Requests | `https://auth.echo6.co/application/launch/jellyseer/` |
|
|
|
|
### Brand Color Reference
|
|
|
|
| Color | Hex | Usage |
|
|
|-------|-----|-------|
|
|
| Cyan | `#28C0E8` | Primary accent, links, focus states |
|
|
| Cyan Light | `#5DD4F5` | Hover states |
|
|
| Yellow | `#F0D848` | Secondary accent, badges |
|
|
| BG Primary | `#0a0e17` | Page backgrounds |
|
|
| BG Secondary | `#111827` | Cards, inputs |
|
|
| BG Tertiary | `#1a2332` | Elevated surfaces |
|
|
| Border | `#1e3a5f` | All borders |
|
|
| Text Primary | `#e0e6ed` | Main text |
|
|
| Text Muted | `#7a8ca0` | Secondary text |
|
|
|
|
---
|
|
|
|
*Last updated: 2026-02-18 — Added LiveSync proxy provider (PK 17) + livesync-users group, Files Forward Auth (PK 16). Authentik 2025.12.4*
|