- 6 docs racine (README, HANDOVER, ARCHITECTURE, DECISIONS, INSTALL, OPERATIONS) - 9 docs thématiques dans docs/ - 10 templates docker-compose dans configs/ - 7 scripts Python/Shell d'import Furious vers Zitadel - 2 matrices RBAC Etat infra au 5 juin 2026 : - 88 users Furious importes dans Zitadel - 8 roles RBAC crees et attribues - Intranet filtre les cartes selon role (Approche A) - 7 outils SSO operationnels
345 lines
13 KiB
Markdown
345 lines
13 KiB
Markdown
# ARCHITECTURE — Vue d'ensemble technique
|
|
|
|
> Comprendre comment tout s'imbrique dans l'infra Seize.
|
|
|
|
---
|
|
|
|
## 🏗️ Vue d'ensemble
|
|
|
|
```
|
|
Internet
|
|
│
|
|
│ HTTPS (443) / HTTP (80)
|
|
▼
|
|
┌──────────────────────┐
|
|
│ Traefik (proxy) │
|
|
│ - Let's Encrypt │
|
|
│ - Routing par Host │
|
|
└──────────┬───────────┘
|
|
│
|
|
┌──────────────┬─────────┼─────────┬──────────────┐
|
|
│ │ │ │ │
|
|
▼ ▼ ▼ ▼ ▼
|
|
┌──────────┐ ┌─────────────┐ ... ┌─────────────┐ ┌─────────────┐
|
|
│ Zitadel │ │ OAuth2-Proxy│ │ OAuth2-Proxy│ │ Gitea │
|
|
│ (sso.*) │ │ (intranet) │ │ (n8n) │ │ (git.*) SSO │
|
|
│ │ │ │ │ │ │ integré │
|
|
└────┬─────┘ └──────┬──────┘ └──────┬──────┘ └─────────────┘
|
|
│ │ │
|
|
│ Auth OIDC │ Forward proxy │ Forward proxy
|
|
│ ▼ ▼
|
|
│ ┌───────────┐ ┌───────────┐
|
|
│ │ Intranet │ │ n8n │
|
|
│ │ (nginx) │ │ │
|
|
│ │ + RBAC JS │ │ │
|
|
│ └───────────┘ └─────┬─────┘
|
|
│ │
|
|
│ │
|
|
▼ ▼
|
|
┌──────────┐ ┌──────────┐
|
|
│ Postgres │ │ Postgres │
|
|
│ (zitadel)│ │ (n8n) │
|
|
└──────────┘ └──────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 🌐 Réseaux Docker
|
|
|
|
| Réseau | Type | Utilisé par |
|
|
|---|---|---|
|
|
| `web` | external | Traefik, tous les services exposés publiquement |
|
|
| `backend` | external | Postgres, Redis, et services backend internes |
|
|
| `zitadel` | local | Zitadel + son Traefik dédié + Postgres |
|
|
| `proxy` | bridge | Réseau auxiliaire (vérifier usage) |
|
|
|
|
⚠️ **Important** : Zitadel a son **propre réseau** + son **propre Traefik** (intégré dans son docker-compose). Il est totalement isolé du Traefik principal pour des raisons de sécurité. Le Traefik principal route uniquement vers le Traefik Zitadel sur `sso.seizeconsulting.com`.
|
|
|
|
---
|
|
|
|
## 🔐 Flux d'authentification SSO
|
|
|
|
### Flux complet pour un user accédant à l'intranet
|
|
|
|
```
|
|
1. User → https://intranet.seizeconsulting.com
|
|
│
|
|
▼
|
|
2. Traefik principal → route vers OAuth2-Proxy "intranet"
|
|
│
|
|
▼
|
|
3. OAuth2-Proxy vérifie le cookie de session
|
|
│
|
|
┌────┴────┐
|
|
│ Pas de │
|
|
│ session │
|
|
▼ │
|
|
4. OAuth2-Proxy redirige vers Zitadel
|
|
(URL : /oauth2/start → Zitadel login)
|
|
│
|
|
▼
|
|
5. User saisit ses identifiants Zitadel + MFA
|
|
│
|
|
▼
|
|
6. Zitadel valide + génère un code OIDC
|
|
│
|
|
▼
|
|
7. Redirect vers OAuth2-Proxy /oauth2/callback?code=xxx
|
|
│
|
|
▼
|
|
8. OAuth2-Proxy échange le code contre un token JWT
|
|
(avec scope : openid email profile urn:zitadel:iam:org:project:roles)
|
|
│
|
|
▼
|
|
9. OAuth2-Proxy stocke le token + cookie de session
|
|
│
|
|
▼
|
|
10. OAuth2-Proxy → forward request vers nginx (intranet)
|
|
avec headers :
|
|
- X-Forwarded-User: <user_id>
|
|
- X-Forwarded-Email: hedi.kalboussi@seizeconsulting.com
|
|
- X-Forwarded-Preferred-Username: HKA
|
|
- Authorization: Bearer <jwt_token>
|
|
│
|
|
▼
|
|
11. nginx lit le header X-Forwarded-Email
|
|
Substitue __USER_EMAIL__ par la vraie valeur dans index.html (sub_filter)
|
|
│
|
|
▼
|
|
12. Browser reçoit l'HTML avec const USER_EMAIL = "hedi.kalboussi@..."
|
|
│
|
|
▼
|
|
13. JavaScript :
|
|
- Fetch /roles-mapping.json
|
|
- Lookup userEmail → role (ex: "consultant_junior")
|
|
- Cache les cartes que ce rôle n'a pas le droit de voir
|
|
│
|
|
▼
|
|
14. User voit uniquement les cartes autorisées
|
|
```
|
|
|
|
---
|
|
|
|
## 🗂️ Structure des dossiers Docker sur le serveur
|
|
|
|
```
|
|
/home/hedi/docker/
|
|
├── traefik/ # Reverse proxy principal
|
|
│ ├── docker-compose.yml
|
|
│ ├── traefik.yml # Config statique
|
|
│ ├── dynamic.yml # Config dynamique (middlewares, etc.)
|
|
│ └── letsencrypt/ # Certificats TLS auto
|
|
│
|
|
├── zitadel/ # IDP Zitadel (avec son propre Traefik)
|
|
│ ├── docker-compose.yml # Multi-services (proxy + login + api + db)
|
|
│ ├── .env # Tous les secrets Zitadel
|
|
│ └── machinekey/ # Clés service accounts
|
|
│
|
|
├── postgres/ # Postgres principal (Gitea, n8n, Authentik historique)
|
|
│ ├── docker-compose.yml
|
|
│ └── data/ # Persistance
|
|
│
|
|
├── redis/ # Plus utilisé (Authentik historique)
|
|
│
|
|
├── intranet/ # ⭐ Page d'accueil avec RBAC
|
|
│ ├── docker-compose.yml
|
|
│ ├── index.html # HTML avec data-app="xxx" + script RBAC
|
|
│ ├── roles-mapping.json # Mapping email → rôle (généré par script)
|
|
│ ├── nginx-rbac.conf # nginx avec sub_filter pour __USER_EMAIL__
|
|
│ └── logo.png
|
|
│
|
|
├── gitea/ # Forge Git avec SSO Zitadel intégré
|
|
│ ├── docker-compose.yml
|
|
│ ├── .env
|
|
│ └── data/
|
|
│
|
|
├── n8n/ # Workflows
|
|
│ ├── docker-compose.yml
|
|
│ ├── .env
|
|
│ └── data/
|
|
│
|
|
├── portainer/ # Gestion Docker
|
|
│ ├── docker-compose.yml
|
|
│ └── data/
|
|
│
|
|
├── code-server/ # VS Code dans le navigateur
|
|
│ ├── docker-compose.yml
|
|
│ ├── .env
|
|
│ └── config/
|
|
│
|
|
├── uptime-kuma/ # Monitoring
|
|
│ ├── docker-compose.yml
|
|
│ └── data/
|
|
│
|
|
├── oauth2-proxy/ # ⭐ OAuth2-Proxy pour l'intranet
|
|
│ ├── docker-compose.yml
|
|
│ └── .env
|
|
│
|
|
├── oauth2-proxy-n8n/ # OAuth2-Proxy pour n8n
|
|
│ ├── docker-compose.yml
|
|
│ └── .env
|
|
│
|
|
├── oauth2-proxy-portainer/ # OAuth2-Proxy pour Portainer
|
|
│ ├── docker-compose.yml
|
|
│ └── .env
|
|
│
|
|
├── oauth2-proxy-code/ # OAuth2-Proxy pour Code Server
|
|
│ ├── docker-compose.yml
|
|
│ └── .env
|
|
│
|
|
├── oauth2-proxy-status/ # OAuth2-Proxy pour Uptime Kuma
|
|
│ ├── docker-compose.yml
|
|
│ └── .env
|
|
│
|
|
├── authentik/ # ❌ Plus utilisé (à archiver/supprimer)
|
|
│
|
|
├── whoami/ # Test debug (à garder ou supprimer)
|
|
│
|
|
└── ctop/ # Outil CLI monitoring conteneurs
|
|
```
|
|
|
|
---
|
|
|
|
## 🎭 Le pattern "OAuth2-Proxy par outil"
|
|
|
|
### Pourquoi ce pattern ?
|
|
|
|
Chaque outil (n8n, Portainer, etc.) a son **propre OAuth2-Proxy dédié**. C'est volontaire :
|
|
|
|
✅ **Avantages** :
|
|
- Configuration différenciée par outil (scopes, callbacks, etc.)
|
|
- Pas de SPOF : si un OAuth2-Proxy crash, les autres outils marchent
|
|
- Plus simple à débugger
|
|
- Permet d'avoir des secrets/clients OIDC différents
|
|
|
|
❌ **Inconvénients** :
|
|
- 5 conteneurs OAuth2-Proxy en plus (impact RAM minime)
|
|
- 5 configurations à maintenir
|
|
|
|
### Anatomie d'un OAuth2-Proxy
|
|
|
|
Pour chaque outil, on a :
|
|
|
|
1. **Un dossier `~/docker/oauth2-proxy-<outil>/`**
|
|
2. **Un `docker-compose.yml`** avec labels Traefik routant `<outil>.seizeconsulting.com` → OAuth2-Proxy port 4180
|
|
3. **Un `.env`** avec :
|
|
- `OAUTH2_PROXY_PROVIDER=oidc`
|
|
- `OAUTH2_PROXY_CLIENT_ID=<id_zitadel>`
|
|
- `OAUTH2_PROXY_CLIENT_SECRET=<secret_zitadel>`
|
|
- `OAUTH2_PROXY_OIDC_ISSUER_URL=https://sso.seizeconsulting.com`
|
|
- `OAUTH2_PROXY_REDIRECT_URL=https://<outil>.seizeconsulting.com/oauth2/callback`
|
|
- `OAUTH2_PROXY_UPSTREAMS=http://<outil>:<port>`
|
|
- `OAUTH2_PROXY_COOKIE_SECRET=<32_chars_random>`
|
|
- `OAUTH2_PROXY_SCOPE=openid email profile urn:zitadel:iam:org:project:roles`
|
|
4. **Une App créée dans Zitadel** (project "Infra Seize") avec les bons callbacks
|
|
|
|
Détails complets dans [docs/04-oauth2-proxy-pattern.md](./docs/04-oauth2-proxy-pattern.md).
|
|
|
|
---
|
|
|
|
## 🧬 Le bug Traefik historique (à connaître)
|
|
|
|
**Symptôme initial** : tous les outils SSO étaient déconnectés à chaque requête.
|
|
|
|
**Cause** : Traefik supprimait par défaut le header `Cookie` quand il faisait un ForwardAuth vers OAuth2-Proxy. Du coup OAuth2-Proxy ne voyait pas le cookie de session et redirigeait vers login à chaque fois.
|
|
|
|
**Fix** : ajouter dans `~/docker/traefik/dynamic.yml` :
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
oauth2-headers:
|
|
forwardAuth:
|
|
address: "http://oauth2-proxy:4180/oauth2/auth"
|
|
authRequestHeaders:
|
|
- Cookie # ← LIGNE CRITIQUE
|
|
- Authorization
|
|
authResponseHeaders:
|
|
- X-Auth-Request-User
|
|
- X-Auth-Request-Email
|
|
```
|
|
|
|
→ Sans `Cookie` dans `authRequestHeaders`, **rien ne fonctionne**.
|
|
|
|
---
|
|
|
|
## 🎯 RBAC : architecture choisie (Approche A)
|
|
|
|
```
|
|
1. Furious Squad (source de vérité RH)
|
|
│
|
|
│ API GraphQL-like
|
|
▼
|
|
2. Scripts Python d'import (~/furious-to-zitadel/)
|
|
│
|
|
▼
|
|
3. Zitadel : 88 users avec metadata custom
|
|
│
|
|
▼
|
|
4. Script generate-roles-mapping.py
|
|
→ génère /docker/intranet/roles-mapping.json
|
|
│
|
|
▼
|
|
5. Intranet (nginx + sub_filter)
|
|
→ injecte __USER_EMAIL__ depuis X-Forwarded-Email
|
|
→ JavaScript lookup dans roles-mapping.json
|
|
→ cache/affiche les cartes selon rôle
|
|
```
|
|
|
|
### Pourquoi Approche A (et pas RBAC complet sur chaque outil) ?
|
|
|
|
Détails dans [DECISIONS.md](./DECISIONS.md), section "RBAC Approche A".
|
|
|
|
**TL;DR** : 0€, 2-3h de boulot, suffisant pour Seize (88 employés majoritairement non-techniques).
|
|
|
|
---
|
|
|
|
## 📊 Volumes critiques à backuper
|
|
|
|
| Données | Volume / Chemin | Criticité |
|
|
|---|---|---|
|
|
| Base PostgreSQL Zitadel | `~/docker/zitadel/.../data/postgres/` | 🔴 Critique |
|
|
| Base PostgreSQL Gitea/n8n | `~/docker/postgres/data/` | 🔴 Critique |
|
|
| Données Gitea (repos) | `~/docker/gitea/data/` | 🔴 Critique |
|
|
| Données Portainer | `~/docker/portainer/data/` | 🟡 Important |
|
|
| Données Uptime Kuma | `~/docker/uptime-kuma/data/` | 🟡 Important |
|
|
| Données n8n | volume Docker `n8n_data` | 🟡 Important |
|
|
| Données Code Server | `~/docker/code-server/config/` | 🟢 Récupérable |
|
|
| Certificats Let's Encrypt | `~/docker/traefik/letsencrypt/` | 🟢 Auto-régénéré |
|
|
| Scripts d'import + clés | `~/furious-to-zitadel/` | 🔴 Critique |
|
|
|
|
⚠️ **Aucun backup automatique en place** au moment de la passation. À mettre en priorité.
|
|
|
|
---
|
|
|
|
## 🔧 Outils CLI utiles installés
|
|
|
|
| Outil | Usage |
|
|
|---|---|
|
|
| `docker` | Gestion conteneurs |
|
|
| `docker compose` | Orchestration |
|
|
| `ctop` | Top des conteneurs (consommation ressources) |
|
|
| `sudo journalctl` | Logs système |
|
|
| `htop` | Top processus |
|
|
| `python3` + venv | Pour les scripts d'import |
|
|
|
|
---
|
|
|
|
## 🌍 DNS — Records configurés (chez le registrar)
|
|
|
|
Tous les sous-domaines pointent vers `151.115.148.243` :
|
|
|
|
| Record | Type | Valeur |
|
|
|---|---|---|
|
|
| `seizeconsulting.com` | A | 151.115.148.243 |
|
|
| `sso.seizeconsulting.com` | A | 151.115.148.243 |
|
|
| `intranet.seizeconsulting.com` | A | 151.115.148.243 |
|
|
| `git.seizeconsulting.com` | A | 151.115.148.243 |
|
|
| `n8n.seizeconsulting.com` | A | 151.115.148.243 |
|
|
| `portainer.seizeconsulting.com` | A | 151.115.148.243 |
|
|
| `code.seizeconsulting.com` | A | 151.115.148.243 |
|
|
| `status.seizeconsulting.com` | A | 151.115.148.243 |
|
|
| `traefik.seizeconsulting.com` | A | 151.115.148.243 |
|
|
| `app.seizeconsulting.com` | A | 151.115.148.243 |
|
|
|
|
⚠️ **Ne PAS oublier** d'ajouter un record DNS si tu ajoutes un nouvel outil. Sinon Let's Encrypt échoue.
|