seize-infra-doc/OPERATIONS.md
Hedi Kalboussi 1e90a85359 Documentation initiale infra Seize — passation J7
- 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
2026-06-05 17:49:43 +00:00

513 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# OPERATIONS — Maintenance et opérations courantes
> Procédures pour maintenir, dépanner, et faire évoluer l'infrastructure Seize.
---
## 🔍 Vérifications quotidiennes (5 min/jour)
### Healthcheck rapide
```bash
# Tous les conteneurs sont-ils Up ?
sudo docker ps --format "table {{.Names}}\t{{.Status}}" | grep -v "Up"
# → Aucune ligne sauf le header = tout va bien
# Tous les sous-domaines répondent-ils en HTTPS ?
for domain in sso intranet git n8n portainer code status; do
echo -n "$domain : "
curl -sk -o /dev/null -w "%{http_code}\n" "https://$domain.seizeconsulting.com"
done
# → 200 ou 302 sur chaque ligne = OK
# → 500, 502, 503 = problème
# Espace disque restant
df -h /
# → Surveiller si > 80% utilisé
```
### Logs récents
```bash
# Logs Traefik (erreurs uniquement)
sudo docker logs --tail 100 traefik 2>&1 | grep -i "error\|warn"
# Logs Zitadel (erreurs)
sudo docker logs --tail 100 zitadel-zitadel-api-1 2>&1 | grep -i "error\|level=err"
# Logs OAuth2-Proxy intranet (erreurs récentes)
sudo docker logs --tail 100 oauth2-proxy 2>&1 | grep -i "error"
```
---
## 📦 Backups
### ⚠️ État actuel : AUCUN backup automatique
À mettre en place en priorité.
### Procédure backup manuel (à exécuter avant grosse modif)
```bash
# Créer un dossier de backup daté
BACKUP_DIR=~/backups/$(date +%Y%m%d-%H%M%S)
mkdir -p $BACKUP_DIR
# Backup Postgres (Gitea, n8n)
docker exec postgres pg_dumpall -U postgres > $BACKUP_DIR/postgres.sql
# Backup Postgres Zitadel (sa propre instance)
docker exec zitadel-postgres-1 pg_dumpall -U postgres > $BACKUP_DIR/postgres-zitadel.sql
# Backup volumes Docker (Gitea data, etc.)
sudo tar czf $BACKUP_DIR/gitea-data.tar.gz -C ~/docker/gitea data/
sudo tar czf $BACKUP_DIR/uptime-data.tar.gz -C ~/docker/uptime-kuma data/
sudo tar czf $BACKUP_DIR/portainer-data.tar.gz -C ~/docker/portainer data/
# Backup configs (.env, docker-compose.yml)
tar czf $BACKUP_DIR/configs.tar.gz -C ~/docker .
# Backup scripts furious-to-zitadel
tar czf $BACKUP_DIR/furious-scripts.tar.gz -C ~/ furious-to-zitadel/
echo "✅ Backup terminé dans $BACKUP_DIR"
ls -lh $BACKUP_DIR
```
### Procédure backup automatique (à mettre en place)
Voir [docs/09-troubleshooting.md](./docs/09-troubleshooting.md) section Backup automatisé.
---
## 🔄 Mises à jour
### Mise à jour d'un service Docker
```bash
cd ~/docker/<service>
docker compose pull
docker compose up -d
```
### Vérifier qu'une mise à jour ne casse rien
```bash
# 1. Backup avant
~/backup-manual.sh # script à créer si pas existant
# 2. Pull la nouvelle image
docker compose pull
# 3. Restart
docker compose down
docker compose up -d
# 4. Vérifier les logs
docker compose logs -f
# Ctrl+C pour quitter
# 5. Tester le service (login, fonctionnalités principales)
```
### Mises à jour critiques à surveiller
| Service | Risque | Procédure |
|---|---|---|
| Traefik | Changement breaking config | Lire le CHANGELOG avant |
| Zitadel | Schema DB peut changer | Backup obligatoire avant |
| Postgres | Migration majeure | Backup + lire migration guide |
| OAuth2-Proxy | Changement de scopes/headers | Tester en dry-run |
---
## Ajouter un nouvel outil
### Procédure standard (~1h)
#### 1. Ajouter le record DNS
Chez le registrar : créer record A `<outil>.seizeconsulting.com` → IP du serveur.
#### 2. Déployer l'outil (sans SSO)
```bash
mkdir -p ~/docker/<outil>
cd ~/docker/<outil>
# Créer docker-compose.yml
cat > docker-compose.yml << EOF
services:
<outil>:
image: <image>:<tag>
container_name: <outil>
restart: unless-stopped
networks:
- web
# PAS de labels Traefik ici → c'est OAuth2-Proxy qui sera exposé
networks:
web:
external: true
EOF
docker compose up -d
```
#### 3. Créer l'application dans Zitadel
Console Zitadel → Projects → Infra Seize → New application :
- Type : **Web**
- Authentication Method : **Basic**
- Grant Types : **Authorization Code**
- Redirect URL : `https://<outil>.seizeconsulting.com/oauth2/callback`
- Token Settings :
- Auth Token Type : **JWT**
- User roles inside ID Token : ✅
- Include user's profile info in the ID Token : ✅
- Add user roles to the access token : ✅
Récupérer : **Client ID** + **Client Secret**.
#### 4. Déployer un OAuth2-Proxy dédié
```bash
mkdir -p ~/docker/oauth2-proxy-<outil>
cd ~/docker/oauth2-proxy-<outil>
# docker-compose.yml (adapter depuis configs/oauth2-proxy/docker-compose.yml.template)
# .env (adapter depuis configs/oauth2-proxy/.env.example)
```
`.env` (variables à remplacer) :
```env
OAUTH2_PROXY_PROVIDER=oidc
OAUTH2_PROXY_CLIENT_ID=<de Zitadel>
OAUTH2_PROXY_CLIENT_SECRET=<de Zitadel>
OAUTH2_PROXY_COOKIE_SECRET=<32 chars random>
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_interne>
OAUTH2_PROXY_SCOPE=openid email profile urn:zitadel:iam:org:project:roles
OAUTH2_PROXY_EMAIL_DOMAINS=*
OAUTH2_PROXY_COOKIE_DOMAINS=.seizeconsulting.com
OAUTH2_PROXY_COOKIE_SECURE=true
OAUTH2_PROXY_HTTP_ADDRESS=0.0.0.0:4180
OAUTH2_PROXY_REVERSE_PROXY=true
OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER=true
OAUTH2_PROXY_PASS_ACCESS_TOKEN=true
OAUTH2_PROXY_SET_AUTHORIZATION_HEADER=true
OAUTH2_PROXY_SET_XAUTHREQUEST=true
OAUTH2_PROXY_WHITELIST_DOMAINS=.seizeconsulting.com
OAUTH2_PROXY_SKIP_PROVIDER_BUTTON=true
```
`chmod 600 .env` + `docker compose up -d`.
#### 5. Ajouter une carte sur l'intranet
Éditer `~/docker/intranet/index.html` et ajouter une nouvelle carte dans la section `<a class="service-card">` :
```html
<a class="service-card" data-app="<outil>" data-color="<color>" href="https://<outil>.seizeconsulting.com" target="_blank" rel="noopener">
<div class="card-header">
<div class="card-icon"><!-- SVG icon --></div>
<div class="card-arrow"><!-- SVG arrow --></div>
</div>
<div class="card-body">
<div class="card-title"><Nom de l'outil></div>
<div class="card-desc">Description courte</div>
<div class="card-meta">
<span class="card-url"><outil>.seizeconsulting.com</span>
<span class="card-tag">Catégorie</span>
</div>
</div>
</a>
```
#### 6. Mettre à jour la matrice RBAC dans le JavaScript
Éditer le `<script>` RBAC dans `index.html` :
```javascript
const ACCESS_MATRIX = {
"portainer": ["direction", "manager"],
"status": ["direction"],
// ...
"<outil>": ["direction", "manager"], // ← Ajouter ta nouvelle ligne
};
```
#### 7. Restart de l'intranet
```bash
cd ~/docker/intranet
docker compose down
docker compose up -d
```
#### 8. Tester
Se logger en incognito avec un user ayant accès → vérifier que la carte s'affiche et que le clic mène à l'outil avec SSO.
---
## 🗑️ Retirer un outil
### Procédure inverse de l'ajout
```bash
# 1. Arrêter l'outil et son OAuth2-Proxy
cd ~/docker/<outil>
docker compose down
cd ~/docker/oauth2-proxy-<outil>
docker compose down
# 2. Archiver les configs (au cas où)
mv ~/docker/<outil> ~/docker/<outil>.archived-$(date +%Y%m%d)
mv ~/docker/oauth2-proxy-<outil> ~/docker/oauth2-proxy-<outil>.archived-$(date +%Y%m%d)
# 3. Supprimer l'application dans Zitadel
# Console → Projects → Infra Seize → Applications → <outil> → Delete
# 4. Retirer la carte sur l'intranet (HTML + matrice RBAC)
# Éditer ~/docker/intranet/index.html
# Restart : docker compose restart intranet
# 5. Optionnel : retirer le record DNS chez le registrar
```
---
## 👥 Gestion des utilisateurs
### Ajouter un nouveau user manuellement
Quand un nouveau collaborateur arrive et n'est pas encore dans Furious :
```
Console Zitadel → Users → New → Human user :
- Email : prenom.nom@seizeconsulting.com
- Username : <trigramme> (ex: ABC)
- Prénom, Nom
- Cocher : Email verified, Password change required
- Generate password : oui (mdp temporaire 16 chars)
```
⚠️ **Distribuer le mdp via Teams privé** au nouveau collaborateur.
### Désactiver un user (départ)
```
Console Zitadel → Users → <user> → Actions → Deactivate
```
⚠️ **Ne PAS supprimer** le user : ses traces dans les outils (commits Gitea, workflows n8n, etc.) seraient orphelines. Désactiver suffit.
### Changer le rôle d'un user
#### Méthode UI (1 user)
```
Console Zitadel → Users → <user> → Authorizations → Edit roles
```
#### Méthode script (plusieurs users)
1. Mettre à jour le mapping dans `assign-roles.py` (variable `JOB_TITLE_TO_ROLE`)
2. Lancer :
```bash
cd ~/furious-to-zitadel
source venv/bin/activate
python3 assign-roles.py
```
⚠️ Le script **ajoute** les rôles, il ne **retire pas** les anciens. Pour retirer, utiliser l'UI ou un script custom.
### Synchronisation périodique avec Furious
Workflow recommandé (à mettre en place) :
1. **Tous les jours** : un cron exécute `read-all-users.sh` pour récupérer les users Furious
2. **Compare** avec les users Zitadel actuels
3. **Pour chaque diff** :
- Nouveau dans Furious → créer dans Zitadel
- Départ détecté dans Furious (`departure_date != "0000-00-00"`) → désactiver dans Zitadel
- Changement de `job_title` → réattribuer le rôle
⚠️ **Pas encore automatisé**. À mettre en place via n8n.
---
## 🔐 Rotation des secrets
### Quand rotater ?
- 🔴 **Immédiatement** : si secret exposé (dans un commit Git, conversation IA, etc.)
- 🟡 **Tous les 6 mois** : pratique de bonne hygiène
- 🟢 **Tous les ans** : pour les secrets très internes (postgres root, etc.)
### Procédure pour un secret OAuth2-Proxy (Client Secret Zitadel)
```bash
# 1. Aller dans Zitadel : Projects → Infra Seize → Applications → <outil>
# 2. Cliquer "Generate Client Secret" → copier le nouveau secret
# 3. Mettre à jour le .env d'OAuth2-Proxy
cd ~/docker/oauth2-proxy-<outil>
# Éditer .env, remplacer OAUTH2_PROXY_CLIENT_SECRET
# Sauvegarder dans le gestionnaire de mots de passe
# 4. Restart OAuth2-Proxy
docker compose down
docker compose up -d
```
### Procédure pour la clé service account Zitadel
```bash
# 1. Console Zitadel → Users → furious-import-service → Keys
# 2. Supprimer l'ancienne clé (et l'invalider)
# 3. Créer une nouvelle clé (Type : JSON)
# 4. Télécharger la nouvelle clé immédiatement
# 5. Remplacer sur le serveur
scp <nouvelle_cle>.json admin@<serveur>:~/furious-to-zitadel/zitadelfuriousimportkey.json
ssh admin@<serveur>
chmod 600 ~/furious-to-zitadel/zitadelfuriousimportkey.json
# 6. Tester les scripts
cd ~/furious-to-zitadel
source venv/bin/activate
python3 assign-roles.py # Doit fonctionner avec la nouvelle clé
```
### Procédure pour Postgres (Gitea/n8n)
⚠️ **À faire HORS heures de bureau** car les services s'arrêteront temporairement.
```bash
# 1. Générer un nouveau mdp
NEW_PWD=$(openssl rand -base64 32 | tr -d '+/=' | head -c 32)
echo "Nouveau mdp : $NEW_PWD"
# → Sauvegarder dans le gestionnaire de mots de passe
# 2. Changer le mdp dans Postgres
docker exec -it postgres psql -U postgres -c "ALTER USER gitea WITH PASSWORD '$NEW_PWD';"
# 3. Mettre à jour le .env de Gitea
nano ~/docker/gitea/.env
# Remplacer GITEA_DB_PASSWORD=$NEW_PWD
# 4. Restart Gitea
cd ~/docker/gitea
docker compose down
docker compose up -d
# 5. Tester Gitea
curl -sI https://git.seizeconsulting.com # Doit retourner 200 ou 302
```
---
## 🐛 Troubleshooting commun
Voir [docs/09-troubleshooting.md](./docs/09-troubleshooting.md) pour une liste exhaustive.
### "Mon SSO ne fonctionne plus, l'user est redirigé en boucle"
→ Probablement le bug Traefik authRequestHeaders=Cookie. Vérifier que `~/docker/traefik/dynamic.yml` contient bien `Cookie` dans `authRequestHeaders` du middleware ForwardAuth.
### "Le certificat Let's Encrypt a expiré"
→ Traefik renouvelle automatiquement. Si pas le cas :
```bash
# Vérifier les logs Traefik
sudo docker logs traefik 2>&1 | grep -i "letsencrypt\|certificate"
# Forcer le renouvellement
cd ~/docker/traefik
docker compose down
docker compose up -d
```
### "Le script Python d'import échoue avec un 401 Unauthorized"
→ Le token Zitadel a expiré (1h) OU les droits du service account ont été révoqués.
```bash
# Vérifier que la clé JSON est valide
python3 -c "import json; data=json.load(open('zitadelfuriousimportkey.json')); print('UserId:', data.get('userId'), 'KeyId:', data.get('keyId'))"
# Vérifier dans Zitadel Console que le service account a toujours IAM_USER_MANAGER
```
### "Un user ne peut pas se logger après avoir été créé"
→ Vérifier :
1. `Email verified` cocher dans son profil Zitadel
2. Si "Only authorized users can authenticate" est activé sur le project → il doit avoir un rôle
3. MFA pas mal configuré → faire un reset MFA si bloqué
---
## 📊 Monitoring
### Métriques à surveiller
| Métrique | Outil | Seuil d'alerte |
|---|---|---|
| Disponibilité des sous-domaines | Uptime Kuma | Down > 5 min |
| Taux d'erreur HTTP (5xx) | Logs Traefik | > 1% sur 5 min |
| Espace disque | `df -h` | > 80% |
| RAM utilisée | `free -h` | > 90% |
| CPU load | `uptime` | > 4 (sur 4 vCPU) |
| Tentatives de login échouées | Logs Zitadel | > 100/heure (potentielle attaque) |
### Mettre en place le monitoring
Uptime Kuma est déjà déployé. Ajouter des moniteurs pour :
- Chaque sous-domaine (HTTP check)
- Le serveur lui-même (ping)
- L'API Furious (HTTP check)
---
## 📞 Escalation
### Quand appeler Philippe ?
- 🔴 Service critique down (Zitadel, Gitea, intranet) > 1h
- 🔴 Faille sécurité avérée
- 🔴 Perte de données
### Quand appeler le support Scaleway ?
- 🔴 Serveur inaccessible (panne hardware)
- 🔴 Problème réseau Scaleway
### Quand appeler le support Furious ?
- 🟡 API Furious down ou très lente
- 🟡 Permissions API qui ne fonctionnent pas
---
## 📝 Checklist hebdomadaire (5-10 min)
- [ ] Vérifier tous les conteneurs Up
- [ ] Vérifier l'espace disque < 80%
- [ ] Lire les alertes Uptime Kuma
- [ ] Vérifier les logs Traefik pour erreurs récurrentes
- [ ] Backup manuel des données critiques (si pas encore automatisé)
- [ ] Vérifier les certificats Let's Encrypt (expire dans > 7 jours)
## 📝 Checklist mensuelle (30 min)
- [ ] Faire un Docker prune des images inutilisées : `docker image prune -a`
- [ ] Vérifier les mises à jour disponibles : `apt list --upgradable`
- [ ] Auditer les users Zitadel (départs non synchronisés ?)
- [ ] Vérifier que les scripts Python fonctionnent toujours
- [ ] Lire les logs sécurité (tentatives de login échouées, etc.)