Migration: consolidate Echo6 docs to cortex with full infrastructure cleanup sync
- 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>
This commit is contained in:
parent
89834796ff
commit
e9231ac24a
93 changed files with 51223 additions and 254 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Location
|
||||
|
||||
- **Server:** Contabo (5.189.158.149 / 100.64.0.6)
|
||||
- **Server:** Contabo (5.189.158.149 / 100.64.0.1)
|
||||
- **URL:** https://auth.echo6.co
|
||||
- **Internal Port:** 9000
|
||||
|
||||
|
|
@ -10,6 +10,12 @@
|
|||
|
||||
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:
|
||||
|
|
@ -21,28 +27,88 @@ Required for OAuth2 provider creation:
|
|||
| 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.6 'docker exec authentik-server ak shell -c "
|
||||
from authentik.core.models import Token, User
|
||||
user = User.objects.get(username=\"akadmin\")
|
||||
token, created = Token.objects.get_or_create(
|
||||
identifier=\"token-name\",
|
||||
user=user,
|
||||
defaults={\"intent\": \"api\", \"expiring\": False}
|
||||
)
|
||||
print(token.key)
|
||||
"'
|
||||
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
|
||||
```
|
||||
|
||||
## Quick OAuth2 Provider Creation
|
||||
## Full OAuth2 App Setup (Provider + Application + Group + Bindings)
|
||||
|
||||
### Step 1: Create the OAuth2 provider
|
||||
|
||||
```bash
|
||||
# Source credentials
|
||||
source /home/zvx/projects/.ref/credentials
|
||||
|
||||
# Create provider
|
||||
curl -s -X POST "https://auth.echo6.co/api/v3/providers/oauth2/" \
|
||||
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
|
|
@ -50,13 +116,25 @@ curl -s -X POST "https://auth.echo6.co/api/v3/providers/oauth2/" \
|
|||
"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"
|
||||
"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
|
||||
```
|
||||
|
||||
# Create application (use pk from provider response)
|
||||
### 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" \
|
||||
|
|
@ -64,14 +142,232 @@ curl -s -X POST "https://auth.echo6.co/api/v3/core/applications/" \
|
|||
"name": "AppName",
|
||||
"slug": "appname",
|
||||
"provider": PROVIDER_PK,
|
||||
"meta_launch_url": "https://app.echo6.co"
|
||||
"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 | `https://app.echo6.co/callback` |
|
||||
| Web app (oauth) | `https://app.echo6.co/oauth/callback` |
|
||||
| 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*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue