feat(auth): Authentik en hub utilisateur public — SSO de bout en bout (#82)

L'entrée unique devient la page « My applications » d'Authentik
(kaya.freeboxos.fr:9081) : login → tuiles des apps autorisées → SSO
vers Guacamole (:9080). Plus de comptes locaux Guacamole.

- Services LoadBalancer dédiés (MetalLB .201 Authentik / .202 Guacamole) :
  le DNAT par port ne peut pas router par Host derrière Traefik :80
- gateway : DNAT 9081→.201:9000 et 9080→.202:8080 + rate-limit par VIP
- Guacamole : endpoints OIDC sur l'URL publique canonique (LAN inclus,
  hairpin Freebox) + retour au SSO direct (openid prioritaire)
- doc : section « Accès externe » réécrite (hub, chaîne, config provider)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
ALI YESILKAYA 2026-07-07 11:25:56 +02:00 committed by GitHub
parent cf09e404ef
commit 5a4190faaa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 92 additions and 40 deletions

View file

@ -21,7 +21,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- ✅ SearXNG opérationnel — `searxng.lab.local`, namespace `ai`, **méta-moteur de recherche web self-host** (privé, in-cluster) consommé par l'outil `web_search` d'Asa (API JSON). Manifests `k8s/apps/searxng/` - ✅ SearXNG opérationnel — `searxng.lab.local`, namespace `ai`, **méta-moteur de recherche web self-host** (privé, in-cluster) consommé par l'outil `web_search` d'Asa (API JSON). Manifests `k8s/apps/searxng/`
- ✅ Ghostfolio opérationnel — `ghostfolio.lab.local`, namespace `ai`, suivi de portefeuille (PostgreSQL s01 + Redis in-cluster) - ✅ Ghostfolio opérationnel — `ghostfolio.lab.local`, namespace `ai`, suivi de portefeuille (PostgreSQL s01 + Redis in-cluster)
- ✅ hermes-auto-improve — analyse quotidienne de `admin/` par Hermes à 22h (n8n → trigger :9095), PR sur `hermes/daily-work` - ✅ hermes-auto-improve — analyse quotidienne de `admin/` par Hermes à 22h (n8n → trigger :9095), PR sur `hermes/daily-work`
- 🆕 Annuaire + portail consoles (2026-07-06) — **Authentik** (`auth.lab.local`, IdP OIDC) + **Guacamole** (`portail.lab.local`, consoles SSH web vers s01/g01 via user `console` sans sudo), namespace `auth`. Config initiale (admin Authentik, provider OIDC, connexions) : voir `admin/k8s/auth-portal.md`. **Accès Internet** : `kaya.freeboxos.fr:9080` (comptes locaux Guacamole, Authentik non exposé, rate-limit nftables) - 🆕 Annuaire + portail consoles (2026-07-06) — **Authentik** (`auth.lab.local`, IdP OIDC) + **Guacamole** (`portail.lab.local`, consoles SSH web vers s01/g01 via user `console` sans sudo), namespace `auth`. Config initiale (admin Authentik, provider OIDC, connexions) : voir `admin/k8s/auth-portal.md`. **Accès Internet** : Authentik = hub utilisateur (`kaya.freeboxos.fr:9081`, page « My applications ») → SSO vers Guacamole (`:9080`) — VIPs MetalLB `.201`/`.202`, rate-limit nftables, URL canonique publique pour tous (hairpin LAN)
- 🆕 WireGuard (2026-07-06) — serveur VPN prêt sur s01 (pairs `full`/`portal`, filtrage nftables) mais **dormant** : pas de redirection Freebox `51820/udp` (choix simplicité 2026-07-07, accès portail via `:9080` direct). Voir `admin/infra/wireguard.md` - 🆕 WireGuard (2026-07-06) — serveur VPN prêt sur s01 (pairs `full`/`portal`, filtrage nftables) mais **dormant** : pas de redirection Freebox `51820/udp` (choix simplicité 2026-07-07, accès portail via `:9080` direct). Voir `admin/infra/wireguard.md`
- ⏳ WOL compute nodes non configuré (BIOS) — allumage manuel requis - ⏳ WOL compute nodes non configuré (BIOS) — allumage manuel requis

View file

