# 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: , 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= # l'hôte existe-t-il au plan ? ssh -v ansible@ 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 ` **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.