D-77 sortait deja le stockage de l'espace derive, mais par une EXCEPTION NOMMEE plutot que par une regle. La question de l'exploitant — « le VLAN 40 n'a aucune VM, pourquoi ne pas lui donner une adresse non routee ? » — a fait apparaitre le critere juste. Ce n'est pas « y a-t-il des machines dedans » (le VLAN de gestion n'en a pas plus qu'un autre), c'est : ce reseau est-il jamais une DESTINATION, ou seulement un CHEMIN ? gestion (10) atteinte depuis ailleurs -> 10.<index>.0.0/24, UNIQUE transit (40) prochains sauts seulement -> 192.168.40.0/24 transport VXLAN(50) VTEP <-> VTEP -> 192.168.50.0/24 stockage (20/30/31) baie <-> hyperviseurs -> 192.168.20/30/31.0/24 Sur six reseaux, CINQ cessent d'exiger la moindre coordination entre deux hebergeurs, et « unique » redevient signifiant. L'OS, trouve par l'exploitant avant ecriture : le VLAN 11 aurait produit 192.168.11.0/24, DEJA occupe par la gestion des hyperviseurs sur vmbr0 (passerelle .254). Le 11 se liberera quand cette gestion rejoindra 10.<index>.0.x — mais faire dependre un plan d'adressage de l'ORDRE d'une migration est le genre de dette qui se paie un an plus tard. Le transport passe au VLAN 50 : libre, 192.168.50.0/24 libre, et ne depend de rien. Verifie : rien ne code le 11 en dur. underlay.yml n'est PAS modifie : il decrit le materiel tel qu'il est, et y ecrire les nouvelles plages avant le deplacement physique le ferait mentir. Les adresses changeront avec le materiel, dans l'ordre du runbook §6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
205 lines
9.9 KiB
Markdown
205 lines
9.9 KiB
Markdown
# Runbooks d'exploitation — Set-OPS
|
||
|
||
> **Pour qui :** l'exploitant, face à une situation. Pas le mainteneur, pas l'apprenant.
|
||
> Le *pourquoi* vit dans les autres `docs/` ; la version pédagogique, dans le wiki. Ici,
|
||
> uniquement le *comment faire*.
|
||
>
|
||
> Tu viens de reprendre l'écosystème et tu ne sais pas par où entrer ?
|
||
> Wiki → **Reprendre l'écosystème**.
|
||
|
||
---
|
||
|
||
## 1. Un certificat a expiré / le login SSO est cassé
|
||
|
||
**Symptôme** : connexion à une app via le SSO échoue *après* le login ; log Grafana/app :
|
||
`tls: failed to verify certificate: x509: certificate has expired`.
|
||
|
||
**Cause fréquente** : le cert step-ca a été **renouvelé sur disque** mais le service (nginx sur
|
||
l'edge) **n'a pas été rechargé** → il sert l'ancien cert en mémoire.
|
||
|
||
**Diagnostic-réflexe** — comparer le cert *servi* au cert *fichier* :
|
||
```bash
|
||
# SERVI (en mémoire par nginx)
|
||
echo | openssl s_client -connect infra-edge-01…:443 -servername keycloak.lab… 2>/dev/null \
|
||
| openssl x509 -noout -enddate
|
||
# FICHIER (sur disque)
|
||
openssl x509 -in /etc/step/certs/infra-edge-01….crt -noout -enddate
|
||
```
|
||
Dates différentes (servi < fichier) ⇒ nginx sert un cert périmé.
|
||
|
||
**Fix immédiat** : `systemctl reload nginx` sur l'edge (idem `postfix`/`dovecot`/`slapd` selon le
|
||
service touché).
|
||
|
||
**Fix permanent (déjà en place)** : `client_pki_reload_services` recharge les vrais consommateurs
|
||
après chaque renouvellement — edge→nginx, mail→postfix/dovecot, annuaire→slapd. Vérifier :
|
||
`systemctl cat cert-renewer@$(hostname -f).service | grep ExecStartPost`.
|
||
Voir aussi l'unité wiki *PKI & confiance* (⑤ le renouvellement).
|
||
|
||
---
|
||
|
||
## 2. Donner Explore/Editor à un opérateur dans Grafana (RBAC)
|
||
|
||
Par défaut, un utilisateur SSO est **Viewer** (dashboards seulement, pas Explore). Pour l'élever :
|
||
|
||
1. Déclarer l'assignation dans l'inventaire de l'instance (group_vars `serveur_keycloak`) :
|
||
```yaml
|
||
serveur_keycloak_role_assignments:
|
||
- { user: <uid>, role: grafana-editor } # ou grafana-admin
|
||
```
|
||
2. Redéployer Keycloak (`make deployer` sur le nœud SSO) — kcadm **à chaud, sans coupure**.
|
||
3. L'utilisateur doit **se déconnecter/reconnecter** (Grafana applique le rôle à la connexion).
|
||
|
||
Mapping (défaut du rôle grafana) : `grafana-admin`→Admin, `grafana-editor`→Editor, sinon Viewer
|
||
(`serveur_grafana_oidc_role_path`). Idéal souverain : piloter par un **groupe d'annuaire** plutôt
|
||
qu'un utilisateur explicite. Voir l'unité wiki *Autorisation & RBAC*.
|
||
|
||
---
|
||
|
||
## 3. « Connection timed out during banner exchange » — le message qui accuse le réseau
|
||
|
||
**Symptôme** : Ansible marque un ou plusieurs hôtes injoignables, avec ce message. On soupçonne
|
||
le réseau, la route ou le pare-feu. **C'est presque toujours autre chose.**
|
||
|
||
**Pourquoi le message trompe.** À travers la frontière, la poignée TCP aboutit *toujours* — le
|
||
pare-feu y répond lui-même sans relayer (voir `frontiere-opnsense.md`). L'échec ne peut donc
|
||
pas se présenter comme un « connection refused » franc : il se manifeste plus tard, au moment
|
||
où le serveur devrait annoncer sa bannière SSH. D'où un message qui parle de délai réseau pour
|
||
un hôte qui, souvent, n'a jamais rien reçu.
|
||
|
||
**Les trois causes, par fréquence :**
|
||
|
||
1. **La VM n'est pas encore là.** `make creer-vm` rend la main dès que Proxmox a *démarré* la
|
||
VM, pas quand elle répond. C'est le cas normal, quotidien, et aucun correctif ne l'abolira :
|
||
les attentes actives du `Makefile` existent pour ça.
|
||
2. **`sshd` refuse une rafale.** `MaxStartups` / `MaxSessions` trop serrés coupent des
|
||
connexions parfaitement légitimes — deux déploiements interrompus le 2026-08-09, sur des
|
||
hôtes qui n'avaient ni redémarré ni perdu leur réseau. Réglés par
|
||
`ssh_hardening_max_startups` (`10:30:60`) et `ssh_hardening_max_sessions` (`10`), avec
|
||
l'arbitrage expliqué dans `roles/ssh_hardening/defaults/main.yml`.
|
||
3. **Le réseau, vraiment** — le cas le plus rare, et le dernier à examiner.
|
||
|
||
**Ce qui tranche, dans l'ordre :**
|
||
|
||
```
|
||
make hote-afficher HOTE=<nom> # l'hôte existe-t-il au plan ?
|
||
ssh -v ansible@<ip> 2>&1 | tail -20 # où la négociation s'arrête exactement
|
||
```
|
||
|
||
Une bannière (`SSH-2.0-OpenSSH…`) prouve que le chemin est bon et que le problème est
|
||
applicatif. **Aucun `connect()` ne prouvera quoi que ce soit ici** — il réussit vers le vide.
|
||
|
||
---
|
||
|
||
## 4. Brander une instance Forgejo (identité visuelle)
|
||
|
||
Activer dans l'inventaire (group_vars `serveur_forgejo`) :
|
||
```yaml
|
||
serveur_forgejo_branding: true
|
||
serveur_forgejo_app_name: "Forge Chezlepro"
|
||
serveur_forgejo_theme: "forgejo-dark"
|
||
serveur_forgejo_meta_description: "…"
|
||
```
|
||
Puis redéployer. Le rôle déploie le dossier `custom/` officiel (logo/favicon aurore, accent CSS par
|
||
variables, page d'accueil brandée) — léger, résistant aux MAJ (aucune classe interne touchée).
|
||
Note : ne s'applique qu'aux Forgejo **gérées par Set-OPS**.
|
||
|
||
## 5. Restaurer — et d'abord : prouver qu'on peut
|
||
|
||
> Éprouvé le 2026-08-12 sur Chezlepro. `make valider` rejoue la partie automatisable ;
|
||
> la restauration d'une base reste manuelle, et porte un piège décrit plus bas.
|
||
|
||
**Ce que `make valider` prouve tout seul**, pour chaque nœud détenteur d'état : le dernier
|
||
instantané se restaure, il en sort des fichiers, et — pour l'annuaire — qu'il est
|
||
**rejouable** (`slapadd -u`, essai à blanc, rien n'est écrit). Verdicts possibles :
|
||
|
||
| Verdict | Sens |
|
||
|---|---|
|
||
| `OK` | restauré, et non vide |
|
||
| `À CONFIRMER` | restauré, mais l'instantané n'emporte **aucun fichier** — légitime si ce nœud n'a pas encore de données, à trancher par un humain |
|
||
| `SANS OBJET` | ce nœud ne détient rien de non régénérable |
|
||
| `ÉCHEC` | la restauration elle-même a échoué |
|
||
|
||
### Restaurer les clés de l'autorité (`infra-pki-01`)
|
||
|
||
```
|
||
export RESTIC_REPOSITORY=$(grep -oP 'RESTIC_REPOSITORY="\K[^"]+' /usr/local/sbin/setops-sauvegarder.sh)
|
||
export RESTIC_PASSWORD_FILE=/etc/setops/restic.pass
|
||
t=$(mktemp -d); restic restore latest --target "$t"
|
||
diff -r "$t/etc/step-ca" /etc/step-ca
|
||
```
|
||
|
||
Mesuré : **12 fichiers, 11 identiques octet pour octet**, `root_ca_key` et
|
||
`intermediate_ca_key` compris. Le seul écart attendu est `db/000000.vlog` — le journal de
|
||
la base badger de step-ca, qui avance à **chaque** émission de certificat.
|
||
|
||
### Rejouer une base depuis `pg_dumpall` — LE PIÈGE
|
||
|
||
`pg_dumpall` écrit `CREATE DATABASE <suivante>` **avant** le `\connect` correspondant.
|
||
Découper « du `\connect X` au `\connect` suivant » emporte donc un ordre qui vise une
|
||
**autre** base. Couper aussi sur `CREATE DATABASE`, et **vérifier avant de rejouer** :
|
||
|
||
```
|
||
awk '/^\\connect forgejo$/{f=1;next} f && (/^\\connect /||/^CREATE DATABASE /){exit} f' \
|
||
toutes-bases.sql > section.sql
|
||
|
||
grep -qE '^(DROP|CREATE|ALTER) DATABASE|^\\connect' section.sql \
|
||
&& { echo "REFUS : ordre hors-perimetre"; exit 1; }
|
||
|
||
runuser -u postgres -- createdb epreuve_restauration
|
||
runuser -u postgres -- psql -q -d epreuve_restauration -f section.sql
|
||
runuser -u postgres -- psql -tAd epreuve_restauration -c "select count(*) from information_schema.tables where table_schema='public'"
|
||
runuser -u postgres -- dropdb epreuve_restauration
|
||
```
|
||
|
||
Mesuré sur `forgejo` : rejeu en **0 erreur**, **130 tables**, et les comptes réels
|
||
(`forgejo-admin`, `sysadmin`). Production vérifiée intacte après coup.
|
||
|
||
**Ne jamais rejouer un `pg_dumpall` entier sur un cluster vivant** : il contient les
|
||
`DROP DATABASE` de toutes les bases. Une restauration réelle se fait sur un cluster neuf.
|
||
|
||
## 6. La frontière porte deux adresses de gestion (transition D-77)
|
||
|
||
> Posée le 2026-08-12 par l'API (`interfaces/vip_settings`, mode `ipalias`). **Ce n'est
|
||
> pas une anomalie** : c'est une bascule d'adressage en cours.
|
||
|
||
```
|
||
10.0.0.1/24 adresse historique — TOUJOURS ACTIVE, rien ne l'a quittée
|
||
10.17.0.1/24 adresse cible (D-77 : underlay dans la bande basse du /16 du site)
|
||
```
|
||
|
||
**Ajouter avant de retirer**, jamais l'inverse. Un point de routage qui change d'adresse
|
||
d'un coup coupe simultanément l'exploitant, les commutateurs qui l'ont en passerelle par
|
||
défaut, et l'outil qui devait faire la bascule.
|
||
|
||
### Ce qui reste à déplacer, et dans quel ordre
|
||
|
||
| # | À déplacer | Vers | Nature (D-78) |
|
||
|---|---|---|---|
|
||
| 1 | le poste de l'exploitant | `10.17.0.x/24` (seconde adresse) | — |
|
||
| 2 | les commutateurs `10.0.0.3/.4` **et leur `ip default-gateway`** | `10.17.0.3/.4`, passerelle `10.17.0.1` | destination |
|
||
| 3 | l'administration des hyperviseurs (`vmbr0`, aujourd'hui `192.168.11.x`), l'OOB/IPMI | `10.17.0.41/.43/.47` | destination |
|
||
| 4 | le transit `10.0.4.x` | **`192.168.40.x`** | chemin |
|
||
| 5 | le transport VXLAN `10.0.5.x`, **VLAN 11 → 50** | **`192.168.50.x`** | chemin |
|
||
| 6 | le stockage `10.0.1–3.x` | **`192.168.20/30/31.x`** | chemin |
|
||
| 7 | **en dernier seulement**, retirer `10.0.0.1` | — | — |
|
||
|
||
Les étapes 4 à 6 sortent définitivement de l'espace dérivé (D-78) : ces réseaux ne sont
|
||
jamais des destinations, seulement des chemins. Une fois faites, **seule la gestion** doit
|
||
rester unique d'un site à l'autre.
|
||
|
||
> **L'étape 5 change aussi le numéro de VLAN**, côté commutateur (trunk) et côté
|
||
> hyperviseurs (`bond3.11` → `bond3.50`). Le 11 est écarté parce que `192.168.11.0/24` est
|
||
> occupé par l'étape 3 tant qu'elle n'est pas faite — et un plan d'adressage ne doit pas
|
||
> dépendre de l'ordre d'une migration.
|
||
|
||
### Ce qui ne dépend PAS de cette bascule
|
||
|
||
**Le renumérotage du tenant** (`10.27` → `10.17`) est **indépendant**. La frontière route
|
||
le supernet du tenant vers le même prochain saut, quelle que soit sa propre adresse de
|
||
gestion : il suffit que la route et les alias suivent. Les deux chantiers peuvent donc
|
||
être menés séparément — et c'est préférable.
|
||
|
||
> **Longueur de préfixe.** Une fois les deux faits, la gestion (`10.17.0.0/24`) vit
|
||
> *à l'intérieur* du supernet du tenant (`10.17.0.0/16`). Aucun conflit : la route
|
||
> connectée du `/24` est plus spécifique que celle du `/16`. C'est la règle du préfixe le
|
||
> plus long, pas une coïncidence.
|