@ -85,22 +85,35 @@ kubectl -n auth create secret generic guacamole-secret \
3. Settings → Users : les comptes Authentik apparaissent après leur **premier login** 3. Settings → Users : les comptes Authentik apparaissent après leur **premier login**
(`POSTGRESQL_AUTO_CREATE_ACCOUNTS=true`) → leur assigner les connexions (READ). (`POSTGRESQL_AUTO_CREATE_ACCOUNTS=true`) → leur assigner les connexions (READ).
## Accès externe (Internet) — `kaya.freeboxos.fr:9080` ## Accès externe (Internet) — Authentik en hub utilisateur
Le portail est joignable depuis Internet **sans VPN** (choix assumé 2026-07-07, **Authentik est la porte d'entrée** (page « My applications » : une tuile par app
simplicité d'abord — le VPN WireGuard reste dispo, cf. `admin/infra/wireguard.md`) : autorisée), le SSO fait le reste. Choix assumé 2026-07-07 : exposition directe sans
VPN, simplicité d'abord (WireGuard reste dispo, cf. `admin/infra/wireguard.md`).
- **Chaîne** : `kaya.freeboxos.fr:9080` → redirection Freebox `9080/tcp → funk-s01` | URL publique | Service | Chaîne |
→ DNAT nftables `→ 192.168.10.200:80` (Traefik) → IngressRoute (host |---|---|---|
`kaya.freeboxos.fr` accepté) → Guacamole | `http://kaya.freeboxos.fr:9081` | **Authentik** (hub + login) | Freebox `9081/tcp → s01` → DNAT → VIP MetalLB `192.168.10.201:9000` |
- **Auth externe = comptes locaux Guacamole** (Settings → Users → New user + mot de | `http://kaya.freeboxos.fr:9080` | Guacamole (via tuile SSO) | Freebox `9080/tcp → s01` → DNAT → VIP `192.168.10.202:8080` |
passe fort + cocher ses connexions). **Authentik n'est PAS exposé** : le SSO ne
fonctionne que du LAN (le lien « OpenID » échoue depuis Internet — normal). - **URL canonique unique** : les endpoints OIDC de Guacamole pointent sur
- **Garde-fous** : rate-limit nftables (30 connexions neuves/min) + anti-bruteforce `kaya.freeboxos.fr`**tout le monde, LAN inclus**, doit utiliser ces URLs (le LAN
Guacamole (5 échecs → ~5 min) + mots de passe forts obligatoires. ⚠️ HTTP **non repasse par la Freebox, hairpin NAT). `portail.lab.local` affiche encore l'UI mais
chiffré** : mots de passe des comptes locaux interceptables en théorie — ne pas le SSO ramène sur l'URL publique.
réutiliser un mot de passe important, et envisager TLS (vrai domaine + Let's - Les VIPs dédiées **contournent Traefik** : un DNAT par port ne peut pas router par
Encrypt) ou l'option TOTP de Guacamole si l'usage s'installe. Host sur l'entrypoint unique :80.
- **Un seul compte par personne (Authentik)** — plus de comptes locaux Guacamole.
Parcours ami : `kaya.freeboxos.fr:9081` → login → tuile « Portail consoles » →
console. Nouvelle app plus tard = nouveau provider + tuile, zéro compte en plus.
- **Garde-fous** : rate-limit nftables (30 connexions neuves/min par service) +
protections applicatives (Authentik : flows/lockout ; Guacamole : ban 5 échecs).
⚠️ HTTP **non chiffré** : mots de passe interceptables en théorie — mots de passe
dédiés, MFA/TOTP Authentik recommandé pour durcir, TLS (vrai domaine + Let's
Encrypt) si l'usage s'installe.
Config côté Authentik (UI) : provider `guacamole` → **Redirect URI
`http://kaya.freeboxos.fr:9080/`** ; application « Portail consoles » → **Launch URL
`http://kaya.freeboxos.fr:9080/`**.
## Pièges / notes ## Pièges / notes
@ -110,12 +123,11 @@ simplicité d'abord — le VPN WireGuard reste dispo, cf. `admin/infra/wireguard
serveur→serveur de Guacamole ne dépend pas de la résolution `lab.local` des pods. serveur→serveur de Guacamole ne dépend pas de la résolution `lab.local` des pods.
L'issuer, lui, reste `http://auth.lab.local/...` (doit matcher le claim `iss` des L'issuer, lui, reste `http://auth.lab.local/...` (doit matcher le claim `iss` des
tokens émis via le navigateur). tokens émis via le navigateur).
3. **`EXTENSION_PRIORITY="*, openid"`** : formulaire local affiché en premier (requis 3. **`EXTENSION_PRIORITY="openid, *"`** : arriver sur Guacamole = redirection
pour les comptes locaux de l'accès Internet), lien « OpenID » en dessous pour le SSO immédiate vers Authentik (le hub). Conséquence : `guacadmin` inaccessible —
LAN. La variante `"openid, *"` (redirection SSO immédiate, testée puis abandonnée l'admin passe par le compte SSO `Alkatrazz` (« Administer system »). Dépannage si
le 2026-07-07) est incompatible avec l'accès externe sans exposer Authentik. Authentik est HS : remettre `"*, openid"` dans le deployment + re-sync (le
NB : le compte SSO `Alkatrazz` a « Administer system » — admin possible par les formulaire local revient).
deux chemins.
4. **Le slug de l'application Authentik doit être exactement `guacamole`** : Authentik 4. **Le slug de l'application Authentik doit être exactement `guacamole`** : Authentik
le génère depuis le nom (« Portail consoles » → `portail-consoles`) et l'issuer/JWKS le génère depuis le nom (« Portail consoles » → `portail-consoles`) et l'issuer/JWKS
deviennent faux → log Guacamole `Non 200 status code (404) ... /o/guacamole/jwks/` deviennent faux → log Guacamole `Non 200 status code (404) ... /o/guacamole/jwks/`

