Deploy Authentik identity provider (C2 Mikado) (#227)
## Summary C2 Mikado chain for deploying Authentik as the SSO identity provider, replacing Dex. This PR will evolve over multiple sessions. Each iteration adds documentation (prerequisite cards) and eventually code as leaf nodes are resolved. ## Current Mikado State - **Goal:** `deploy-authentik` (active) - **Leaf prerequisites:** - `build-authentik-container` — Build Nix container image - `provision-authentik-database` — Create PostgreSQL database on CNPG cluster - `create-authentik-secrets` — Create 1Password item with credentials ## Process refinements - Updated agent-change-process with lessons from first attempt: reset code before committing cards, open PRs early ## Test plan - [ ] `mise run docs-mikado` shows correct dependency chain - [ ] Leaf nodes can be worked independently - [ ] Container builds on ringtail - [ ] Authentik starts and reaches healthy state - [ ] Forgejo OAuth2 connector works Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/227
This commit is contained in:
parent
174c6414ac
commit
71cb256527
46 changed files with 848 additions and 395 deletions
|
|
@ -68,7 +68,7 @@ Sync order: `1password-connect-ringtail` -> `external-secrets-crds-ringtail` ->
|
|||
| [[frigate]] | `frigate` | NVR with GPU-accelerated detection (RTX 4080) |
|
||||
| [[frigate]]-notify | `frigate` | MQTT-to-ntfy alert bridge |
|
||||
| Mosquitto | `mqtt` | MQTT broker for Frigate events |
|
||||
| [[dex]] | `dex` | OIDC identity provider (Forgejo-backed) |
|
||||
| [[authentik]] | `authentik` | OIDC identity provider |
|
||||
| [[ntfy]] | `ntfy` | Push notification server |
|
||||
| nvidia-device-plugin | `nvidia-device-plugin` | Exposes GPU to pods via CDI + nvidia RuntimeClass |
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ Individual service reference cards with URLs and configuration details.
|
|||
| [[zot]] | Container registry | indri |
|
||||
| [[devpi]] | PyPI caching proxy | k8s |
|
||||
| [[cv]] | Resume / CV site | k8s |
|
||||
| [[dex]] | OIDC identity provider | k8s (ringtail) |
|
||||
| [[authentik]] | OIDC identity provider | k8s (ringtail) |
|
||||
| [[docs]] | Documentation site (Quartz) | k8s |
|
||||
| [[flyio-proxy]] | Public reverse proxy (Fly.io + Tailscale) | Fly.io |
|
||||
| [[automounter]] | SMB share automounter | indri |
|
||||
|
|
|
|||
78
docs/reference/services/authentik.md
Normal file
78
docs/reference/services/authentik.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
title: Authentik
|
||||
modified: 2026-02-20
|
||||
tags:
|
||||
- service
|
||||
- security
|
||||
- oidc
|
||||
---
|
||||
|
||||
# Authentik
|
||||
|
||||
OIDC identity provider for BlumeOps. Authentik is the **source of truth** for user identity — users are created and managed in Authentik, and services authenticate against it via OIDC.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **URL** | https://authentik.ops.eblu.me |
|
||||
| **Admin UI** | https://authentik.ops.eblu.me/if/admin/ |
|
||||
| **Tailscale URL** | https://authentik.tail8d86e.ts.net |
|
||||
| **Namespace** | `authentik` |
|
||||
| **Cluster** | k3s (ringtail) |
|
||||
| **Image** | `registry.ops.eblu.me/blumeops/authentik:v1.1.2-nix` |
|
||||
| **Manifests** | `argocd/manifests/authentik/` |
|
||||
| **Container build** | `containers/authentik/default.nix` |
|
||||
|
||||
## Architecture
|
||||
|
||||
Authentik runs on [[ringtail]]'s k3s cluster, isolated from the main services on indri's minikube. This means the IdP is independent of the minikube cluster lifecycle.
|
||||
|
||||
Three deployments:
|
||||
- **server** — HTTP/HTTPS interface, handles OIDC flows
|
||||
- **worker** — Background tasks, blueprint application
|
||||
- **redis** — Caching, sessions, task queue
|
||||
|
||||
## Database
|
||||
|
||||
Uses the shared CNPG `blumeops-pg` cluster on [[indri]], accessed cross-cluster via `pg.ops.eblu.me:5432`. Database `authentik` with managed role.
|
||||
|
||||
## Blueprints
|
||||
|
||||
Authentik configuration is managed via Blueprints (YAML) stored as a ConfigMap mounted into the worker at `/blueprints/custom/`. Current blueprints define:
|
||||
|
||||
- `admins` group
|
||||
- Grafana OAuth2 provider (client ID: `grafana`)
|
||||
- Grafana application with group-based policy binding
|
||||
|
||||
Blueprint file: `argocd/manifests/authentik/configmap-blueprint.yaml`
|
||||
|
||||
## OIDC Clients
|
||||
|
||||
| Client | Status |
|
||||
|--------|--------|
|
||||
| [[grafana]] | Active |
|
||||
|
||||
Future clients: [[forgejo]], [[argocd]], [[miniflux]], [[zot]]
|
||||
|
||||
## Secrets
|
||||
|
||||
Injected via [[external-secrets]] from the "Authentik (blumeops)" 1Password item.
|
||||
|
||||
| 1Password Field | Purpose |
|
||||
|-----------------|---------|
|
||||
| `secret-key` | Authentik secret key |
|
||||
| `db-password` | PostgreSQL password |
|
||||
| `grafana-client-secret` | OIDC client secret for Grafana |
|
||||
| `api-token` | Authentik API token |
|
||||
|
||||
## Container Image
|
||||
|
||||
Nix-built via `dockerTools.buildLayeredImage`. The entrypoint wrapper symlinks built-in blueprint directories from the Nix store into `/blueprints/` at runtime, allowing custom blueprints to coexist with defaults. `AUTHENTIK_BLUEPRINTS_DIR=/blueprints` overrides the hardcoded Nix store path.
|
||||
|
||||
## Related
|
||||
|
||||
- [[federated-login]] - How authentication works across BlumeOps
|
||||
- [[grafana]] - First OIDC client
|
||||
- [[deploy-authentik]] - Deployment how-to
|
||||
- [[external-secrets]] - Secrets injection from 1Password
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
---
|
||||
title: Dex
|
||||
modified: 2026-02-19
|
||||
tags:
|
||||
- service
|
||||
- security
|
||||
- oidc
|
||||
---
|
||||
|
||||
# Dex
|
||||
|
||||
OIDC identity provider for BlumeOps. Dex federates authentication — downstream services (Grafana, future ArgoCD, etc.) delegate login to Dex, and Dex delegates to [[forgejo]] as the upstream OAuth2 provider.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **URL** | https://dex.ops.eblu.me |
|
||||
| **Tailscale URL** | https://dex.tail8d86e.ts.net |
|
||||
| **Namespace** | `dex` |
|
||||
| **Cluster** | k3s (ringtail) |
|
||||
| **Image** | `registry.ops.eblu.me/blumeops/dex:v1.0.0-nix` |
|
||||
| **Upstream** | https://github.com/dexidp/dex |
|
||||
| **Manifests** | `argocd/manifests/dex/` |
|
||||
| **Container build** | `containers/dex/default.nix` |
|
||||
|
||||
## Architecture
|
||||
|
||||
Dex runs on [[ringtail]]'s k3s cluster, isolated from the main services on indri's minikube. This means the IdP is independent of the minikube cluster lifecycle — if minikube goes down, Dex stays up and services can still authenticate once restored.
|
||||
|
||||
```
|
||||
User Browser
|
||||
|
|
||||
v
|
||||
Grafana (indri/minikube) --OIDC--> Dex (ringtail/k3s) --OAuth2--> Forgejo (indri/native)
|
||||
^ |
|
||||
| |
|
||||
+---------------------- redirect back with token -------------------+
|
||||
```
|
||||
|
||||
Cross-cluster communication works because Grafana reaches Dex via `https://dex.ops.eblu.me` (Caddy → Tailscale → ringtail), not k8s-internal DNS.
|
||||
|
||||
## Identity Source
|
||||
|
||||
Dex uses a **Gitea connector** pointed at [[forgejo]] (`https://forge.ops.eblu.me`). Users authenticate with their Forgejo credentials. There are no static passwords — user management happens entirely in Forgejo.
|
||||
|
||||
This means adding a new user to BlumeOps SSO is just creating a Forgejo account.
|
||||
|
||||
## Storage
|
||||
|
||||
SQLite3 with an `emptyDir` volume. This stores refresh tokens and auth codes. A pod restart invalidates active sessions (users re-login), which is acceptable for a homelab. No PVC needed.
|
||||
|
||||
## OIDC Clients
|
||||
|
||||
| Client | Redirect URIs | Status |
|
||||
|--------|---------------|--------|
|
||||
| [[grafana]] | `grafana.ops.eblu.me/login/generic_oauth`, `grafana.tail8d86e.ts.net/login/generic_oauth` | Active |
|
||||
|
||||
Future clients: [[argocd]], [[forgejo]], [[miniflux]], [[zot]]
|
||||
|
||||
## Secrets
|
||||
|
||||
All sensitive configuration is injected via [[external-secrets]] from the "Dex (blumeops)" 1Password item. The entire `config.yaml` is templated in the ExternalSecret — nothing sensitive is committed to git.
|
||||
|
||||
| 1Password Field | Purpose |
|
||||
|-----------------|---------|
|
||||
| `forgejo-client-id` | OAuth2 app client ID from Forgejo |
|
||||
| `forgejo-client-secret` | OAuth2 app client secret from Forgejo |
|
||||
| `grafana-client-secret` | OIDC client secret for Grafana |
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `/.well-known/openid-configuration` | OIDC discovery |
|
||||
| `/auth` | Authorization (browser redirect) |
|
||||
| `/token` | Token exchange |
|
||||
| `/userinfo` | User info |
|
||||
| `/keys` | JWKS (public keys) |
|
||||
| `/callback` | OAuth2 callback from Forgejo |
|
||||
| `/healthz` | Health check |
|
||||
|
||||
## Related
|
||||
|
||||
- [[federated-login]] - How authentication works across BlumeOps
|
||||
- [[forgejo]] - Upstream OAuth2 provider
|
||||
- [[grafana]] - First OIDC client
|
||||
- [[routing]] - How Dex is exposed via Caddy
|
||||
- [[external-secrets]] - Secrets injection from 1Password
|
||||
|
|
@ -77,11 +77,9 @@ The Ansible role authenticates to the Forgejo API using a Personal Access Token
|
|||
|
||||
This is a bootstrapping requirement - the PAT enables IaC for all other secrets.
|
||||
|
||||
## OAuth2 Provider for Dex
|
||||
## Identity Provider
|
||||
|
||||
Forgejo acts as the upstream OAuth2 provider for [[dex]], the BlumeOps OIDC identity provider. An OAuth2 application is registered in Forgejo's Site Administration with a redirect URI pointing to Dex's callback (`https://dex.ops.eblu.me/callback`). Client credentials are stored in 1Password ("Dex (blumeops)").
|
||||
|
||||
This means Forgejo accounts are the source of truth for BlumeOps SSO identity. Adding a user to any Dex-integrated service (currently [[grafana]]) is just creating a Forgejo account.
|
||||
[[authentik]] is the BlumeOps OIDC identity provider and source of truth for user identity. Forgejo will eventually authenticate against Authentik as an OIDC client, with user provisioning managed in Authentik. This migration is deferred — the existing `eblume` account has extensive automations that need careful migration.
|
||||
|
||||
## Future: Public Access
|
||||
|
||||
|
|
@ -104,5 +102,5 @@ See [[expose-service-publicly]] for the full howto and dynamic service checklist
|
|||
## Related
|
||||
|
||||
- [[argocd]] - Uses Forgejo as git source
|
||||
- [[dex]] - OIDC identity provider (Forgejo is the upstream OAuth2 source)
|
||||
- [[authentik]] - OIDC identity provider
|
||||
- [[zot]] - Container registry for built images
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ Dashboards and visualization for BlumeOps observability.
|
|||
|
||||
Grafana supports two login methods:
|
||||
|
||||
- **SSO via [[dex]]** — federated login through [[forgejo]] (`auth.generic_oauth`). Users click "Sign in with Dex", authenticate at Forgejo, and are redirected back as Admin.
|
||||
- **Local admin** — break-glass login using the password from 1Password ("Grafana (blumeops)"). Always available if Dex is down.
|
||||
- **SSO via [[authentik]]** — OIDC login through Authentik (`auth.generic_oauth`). Users click "Sign in with Authentik", authenticate at Authentik, and are redirected back as Admin.
|
||||
- **Local admin** — break-glass login using the password from 1Password ("Grafana (blumeops)"). Always available if Authentik is down.
|
||||
|
||||
The OIDC client secret is injected via [[external-secrets]] (`grafana-dex-oauth` secret in monitoring namespace).
|
||||
The OIDC client secret is injected via [[external-secrets]] (`grafana-authentik-oauth` secret in monitoring namespace).
|
||||
|
||||
## Datasources
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ Optional annotation: `grafana_folder: "FolderName"`
|
|||
|
||||
## Related
|
||||
|
||||
- [[dex]] - OIDC identity provider for SSO
|
||||
- [[authentik]] - OIDC identity provider for SSO
|
||||
- [[prometheus]] - Metrics datasource
|
||||
- [[loki]] - Logs datasource
|
||||
- [[alloy|Alloy]] - Data collector
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue