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>
11 KiB
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 | (is the IdP) | ✅ Running | |
| Grafana | grafana.kitestacks.com | OAuth2 | ✅ Configured | |
| Kite AI (OpenWebUI) | ai.kitestacks.com | OIDC | ✅ Configured | |
| Forgejo | gitforge.kitestacks.com | OAuth2 | ✅ Configured | |
| BookStack | — | — | — | 🚫 Retired — books hosted on Kavita |
| OpenProject | tasks.kitestacks.com | OIDC | ✅ Configured, upgraded v13→v15 | |
| Kavita | kavita.kitestacks.com | OIDC | ✅ Configured, secret filled | |
| Shaarli | links.kitestacks.com | Proxy | ⚠️ Provider configured, CF tunnel update pending | |
| Uptime Kuma | status.kitestacks.com | Proxy | ⚠️ Provider configured, CF tunnel update pending | |
| LiteLLM | llm.kitestacks.com | Proxy | ⚠️ Provider configured, CF tunnel update pending | |
| Portainer | portainer.kitestacks.com | — | 🚫 SSO excluded | |
| Prometheus | prometheus.kitestacks.com | — | 🚫 SSO excluded | |
| Node Exporter | node-exporter.kitestacks.com | — | 🚫 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
- Name:
- Applications → Create
- Name:
Grafana, Slug:grafana - Provider:
Grafana(above)
- Name:
- 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
- Name:
- 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
- Name:
- Applications → Create: Name:
Kavita, Slug:kavita - Copy secret →
/home/kenpat/docker/kavita/config/appsettings.json:"Secret": "<paste_secret>" - Restart:
cd ~/docker/kavita && docker compose restart(if a compose exists) ordocker 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
- Name:
- 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:13but compose specifiesopenproject/openproject:15. Recreation will upgrade it. Verify data migration after restart. The Cloudflare tunnel fortasks.kitestacks.commay need updating fromopenproject:8080→openproject:80after the upgrade.
- Note: Container is currently running
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
- Name:
- Applications → Create: Name:
Shaarli, Slug:shaarli - Cloudflare Tunnel: Change
links.kitestacks.comroute fromhttp://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>
- Name:
- Applications → Create: Name:
Uptime Kuma, Slug:uptime-kuma - Cloudflare Tunnel: Change
status.kitestacks.comroute fromhttp://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>
- Name:
- 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.
- Go to https://gitforge.kitestacks.com → Site Administration → Authentication Sources
- Click Add Authentication Source
- 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
- Authentication Type:
- 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:
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.jsonchange requires a container restart:docker restart kavita - Verify
Enabled: trueandSecretis not empty.
BookStack redirects to http://<MONK_LAN_IP>:<port>
APP_URLmust 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 | |
| grafana.kitestacks.com | grafana | |
| ai.kitestacks.com | kite-openwebui | |
| gitforge.kitestacks.com | forgejo | |
| tasks.kitestacks.com | openproject | 80 (after upgrade) |
| kavita.kitestacks.com | kavita | |
| links.kitestacks.com | shaarli | |
| status.kitestacks.com | uptime-kuma | |
| llm.kitestacks.com | kite-litellm | |
| www.kitestacks.com | homepage | |
| portainer.kitestacks.com | portainer | 9000 (excluded) |
| prometheus.kitestacks.com | prometheus | 9090 (excluded) |
| node-exporter.kitestacks.com | node-exporter | 9100 (excluded) |