View file

@ -77,12 +77,14 @@ table inet filter {
ip daddr 192.168.10.20 tcp dport { 7777, 8888 } ct state new,established,related accept ip daddr 192.168.10.20 tcp dport { 7777, 8888 } ct state new,established,related accept
ip daddr 192.168.10.20 udp dport 7777 ct state new,established,related accept ip daddr 192.168.10.20 udp dport 7777 ct state new,established,related accept
# Portail public — trafic Internet DNATé (9080 → Traefik :80). # Hub public Authentik (:9081) + portail Guacamole (:9080) — trafic Internet
# Rate-limit sur les connexions neuves : anti-bruteforce/scan réseau, # DNATé vers les VIPs MetalLB dédiées. Rate-limit sur les connexions neuves :
# en plus de la protection intégrée de Guacamole. Le LAN domestique n'est # anti-bruteforce/scan réseau, en plus des protections applicatives. Le LAN
# pas concerné (accepté par la règle 192.168.1.0/24 plus haut). # domestique n'est pas concerné (règle 192.168.1.0/24 plus haut, hairpin
ip daddr 192.168.10.200 tcp dport 80 ct state new limit rate 30/minute burst 15 packets accept # Freebox inclus).
ip daddr 192.168.10.200 tcp dport 80 ct state established,related accept ip daddr 192.168.10.201 tcp dport 9000 ct state new limit rate 30/minute burst 15 packets accept
ip daddr 192.168.10.202 tcp dport 8080 ct state new limit rate 30/minute burst 15 packets accept
ip daddr { 192.168.10.201, 192.168.10.202 } ct state established,related accept
# WireGuard → lab : filtrage par pair (host_vars wireguard_peers) # WireGuard → lab : filtrage par pair (host_vars wireguard_peers)
{% for peer in wireguard_peers | selectattr('access', 'equalto', 'full') | list %} {% for peer in wireguard_peers | selectattr('access', 'equalto', 'full') | list %}
@ -111,8 +113,10 @@ table ip nat {
iif {{ wan_interface }} tcp dport { 7777, 8888 } dnat to 192.168.10.20 iif {{ wan_interface }} tcp dport { 7777, 8888 } dnat to 192.168.10.20
iif {{ wan_interface }} udp dport 7777 dnat to 192.168.10.20:7777 iif {{ wan_interface }} udp dport 7777 dnat to 192.168.10.20:7777
# Portail Guacamole public — kaya.freeboxos.fr:9080 (redirection Freebox → s01) # Hub Authentik + portail Guacamole publics (redirections Freebox → s01)
iif {{ wan_interface }} tcp dport 9080 dnat to 192.168.10.200:80 # kaya.freeboxos.fr:9081 → Authentik (VIP .201) ; :9080 → Guacamole (VIP .202)
iif {{ wan_interface }} tcp dport 9081 dnat to 192.168.10.201:9000
iif {{ wan_interface }} tcp dport 9080 dnat to 192.168.10.202:8080
} }
chain postrouting { chain postrouting {

View file

@ -9,3 +9,21 @@ spec:
ports: ports:
- port: 9000 - port: 9000
targetPort: 9000 targetPort: 9000
---
# VIP MetalLB dédiée — exposition Internet via DNAT s01 (kaya.freeboxos.fr:9081).
# Bypass Traefik : le DNAT par port ne peut pas router par Host derrière un
# entrypoint unique.
apiVersion: v1
kind: Service
metadata:
name: authentik-external
namespace: auth
annotations:
metallb.io/loadBalancerIPs: 192.168.10.201
spec:
type: LoadBalancer
selector:
app: authentik-server
ports:
- port: 9000
targetPort: 9000

View file

@ -42,28 +42,30 @@ spec:
# Servir sur / plutôt que /guacamole # Servir sur / plutôt que /guacamole
- name: WEBAPP_CONTEXT - name: WEBAPP_CONTEXT
value: "ROOT" value: "ROOT"
# --- SSO OIDC → Authentik (app « guacamole » à créer dans l'UI Authentik) --- # --- SSO OIDC → Authentik (app « guacamole » dans l'UI Authentik) ---
# Endpoints navigateur : via l'IngressRoute ; JWKS : appel serveur→serveur, # Endpoints navigateur : URL publique kaya.freeboxos.fr (une seule URL
# on passe par le Service interne pour ne pas dépendre du DNS lab.local des pods # canonique, LAN inclus — hairpin NAT Freebox) ; JWKS : appel
# serveur→serveur via le Service interne
- name: OPENID_AUTHORIZATION_ENDPOINT - name: OPENID_AUTHORIZATION_ENDPOINT
value: "http://auth.lab.local/application/o/authorize/" value: "http://kaya.freeboxos.fr:9081/application/o/authorize/"
- name: OPENID_JWKS_ENDPOINT - name: OPENID_JWKS_ENDPOINT
value: "http://authentik.auth.svc.cluster.local:9000/application/o/guacamole/jwks/" value: "http://authentik.auth.svc.cluster.local:9000/application/o/guacamole/jwks/"
- name: OPENID_ISSUER - name: OPENID_ISSUER
value: "http://auth.lab.local/application/o/guacamole/" value: "http://kaya.freeboxos.fr:9081/application/o/guacamole/"
- name: OPENID_CLIENT_ID - name: OPENID_CLIENT_ID
value: "guacamole" value: "guacamole"
- name: OPENID_REDIRECT_URI - name: OPENID_REDIRECT_URI
value: "http://portail.lab.local/" value: "http://kaya.freeboxos.fr:9080/"
- name: OPENID_USERNAME_CLAIM_TYPE - name: OPENID_USERNAME_CLAIM_TYPE
value: "preferred_username" value: "preferred_username"
- name: OPENID_SCOPE - name: OPENID_SCOPE
value: "openid profile email" value: "openid profile email"
# Formulaire local en premier : requis pour l'accès Internet # SSO direct : Authentik est le hub utilisateurs (page « My applications »,
# (kaya.freeboxos.fr:9080 — comptes locaux Guacamole, Authentik n'étant # exposée en :9081) — arriver sur Guacamole = redirection immédiate vers lui.
# pas exposé). Le lien « OpenID » reste dispo pour le SSO depuis le LAN. # guacadmin inaccessible : l'admin passe par le compte SSO « Administer
# system » (Alkatrazz). Dépannage sans Authentik : "*, openid" + re-sync.
- name: EXTENSION_PRIORITY - name: EXTENSION_PRIORITY
value: "*, openid" value: "openid, *"
- name: TZ - name: TZ
value: "Europe/Paris" value: "Europe/Paris"
resources: resources:

View file

@ -7,7 +7,7 @@ spec:
entryPoints: entryPoints:
- web - web
routes: routes:
- match: Host(`portail.lab.local`) || Host(`kaya.freeboxos.fr`) - match: Host(`portail.lab.local`)
kind: Rule kind: Rule
services: services:
- name: guacamole - name: guacamole

View file

@ -9,3 +9,19 @@ spec:
ports: ports:
- port: 8080 - port: 8080
targetPort: 8080 targetPort: 8080
---
# VIP MetalLB dédiée — exposition Internet via DNAT s01 (kaya.freeboxos.fr:9080)
apiVersion: v1
kind: Service
metadata:
name: guacamole-external
namespace: auth
annotations:
metallb.io/loadBalancerIPs: 192.168.10.202
spec:
type: LoadBalancer
selector:
app: guacamole
ports:
- port: 8080
targetPort: 8080