Set-OPS-Public/docs/runbooks-exploitation.md
Daniel Allaire 8a9da0c31a restauration : la reconstruction remet l'etat de l'incarnation precedente
Chaque role proprietaire (AC, bases, annuaire, Nextcloud, rspamd/DKIM,
courriel, forge, web) remet son etat au moment ou il le creerait neuf,
depuis le dernier instantane anterieur a la naissance de la machine.
La sauvegarde refuse de deposer tant qu'un etat d'avant attend.
Outil de noeud setops-restaurer ; cibles sauvegarder-maintenant,
restauration-etat, restauration-renoncer ; test_restauration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 02:39:22 -04:00

26 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

5.0 La reconstruction remet l'état (depuis le 2026-09-30)

Jusqu'au 2026-09-30, une reconstruction repartait d'un état neuf : nouvelle racine d'AC, annuaire vierge (sysadmin au mot de passe d'amorçage), bases, Nextcloud et courriel vides. Les instantanés se restauraient pour prouver qu'ils s'ouvrent, jamais pour être remis en service.

Désormais chaque rôle propriétaire remet l'état de l'incarnation précédente, au moment où il le créerait neuf — la bonne séquence vient des couches de déploiement :

Rôle Quand Condition « vierge »
serveur_step_ca avant step ca init pas de ca.json — et la voûte doit ouvrir les clés restaurées
serveur_postgresql juste après la création des bases base sans table (hors nextcloud, icinga)
serveur_openldap en fin de rôle (schéma et ppolicy chargés) aucune entrée hors la racine
serveur_dovecot après le répertoire des boîtes répertoire vide
serveur_rspamd avant la génération DKIM pas de clé DKIM
serveur_forgejo avant l'arborescence de données répertoire vide
serveur_nextcloud en fin de rôle : base + fichiers + config ensemble, puis occ upgrade pas de config.php au départ, base vide
serveur_web_frontal / _dorsal après leur racine racine vide

L'instantané remis est le dernier pris avant la naissance de la machine (date de sa clé d'hôte SSH). Un état en place n'est jamais écrasé d'office. Chaque jeu laisse un marqueur dans /etc/setops/restauration/<jeu> ; ce qui a été remplacé est mis de côté dans /var/backups/setops-avant-restauration/.

La sauvegarde refuse de déposer (code 3) tant qu'un état d'avant n'a été ni remis ni écarté : sinon restic forget --keep-daily chasserait l'instantané d'avant au profit de l'état neuf du même jour.

Les gestes :

make sauvegarder-maintenant                 # JUSTE AVANT de raser : sinon on perd la journée
make restauration-etat [HOTE=...]           # ce que chaque jeu est devenu
make restauration-renoncer HOTE=.. JEU=.. CONFIRMER=true   # écarter un état, sans le remettre
-e client_backup_restauration_instantane=<id>              # imposer un instantané, par hôte

Sur un nœud, sans Ansible : setops-restaurer etat, et les répétitions qui ne touchent à rien — setops-restaurer annuaire --essai <base_dn>, base <nom> --vers epreuve, fichiers --vers /var/tmp/epreuve <chemin>.

É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 sur DROP DATABASE (avec --clean, la section de nextcloud finissait par DROP DATABASE postgres; — 2026-09-30), et vérifier avant de rejouer :

awk '/^\\connect forgejo$/{f=1;next} f && (/^\\connect /||/^CREATE DATABASE /||/^DROP 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.

7. Le nœud qui porte le gabarit tombe — la reproduction s'arrête

Symptôme. Aucune VM nouvelle ne peut naître. make creer-vm, make flotte-creer et make reconstruire échouent au clonage. Les machines existantes, elles, ne bronchent pas.

Ce qui se passe. Le gabarit doré vit sur un nœud nommé — vishnu chez l'hébergeur de référence — et sa configuration porte ce nom dans son chemin même :

/etc/pve/nodes/vishnu/qemu-server/9006.conf

Le clonage appelle nodes/vishnu/qemu/9006/clone : c'est l'API de ce nœud-là qui doit répondre. Nœud éteint, API muette, aucune naissance.

Ce qui ne se passe PAS, et qu'il faut savoir avant de paniquer. Les données du gabarit ne sont pas perdues. Mesure du 2026-09-10 :

pool CephNVMe   size=3   min_size=2
OSD sur les trois hôtes : asgard, gandalf, vishnu
base-9006-disk-0, base-9006-disk-1   présentes dans le pool

L'image est répliquée trois fois et reste lisible avec un nœud en moins. Et les quatorze VM d'un écosystème tournent ailleurs, sur leurs propres disques Ceph : elles ne s'aperçoivent de rien.

vishnu n'est pas un point unique de défaillance pour l'exploitation. Il l'est pour la reproduction — et la reproduction est ce que ce dépôt existe pour garantir.

La manœuvre. Deux gestes, quelques minutes, aucun mouvement de données :

# 1. re-héberger la configuration sur un nœud debout
mv /etc/pve/nodes/vishnu/qemu-server/9006.conf \
   /etc/pve/nodes/asgard/qemu-server/9006.conf

# 2. le déclarer, sinon la garde refusera
#    SITE-Chezlepro/plan/10-intrants.yml :  gabarit.noeud: asgard

Puis vérifier :

make gabarit-etat        # doit rendre « Conforme »

Pourquoi c'est aussi court. Parce que le disque du gabarit vit sur un stockage partagé depuis le 2026-09-10 : le déplacement ne bouge qu'un fichier de configuration. Sur un stockage local, il aurait fallu recopier 16 Go — ou refabriquer le gabarit.

L'ordre compte. Déplacer sans déclarer laisse gabarit_etat en écart ; déclarer sans déplacer fait échouer le clonage sur un nœud qui ne détient pas le modèle. Faire les deux, dans cet ordre.

Ce que cette section ne fait pas. Elle ne supprime pas la dépendance : elle la rend connue et courte. Deux remèdes de fond existent, aucun n'est appliqué :

  • déplacer le gabarit là où vivent déjà les VM — ça ne supprime pas le point unique, ça cesse d'en avoir deux (le nœud des VM et celui du modèle) ;
  • une garde qui refuse quand le nœud du gabarit n'héberge aucune machine de la flotte, c'est-à-dire quand la reproduction dépend d'un nœud qui ne porte rien d'autre.

Une dépendance qu'on documente sans la mesurer reste une dépendance qu'on découvrira au mauvais moment.

8. Les contrôles à la demande — ce que make prouver ne peut pas voir

make prouver est statique : il lit le dépôt, zéro appel réseau. C'est ce qui le rend rejouable partout, par n'importe qui, et présentable comme pièce justificative. Le prix de cette propriété : il ne peut rien dire de ce qui ne s'observe qu'en ouvrant une connexion.

Quatre contrôles comblent ce creux. Aucun ne corrige quoi que ce soit — ils regardent, et rendent 0 si tout concorde.

Contrôle Ce qu'il compare Le piège qu'il attrape
make expositions-etat Expositions du plan ↔ SAN du certificat servi ↔ code du vhost Un renommage déployé partout sauf dans le certificat
make gabarit-etat Gabarit déclaré ↔ VM modèle réelle Le modèle a dérivé de ce que le plan promet
make routes-fabric-etat Zones déclarées ↔ routes déclarées ↔ routes vivantes Une route vivante mais non déclarée — elle part au redémarrage
make frontiere-plan Registre des flux ↔ règles de la frontière Une règle posée à la main, qu'aucune déclaration ne porte

Ajouter SITE=1 à expositions-etat pour interroger l'écosystème du SITE plutôt que l'instance montée.

Pourquoi expositions-etat existe

Renommer une exposition touche cinq choses. Quatre suivent au déploiement ; la cinquième, non :

serveur_powerdns   la zone publie le nouveau nom          ✓
serveur_keycloak   le client OIDC accepte le retour       ✓
le service         il fabrique ses URL avec le bon nom    ✓  (P67)
serveur_nginx      le vhost répond sur le nouveau nom     ✓
client_pki         le SAN du certificat porte le nom      ✗  il faut le rejouer

