Set-OPS-Public/docs/runbooks-exploitation.md
Daniel Allaire 5bc3bceac1
Some checks failed
verifier / verifier (push) Has been cancelled
documentation : la tournee des 74 documents, parce qu un balayage ne lit pas
La revision a commence par un balayage par motifs — chemins morts, cibles make
absentes, comptes derives. Il a trouve une trentaine d ecarts et rate presque
tout le reste : un motif ne voit que ce qui s exprime en motif.

make hote-planifier en est l exemple. La cible EXISTE, donc le controle passait
au vert. C est une cible depreciee qui refuse et sort en 2, recommandee par
AGENTS.md, et qui contredit la REGLE D OR du meme fichier trois ecrans plus
haut. Il fallait lire pour la voir.

74 documents lus un par un. 66 corriges, 8 exacts.

CE QUI ETAIT FRANCHEMENT FAUX

AGENTS.md, la source d autorite, annoncait la flotte pas encore executee contre
des VM reelles. Elle a ete rasee et remontee depuis zero trois fois.
ecosysteme-chezlepro.md, le document montre a un client, portait la meme
phrase : il se sous-vendait gravement.

courriel-conception.md s ouvrait sur aucun role n est encore ecrit, au-dessus de
son propre paragraphe 1 qui les nomme. autorisation.md se terminait sur rien n
est construit alors qu il rapporte des mesures datees du role en fonctionnement.
hebergeur-exploitation.md disait rien n est fait d un depot qui existe.
filiation-emancipation.md se contredisait a deux ecrans de distance.

DES MODELES DECRITS D APRES UN MONDE ANTERIEUR

Le resolveur : cinq documents decrivaient un Unbound par VM en opt-in, trois le
donnaient en exemple d integration FACULTATIVE — il est universel depuis le
2026-08-24. L adressage de nomenclature-vm.md : reseau unique, VLAN 11-15, VMID
a cinq chiffres. Le nommage SDN de sdn-evpn.md contre le code : c est le wiki
qui avait raison.

CE QUI CASSE AU PREMIER ESSAI

Le nom du gabarit dore etait faux a quatre endroits, dont la procedure qui le
FABRIQUE et le critere R2 de l epreuve d operateur independant.
preparer-un-site-hebergeur.md avertissait qu une VM faite a la main serait
detruite : raser derive du plan, il ne la detruira jamais — le risque est l
inverse. Un mot de passe d essai en clair dans un depot public.

DEUX PREUVES ETENDUES, ET UNE QUI SE TROMPAIT ELLE-MEME

P57 couvre les groupes : elle a signale aussitot 29 groupes annonces au-dessus d
un tableau qui en cite 40. P29 confronte le tableau de authentification.md aux
declarations reelles : 12 annonces, 21 reels.

Et P57 imposait un chiffre faux — 56 preuves alors que le depot en porte 57, la
conditionnelle vivant hors de tout comptage. Un garde-fou qui fait respecter une
erreur ajoute l assurance a l erreur.

CE QUI RESTE, ET QU AUCUNE PREUVE NE TIENT

Deux comptes trouves a la main. Et une lacune reelle : rien ne garde les
meta/acces.yml — ni qu un service web-sso en porte un, ni que le groupe qu il
nomme existe. P29 tient les positions d authentification, personne ne tient les
habilitations.

make prouver : CONFORME, 56 OK, 0 echec, 1 saute. 0 lien mort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-06 16:18:23 -04:00

11 KiB

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 :

# SERVI (en mémoire par nginx)
echo | openssl s_client -connect infra-edge-01.chezlepro.internal:443 -servername keycloak.chezlepro.internal 2>/dev/null \
  | openssl x509 -noout -enddate
# FICHIER (sur disque)
openssl x509 -in /etc/step/certs/infra-edge-01.chezlepro.internal.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) :
    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).

