Replace all production IPs (public, LAN, Tailscale), host port bindings, and hardcoded passwords/secrets across RUNBOOK.md, docs/, and projects/ with descriptive placeholders (<KSCLOUD1_PUBLIC_IP>, <port>, <KSCLOUD1_SUDO_PASSWORD>, etc.) so no sensitive infrastructure details are committed to the repository. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
269 lines
11 KiB
Markdown
269 lines
11 KiB
Markdown
# KiteStacks Authentik SSO Setup
|
|
|
|
**Established:** 2026-06-08
|
|
**Author:** kenpat
|
|
**Status:** In Progress — all providers/apps configured, Cloudflare tunnel updates pending
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
All services sit behind Cloudflare Tunnels on the `kitestacks` Docker network.
|
|
`cloudflared` routes external traffic directly to each service container by hostname.
|
|
Authentik (`authentik:9000`) is the single identity provider.
|
|
|
|
```
|
|
Internet → Cloudflare → cloudflared → [service container]
|
|
↕
|
|
Authentik auth check
|
|
```
|
|
|
|
**Two integration patterns are used:**
|
|
|
|
| Pattern | How it works | Services |
|
|
|---------|-------------|---------|
|
|
| Native OIDC/OAuth2 | App calls Authentik directly for login | Grafana, OpenWebUI, Forgejo, OpenProject, Kavita |
|
|
| Authentik Proxy Provider | Cloudflare tunnel → Authentik (embedded outpost) → service | Shaarli, Uptime Kuma, LiteLLM |
|
|
|
|
---
|
|
|
|
## SSO Status
|
|
|
|
| Service | Subdomain | Port | Method | Status |
|
|
|---------|-----------|------|--------|--------|
|
|
| Authentik | auth.kitestacks.com | <port> | (is the IdP) | ✅ Running |
|
|
| Grafana | grafana.kitestacks.com | <port> | OAuth2 | ✅ Configured |
|
|
| Kite AI (OpenWebUI) | ai.kitestacks.com | <port> | OIDC | ✅ Configured |
|
|
| Forgejo | gitforge.kitestacks.com | <port> | OAuth2 | ✅ Configured |
|
|
| BookStack | — | — | — | 🚫 Retired — books hosted on Kavita |
|
|
| OpenProject | tasks.kitestacks.com | <port> | OIDC | ✅ Configured, upgraded v13→v15 |
|
|
| Kavita | kavita.kitestacks.com | <port> | OIDC | ✅ Configured, secret filled |
|
|
| Shaarli | links.kitestacks.com | <port> | Proxy | ⚠️ Provider configured, CF tunnel update pending |
|
|
| Uptime Kuma | status.kitestacks.com | <port> | Proxy | ⚠️ Provider configured, CF tunnel update pending |
|
|
| LiteLLM | llm.kitestacks.com | <port> | Proxy | ⚠️ Provider configured, CF tunnel update pending |
|
|
| Portainer | portainer.kitestacks.com | <port> | — | 🚫 SSO excluded |
|
|
| Prometheus | prometheus.kitestacks.com | <port> | — | 🚫 SSO excluded |
|
|
| Node Exporter | node-exporter.kitestacks.com | <port> | — | 🚫 SSO excluded |
|
|
| OpenRouter | openrouter.ai | — | — | 🚫 external, excluded |
|
|
|
|
*BookStack has been retired. All books are hosted on Kavita (`kavita.kitestacks.com`).
|
|
|
|
---
|
|
|
|
## Files Changed (2026-06-08)
|
|
|
|
| File | What changed |
|
|
|------|-------------|
|
|
| `apps/authentik/docker-compose.yml` | Added `kitestacks` network declaration, `AUTHENTIK_ERROR_REPORTING__ENABLED: false` |
|
|
| `apps/kavita/config/appsettings.json` | `OpenIdConnectSettings.Enabled` → `true`, `Authority` set |
|
|
| `apps/bookstack/docker-compose.yml` | OIDC env vars added, `kitestacks` network added, `APP_URL` updated |
|
|
| `apps/bookstack/.env` | Created with `BOOKSTACK_OIDC_SECRET` placeholder |
|
|
| `apps/openproject/docker-compose.yml` | OIDC env vars added, `kitestacks` network declared |
|
|
| `apps/openproject/.env` | Created with `OPENPROJECT_OIDC_SECRET` placeholder |
|
|
|
|
---
|
|
|
|
## Step 1 — Authentik Admin UI: Create Providers and Applications
|
|
|
|
Go to **https://auth.kitestacks.com** → Admin Interface.
|
|
|
|
### For each native OIDC service, create one OAuth2/OpenID Provider:
|
|
|
|
**General settings (all providers):**
|
|
- Signing Key: select the default RS256 key
|
|
- Token validity: 10 minutes (access), 30 days (refresh)
|
|
|
|
#### 1. Grafana
|
|
|
|
- **Providers → Create → OAuth2/OpenID Provider**
|
|
- Name: `Grafana`
|
|
- Client type: `Confidential`
|
|
- Client ID: `grafana`
|
|
- Redirect URIs: `https://grafana.kitestacks.com/login/generic_oauth`
|
|
- Scopes: `openid`, `email`, `profile`
|
|
- **Applications → Create**
|
|
- Name: `Grafana`, Slug: `grafana`
|
|
- Provider: `Grafana` (above)
|
|
- Copy the **Client Secret** → put it in `/home/kenpat/docker/grafana/.env`:
|
|
```
|
|
GRAFANA_OAUTH_CLIENT_SECRET=<paste_secret>
|
|
```
|
|
- Restart: `cd ~/docker/grafana && docker compose up -d`
|
|
|
|
#### 2. Kite AI (Open WebUI)
|
|
|
|
- **Providers → Create → OAuth2/OpenID Provider**
|
|
- Name: `Open WebUI`, Client ID: `open-webui`
|
|
- Redirect URIs: `https://ai.kitestacks.com/oauth/oidc/callback`
|
|
- Scopes: `openid`, `email`, `profile`
|
|
- **Applications → Create**: Name: `Kite AI`, Slug: `open-webui`
|
|
- Copy secret → `/home/kenpat/docker/kite-ai/.env`:
|
|
```
|
|
OPENWEBUI_OAUTH_CLIENT_SECRET=<paste_secret>
|
|
```
|
|
- Restart: `cd ~/docker/kite-ai && docker compose up -d`
|
|
|
|
#### 3. Kavita
|
|
|
|
- **Providers → Create → OAuth2/OpenID Provider**
|
|
- Name: `Kavita`, Client ID: `kavita`
|
|
- Redirect URIs: `https://kavita.kitestacks.com/api/Account/OIDCCallback`
|
|
- Scopes: `openid`, `email`, `profile`
|
|
- **Applications → Create**: Name: `Kavita`, Slug: `kavita`
|
|
- Copy secret → `/home/kenpat/docker/kavita/config/appsettings.json`:
|
|
```json
|
|
"Secret": "<paste_secret>"
|
|
```
|
|
- Restart: `cd ~/docker/kavita && docker compose restart` (if a compose exists) or `docker restart kavita`
|
|
|
|
#### 4. OpenProject
|
|
|
|
- **Providers → Create → OAuth2/OpenID Provider**
|
|
- Name: `OpenProject`, Client ID: `openproject`
|
|
- Redirect URIs: `https://tasks.kitestacks.com/auth/oidc/callback`
|
|
- Scopes: `openid`, `email`, `profile`
|
|
- **Applications → Create**: Name: `OpenProject`, Slug: `openproject`
|
|
- Copy secret → `/home/kenpat/docker/openproject/.env`:
|
|
```
|
|
OPENPROJECT_OIDC_SECRET=<paste_secret>
|
|
```
|
|
- Restart: `cd ~/docker/openproject && docker compose up -d`
|
|
- **Note:** Container is currently running `openproject/community:13` but compose
|
|
specifies `openproject/openproject:15`. Recreation will upgrade it. Verify data
|
|
migration after restart. The Cloudflare tunnel for `tasks.kitestacks.com` may need
|
|
updating from `openproject:8080` → `openproject:80` after the upgrade.
|
|
|
|
---
|
|
|
|
### For proxy services, create Proxy Providers:
|
|
|
|
These services have no native OIDC. Authentik will act as the reverse proxy.
|
|
|
|
**After creating each Proxy Provider + Application, add all three to the Embedded Outpost:**
|
|
Outposts → `authentik Embedded Outpost` → Edit → move all three proxy applications to "Selected Applications".
|
|
|
|
#### 6. Shaarli
|
|
|
|
- **Providers → Create → Proxy Provider**
|
|
- Name: `Shaarli`
|
|
- Mode: `Reverse Proxy`
|
|
- External host: `https://links.kitestacks.com`
|
|
- Internal host: `http://shaarli:<port>`
|
|
- Internal host SSL validation: off
|
|
- **Applications → Create**: Name: `Shaarli`, Slug: `shaarli`
|
|
- **Cloudflare Tunnel**: Change `links.kitestacks.com` route from `http://shaarli:<port>` → `http://authentik:<port>`
|
|
|
|
#### 7. Uptime Kuma
|
|
|
|
- **Providers → Create → Proxy Provider**
|
|
- Name: `Uptime Kuma`
|
|
- Mode: `Reverse Proxy`
|
|
- External host: `https://status.kitestacks.com`
|
|
- Internal host: `http://uptime-kuma:<port>`
|
|
- **Applications → Create**: Name: `Uptime Kuma`, Slug: `uptime-kuma`
|
|
- **Cloudflare Tunnel**: Change `status.kitestacks.com` route from `http://uptime-kuma:<port>` → `http://authentik:<port>`
|
|
|
|
#### 8. LiteLLM *(when ready to expose publicly)*
|
|
|
|
- **Providers → Create → Proxy Provider**
|
|
- Name: `LiteLLM`
|
|
- Mode: `Reverse Proxy`
|
|
- External host: `https://llm.kitestacks.com`
|
|
- Internal host: `http://kite-litellm:<port>`
|
|
- **Applications → Create**: Name: `LiteLLM`, Slug: `litellm`
|
|
- **Cloudflare Tunnel**: Add route `llm.kitestacks.com` → `http://authentik:<port>`
|
|
|
|
---
|
|
|
|
## Step 2 — Forgejo: Add Authentik OAuth2 Source
|
|
|
|
Forgejo stores OAuth sources in its database (not in app.ini), so this requires the Forgejo admin UI.
|
|
|
|
1. Go to **https://gitforge.kitestacks.com** → Site Administration → Authentication Sources
|
|
2. Click **Add Authentication Source**
|
|
3. Settings:
|
|
- Authentication Type: `OAuth2`
|
|
- Authentication Name: `authentik`
|
|
- OAuth2 Provider: `OpenID Connect`
|
|
- Client ID: `forgejo`
|
|
- Client Secret: *(create an Authentik app first — see below)*
|
|
- OpenID Connect Auto Discovery URL:
|
|
`https://auth.kitestacks.com/application/o/forgejo/.well-known/openid-configuration`
|
|
- Additional scopes: `email profile`
|
|
4. Save
|
|
|
|
**Create the Authentik app for Forgejo first:**
|
|
- Provider: OAuth2/OpenID, Client ID: `forgejo`
|
|
- Redirect URI: `https://gitforge.kitestacks.com/user/oauth2/authentik/callback`
|
|
- Application slug: `forgejo`
|
|
|
|
---
|
|
|
|
## Step 3 — Cloudflare Tunnel Updates
|
|
|
|
In the Cloudflare Zero Trust Dashboard → Networks → Tunnels → your tunnel → Configure → Public Hostnames:
|
|
|
|
| Hostname | Change From | Change To |
|
|
|----------|------------|-----------|
|
|
| `links.kitestacks.com` | `http://shaarli:<port>` | `http://authentik:<port>` |
|
|
| `status.kitestacks.com` | `http://uptime-kuma:<port>` | `http://authentik:<port>` |
|
|
| `llm.kitestacks.com` | (new) | `http://authentik:<port>` |
|
|
| `tasks.kitestacks.com` | `http://openproject:<port>` | `http://openproject:<port>` *(after OpenProject upgrade)* |
|
|
|
|
---
|
|
|
|
## Step 4 — Restart Containers After Config Changes
|
|
|
|
After filling in all secrets in the `.env` files:
|
|
|
|
```bash
|
|
cd ~/docker/grafana && docker compose up -d
|
|
cd ~/docker/kite-ai && docker compose up -d
|
|
cd ~/docker/bookstack && docker compose up -d
|
|
cd ~/docker/openproject && docker compose up -d
|
|
cd ~/docker/authentik && docker compose up -d
|
|
docker restart kavita
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
**OIDC redirect loop / 400 error**
|
|
- Check that the Redirect URI in the Authentik provider exactly matches what the app sends.
|
|
- Grafana: `https://grafana.kitestacks.com/login/generic_oauth` (no trailing slash)
|
|
|
|
**Kavita "OIDC is not configured" message**
|
|
- The `appsettings.json` change requires a container restart: `docker restart kavita`
|
|
- Verify `Enabled: true` and `Secret` is not empty.
|
|
|
|
**BookStack redirects to `http://<MONK_LAN_IP>:<port>`**
|
|
- `APP_URL` must be updated to the real HTTPS domain and the container recreated.
|
|
|
|
**OpenProject: OIDC provider not visible in login**
|
|
- Env var format is version-specific. If env vars don't work in v15, configure via:
|
|
Administration → Authentication → OpenID Connect Providers → Add
|
|
|
|
**Proxy services (Shaarli/Uptime Kuma) show Authentik login but then 502**
|
|
- Verify the Embedded Outpost has the proxy providers assigned.
|
|
- Verify internal host is reachable: `docker exec authentik curl -v http://shaarli:<port>`
|
|
|
|
---
|
|
|
|
## Service-to-Container Name Reference
|
|
|
|
| Subdomain | Container Name | Internal Port |
|
|
|-----------|---------------|--------------|
|
|
| auth.kitestacks.com | authentik | <port> |
|
|
| grafana.kitestacks.com | grafana | <port> |
|
|
| ai.kitestacks.com | kite-openwebui | <port> |
|
|
| gitforge.kitestacks.com | forgejo | <port> |
|
|
| tasks.kitestacks.com | openproject | 80 (after upgrade) |
|
|
| kavita.kitestacks.com | kavita | <port> |
|
|
| links.kitestacks.com | shaarli | <port> |
|
|
| status.kitestacks.com | uptime-kuma | <port> |
|
|
| llm.kitestacks.com | kite-litellm | <port> |
|
|
| www.kitestacks.com | homepage | <port> |
|
|
| portainer.kitestacks.com | portainer | 9000 (excluded) |
|
|
| prometheus.kitestacks.com | prometheus | 9090 (excluded) |
|
|
| node-exporter.kitestacks.com | node-exporter | 9100 (excluded) |
|