Le symptôme est trompeur : le site répond, la page s'affiche, et c'est le navigateur qui refuse — avec une erreur de certificat que personne ne relie à un renommage fait la veille. Mesuré deux fois le 2026-09-10, sur grafana → observatoire puis icinga → vigie.

Le contrôle regarde dans les deux sens. Un nom resté dans le SAN après avoir quitté le plan est un nom que le certificat continue d'authentifier : c'est exactement ce qu'avait laissé le premier renommage, jusqu'au passage de client_pki.

Un INJOIGNABLE ne condamne pas le service : il dit que ce poste n'a pas pu ouvrir la connexion. Les zones du SITE ne sont pas routées depuis le plan d'administration du locataire — le mur est la frontière, pas le vhost.

9. Le premier jour d'un site — la séquence, et les dix-huit murs

Écrit le 2026-09-12, au sortir de la première reconstruction d'un site depuis zéro. Avant elle, SITE-Chezlepro n'avait jamais été rasé : il avait été monté par ajouts successifs, sur des semaines, avec un service déjà debout à chaque étape.

La limite qu'on répétait — « l'infrastructure d'accueil n'a jamais été reconstruite depuis zéro » — se lisait comme de la prudence. C'était dix-huit défauts que rien d'autre n'aurait pu révéler.

Trois reconstructions complètes ont été nécessaires : la première pour les trouver, la deuxième pour vérifier les correctifs — elle en a révélé deux de plus, invisibles tant que l'état n'était pas assez neuf — et la troisième pour prouver la séquence.

Passages Durée Défauts trouvés
Tour 1 8 ~2 h 30 16
Tour 2 3 53 min 2
Tour 3 2 37 min 24 0

La séquence ci-dessous est celle du troisième tour. Elle est mesurée, pas reconstituée.

Ce qui rend un site différent d'un locataire

Un locataire naît dans un monde déjà peuplé : le site lui fournit les paquets, les noms, le génome, l'heure et le dépôt de sauvegarde. Un site n'a personne au-dessus de lui, sauf sa frontière. Tout ce qu'un locataire reçoit, un site doit se le donner — et pendant qu'il se le donne, il ne l'a pas.

C'est de là que viennent onze des quinze murs.

La séquence, dans l'ordre

# 0. AVANT TOUT — l'état sort du bâtiment
make depot-hors-site VERS=<support hors site>

# 1. Le résolveur d'amorçage, DÉCLARÉ dans le plan du site AVANT de créer quoi que ce soit
#    plan/10-intrants.yml :  dns_amorcage: <patte de la frontière>
#    Sans lui, les machines pointent sur le DNS du site — qui est l'une d'elles.
#    Et `nftables_admin_ssh: [<plan d'administration>]`, sans quoi l'exploitant ne
#    pourra pas atteindre ce qu'il vient de construire.

# 2. Les VM, depuis l'underlay                                        ~9 min
make site-creer CONFIRMER=true

# 3. ATTENDRE QU'ELLES REPONDENT — `site-creer` ne le fait pas        ~3 min
until ansible -i scripts/site_inventaire.py all,'!<hyperviseurs>' -m ping >/dev/null 2>&1
do sleep 10; done

# 4. Passage 1 — va jusqu'à `serveur_ops`, s'arrête sur la forge vide ~20 min
V="$(dirname "$(readlink -f underlay.yml)")/underlay.vault.yml"
ansible-playbook -i scripts/site_inventaire.py playbooks/site.yml -e "@$V"

# 5. Amorcer la forge — elle tourne, elle est vide                     ~45 s
export SETOPS_FORGE_MDP=…                # jamais en argument de ligne de commande
make forge-amorcer CONFIRMER=true

# 6. Passage 2 — doit finir à 0 échec                                  ~4 min
ansible-playbook -i scripts/site_inventaire.py playbooks/site.yml -e "@$V"

Total mesuré : 37 min 24 pour sept machines, de rien du tout à un site complet.