Préférer le groupe à la personne — et c'est construit, pas un idéal. Les groupes LDAP sont projetés dans Keycloak et émis en claim (tasks/groupes-ldap.yml, claim-groupes.yml), et roles/serveur_grafana/meta/acces.yml déclare déjà sysadmin ⇒ Admin, personnel ⇒ Viewer. Ajouter quelqu'un au groupe lui ouvre Grafana, Forgejo, Icinga et le courriel d'un seul geste — alors qu'une assignation nominative crée une dette qu'on découvre le jour du départ, service par service. L'assignation explicite ci-dessus reste le geste de dépannage, pas la façon normale d'accorder un accès. Voir docs/autorisation.md §5.


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) :

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 bascule d'adressage de la fabric (D-77) — FAITE

Cette section décrivait une transition en cours jusqu'au 2026-09-06. Elle est terminée : mesuré le 2026-08-22, 10.0.0.0/24 n'existe plus — ni 10.0.0.1, ni 10.0.0.41 ne répondent. Le plan d'administration est 10.17.0.0/24 : la frontière y répond en 10.17.0.1 sur un port physique à elle, les commutateurs sont en 10.17.0.3 et .4 avec cette passerelle par défaut, le poste de l'exploitant en 10.17.0.17. Le renumérotage du tenant (10.27 → 10.17) est fait lui aussi.

On garde la méthode, parce qu'elle est ce qui a permis de le faire sans coupure, et qu'un autre site la rejouera :

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. La seconde adresse a donc vécu à côté de l'ancienne (ipalias), et l'ancienne n'est tombée qu'en dernier.

Distinguer une destination d'un chemin (D-78). Un réseau qui n'est jamais une destination — seulement un chemin — n'a aucune raison d'être unique entre deux hébergeurs : il sort de l'espace dérivé, vers 192.168.<vlan>.0/24. Seule la gestion doit rester unique d'un site à l'autre, parce que le poste de l'exploitant, un VPN et demain un lien inter-sites doivent l'atteindre.

Appliqué pour le transport VXLAN seulement, à ce jour (mesuré le 2026-09-06) : underlay-vxlan est bien en 192.168.50.0/24. Le transit (10.0.4.0/24, VLAN 40) et le stockage (10.11.5-7.x, VLAN 5/6/7) sont encore dans l'ancien espace. Ce n'est pas une urgence — ces réseaux ne quittent jamais leur site — mais la carte doit dire ce qui est, pas ce qui a été décidé. Un site neuf se monte directement au schéma final : il n'a aucune transition à subir.

Un plan d'adressage ne doit pas dépendre de l'ordre d'une migration. Le VLAN de transport est passé de 11 à 50 — non parce que 11 était mauvais, mais parce que 192.168.11.0/24 est occupé par le contrôle de la grappe. Faire dépendre un plan d'adressage de l'ordre d'une migration est exactement la dette qui se paie un an plus tard.

Ce qui reste, et qui n'est PAS un reliquat de la bascule

Les hyperviseurs gardent deux plans, et c'est voulu :

Plan Réseau Ce qu'il porte
administration 10.17.0.0/24 (vmbr3, segment physique) les équipements et l'exploitant ; aucune VM ne peut y naître (aucun pont ne le touche, et le validateur refuse qu'on y déclare une machine)
contrôle de la grappe 192.168.11.0/24 (vmbr0, carte dédiée) l'interface web Proxmox et le dialogue entre nœuds — c'est par là qu'on atteint ansible@192.168.11.4x

Le /24 de gestion vit à l'intérieur du /16 du tenant, et ce n'est pas un conflit. Les zones d'un tenant commencent au 3ᵉ octet 16 ; la bande 0-15 est libre pour la fabric, et la route connectée du /24 est plus spécifique que celle du /16 — la règle du préfixe le plus long, pas une coïncidence. Il faut cependant le déclarer (bande_basse_de: dans underlay.yml), sinon le validateur ne peut pas distinguer ce chevauchement voulu d'un chevauchement accidentel.