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

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:
alkatrazz 2026-07-07 11:25:07 +02:00
parent cf09e404ef
commit 84778632b3
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/`
- ✅ 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`
- 🆕 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`
- ⏳ 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**
(`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,
simplicité d'abord — le VPN WireGuard reste dispo, cf. `admin/infra/wireguard.md`) :
**Authentik est la porte d'entrée** (page « My applications » : une tuile par app
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`
→ DNAT nftables `→ 192.168.10.200:80` (Traefik) → IngressRoute (host
`kaya.freeboxos.fr` accepté) → Guacamole
- **Auth externe = comptes locaux Guacamole** (Settings → Users → New user + mot de
passe fort + cocher ses connexions). **Authentik n'est PAS exposé** : le SSO ne
fonctionne que du LAN (le lien « OpenID » échoue depuis Internet — normal).
- **Garde-fous** : rate-limit nftables (30 connexions neuves/min) + anti-bruteforce
Guacamole (5 échecs → ~5 min) + mots de passe forts obligatoires. ⚠️ HTTP **non
chiffré** : mots de passe des comptes locaux interceptables en théorie — ne pas
réutiliser un mot de passe important, et envisager TLS (vrai domaine + Let's
Encrypt) ou l'option TOTP de Guacamole si l'usage s'installe.
| URL publique | Service | Chaîne |
|---|---|---|
| `http://kaya.freeboxos.fr:9081` | **Authentik** (hub + login) | Freebox `9081/tcp → s01` → DNAT → VIP MetalLB `192.168.10.201:9000` |
| `http://kaya.freeboxos.fr:9080` | Guacamole (via tuile SSO) | Freebox `9080/tcp → s01` → DNAT → VIP `192.168.10.202:8080` |
- **URL canonique unique** : les endpoints OIDC de Guacamole pointent sur
`kaya.freeboxos.fr`**tout le monde, LAN inclus**, doit utiliser ces URLs (le LAN
repasse par la Freebox, hairpin NAT). `portail.lab.local` affiche encore l'UI mais
le SSO ramène sur l'URL publique.
- Les VIPs dédiées **contournent Traefik** : un DNAT par port ne peut pas router par
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
@ -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.
L'issuer, lui, reste `http://auth.lab.local/...` (doit matcher le claim `iss` des
tokens émis via le navigateur).
3. **`EXTENSION_PRIORITY="*, openid"`** : formulaire local affiché en premier (requis
pour les comptes locaux de l'accès Internet), lien « OpenID » en dessous pour le SSO
LAN. La variante `"openid, *"` (redirection SSO immédiate, testée puis abandonnée
le 2026-07-07) est incompatible avec l'accès externe sans exposer Authentik.
NB : le compte SSO `Alkatrazz` a « Administer system » — admin possible par les
deux chemins.
3. **`EXTENSION_PRIORITY="openid, *"`** : arriver sur Guacamole = redirection
immédiate vers Authentik (le hub). Conséquence : `guacadmin` inaccessible —
l'admin passe par le compte SSO `Alkatrazz` (« Administer system »). Dépannage si
Authentik est HS : remettre `"*, openid"` dans le deployment + re-sync (le
formulaire local revient).
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
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 udp dport 7777 ct state new,established,related accept
# Portail public — trafic Internet DNATé (9080 → Traefik :80).
# Rate-limit sur les connexions neuves : anti-bruteforce/scan réseau,
# en plus de la protection intégrée de Guacamole. Le LAN domestique n'est
# pas concerné (accepté par la règle 192.168.1.0/24 plus haut).
ip daddr 192.168.10.200 tcp dport 80 ct state new limit rate 30/minute burst 15 packets accept
ip daddr 192.168.10.200 tcp dport 80 ct state established,related accept
# Hub public Authentik (:9081) + portail Guacamole (:9080) — trafic Internet
# DNATé vers les VIPs MetalLB dédiées. Rate-limit sur les connexions neuves :
# anti-bruteforce/scan réseau, en plus des protections applicatives. Le LAN
# domestique n'est pas concerné (règle 192.168.1.0/24 plus haut, hairpin
# Freebox inclus).
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)
{% 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 }} udp dport 7777 dnat to 192.168.10.20:7777
# Portail Guacamole public — kaya.freeboxos.fr:9080 (redirection Freebox → s01)
iif {{ wan_interface }} tcp dport 9080 dnat to 192.168.10.200:80
# Hub Authentik + portail Guacamole publics (redirections Freebox → s01)
# 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 {

View file

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

View file

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

View file

@ -9,3 +9,19 @@ spec:
ports:
- port: 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