La voûte se passe en -e @ — make site-appliquer la dérive du symlink underlay.yml, mais il n'existe aucune cible qui déploie le site EN ENTIER. Un ansible-playbook direct l'oublie, et l'échec parle d'une assertion, jamais d'un fichier manquant.

Deux passages, et le second ne sert qu'à la forge. Le premier va jusqu'à la couche 45 sur 46 ; seul serveur_ops manque, faute de génome dans une forge neuve.

Il en fallait TROIS avant le 2026-09-12. client_pki posait les droits de la clé d'hôte pour le groupe git, que le paquet de Forgejo crée dans une couche postérieure : au premier passage la clé restait fermée, Forgejo ne démarrait pas — après 300 secondes d'attente perdue — et il fallait un passage entier pour qu'il démarre, un autre pour la forge. Aucun ordre de couches ne dénoue ce cycle ; c'est la propriété du geste qui a changé de main : serveur_forgejo revendique désormais la clé au moment où il crée le groupe. client_pki garde la sienne et la repose à chaque passage, parce que step réécrit la clé à chaque renouvellement.

Les quinze murs, et ce que chacun enseigne

# Le mur Ce qu'il enseigne
1 Aucun moyen de raser le site make site-raser — la limite était un trou d'outillage, pas une fatalité
2 La forge naît vide, le runner y clone make forge-amorcer — un amorçage vient de l'extérieur de ce qu'il amorce
3 dns_amorcage pointe sur le DNS du site le mécanisme existait, la surcharge n'avait jamais été posée
4 La garde du résolveur teste l'adresse écrite une adresse écrite ne prouve pas qu'elle répond
5 Aucune cible « déployer tout le site » la voûte n'était jointe nulle part à la séquence complète
6 client_pki bloque sur un groupe absent blocage circulaire : l'échec empêchait d'atteindre ce qui créait le groupe
7 PostgreSQL du site sans TLS de l'AC un écart de sécurité, révélé par le premier client exigeant verify-full
8 pg_hba n'autorisait personne le site ne déclarait aucun réseau client
9 /etc/setops absent sur le dépôt un rôle supposait qu'une couche ultérieure était déjà passée
10 Forgejo attend 300 s une clé illisible une attente devrait abandonner quand la cause est déjà au journal
11 La clé SSH de l'exploitant inconnue de la forge une forge neuve ne connaît personne
12 L'accès de l'exploitant était accidentel il tenait au chevauchement d'adressage que le renumérotage a supprimé
13 Le devis reconnaît l'administration à son port "22" in ports plutôt que "admin" in pairs
14 Un flux à deux paires n'obtient qu'une branche la chaîne de elif rangeait [flotte, admin] dans un seul cas
15 Dépôts créés privés, runner anonyme could not read Username — un message qui pointe ailleurs que sa cause
16 Le wiki n'existe pas sur une forge neuve Forgejo ne crée <dépôt>.wiki.git qu'à la première page, posée à la main dans l'interface
17 site-creer rend la main avant que les machines répondent mesuré à 3 min 20 — un enchaînement automatique échouerait sur UNREACHABLE
18 Les clés d'hôte changent à chaque reconstruction accept-new couvre la première rencontre, jamais un changement : forge-amorcer purge l'entrée périmée de l'hôte que git va contacter

Le motif

Douze des dix-huit sont du code juste en régime établi, faux le premier jour : un résolveur qui se pointe sur lui-même, une clé dont le consommateur n'existe pas encore, un répertoire créé par une couche ultérieure, une forge vide qu'on croit remplie.

Trois sont des gardes qui vérifiaient la forme au lieu du résultat. C'est la famille la plus coûteuse : elles donnent l'apparence d'une vérification.

Un seul touchait la sécurité — et il était invisible tant qu'aucun client n'exigeait la vérification. Le défaut n'a pas cassé la construction : la construction a révélé le défaut.

Pour le prochain site. Poser dns_amorcage dès le départ, prévoir deux passages, amorcer la forge entre les deux, déclarer nftables_admin_ssh — sans quoi l'exploitant ne peut pas atteindre ce qu'il vient de construire — et créer la première page du wiki dans l'interface avant make wiki-publier.