#!/usr/bin/env python3 """La cle d'amorcage inscrite au plan du SITE est-elle CELLE de ce runner ? LE PLAN PORTE UNE TRANSCRIPTION, ET UNE TRANSCRIPTION PREND DU RETARD (2026-09-13). `runner_cle_publique`, dans `/plan/10-intrants.yml`, recopie la cle publique du runner du site. Elle est deposee par cloud-init sur le SEUL hote d'un tenant qui porte `serveur_ops_tenant` — c'est ce qui rend l'insemination possible. En reconstruisant `site-ops-01`, `setops` a recu une paire NEUVE. Cette ligne, elle, n'a pas bouge. Les deux se lisent pareil : meme algorithme, meme commentaire final `setops@site-ops-01.genese.internal`. Seul le materiel differe, et aucun coup d'oeil ne les distingue. CE QUE CA A COUTE : quatorze machines clonees, puis `Permission denied (publickey)` sur la seule que le site devait amorcer. Le message accusait une cle absente ; elle etait la, au mauvais materiel — et le clone etant deja fait, la correction du plan ne suffit plus : il faut RECREER la machine, puisque la cle se pose a la naissance. CETTE GARDE NE DEMANDE RIEN AU RESEAU. Le runner du site EST la machine qui insemine : sa cle est sous sa main. Deux secondes ici valent la materialisation d'une flotte entiere la-bas. ELLE NE S'APPLIQUE QUE LORSQUE LES DEUX CLES NOMMENT LE MEME HOTE. Lancee depuis le poste d'un exploitant, la cle locale est la SIENNE : comparer la declarerait fausse. Degrader, jamais deviner — on se tait et on laisse passer. """ from __future__ import annotations import sys from pathlib import Path RACINE = Path(__file__).resolve().parents[1] sys.path.insert(0, str(RACINE / "scripts")) def materiel_et_hote(ligne: str) -> tuple[str, str]: """(materiel de la cle, hote du commentaire) — ('', '') si la ligne n'est pas une cle.""" parts = ligne.split() return (parts[1], parts[-1]) if len(parts) > 2 else ("", "") def verifier() -> tuple[bool, str]: try: import underlay plan = str((underlay.lire_plan_site("10-intrants.yml") or {}) .get("runner_cle_publique") or "").strip() except Exception as e: # pas de site monte return True, f"aucun plan de site lisible ({e.__class__.__name__}) — rien a comparer." if not plan: return True, "le plan du site ne declare aucune cle d'amorcage — rien a comparer." pub = Path.home() / ".ssh" / "id_ed25519.pub" if not pub.is_file(): return True, f"{pub} absent — cette machine n'est pas le runner du site." mat_plan, hote_plan = materiel_et_hote(plan) mat_local, hote_local = materiel_et_hote(pub.read_text(encoding="utf-8").strip()) if not (hote_plan and hote_plan == hote_local): return True, f"cle locale de `{hote_local or 'inconnu'}`, plan de `{hote_plan or 'inconnu'}` — pas le meme hote, aucune comparaison." if mat_plan != mat_local: return False, ( "la cle d'amorcage du plan n'est PAS celle de ce runner.\n" f" au plan ...{mat_plan[-20:]}\n" f" reelle ...{mat_local[-20:]}\n" "Les deux portent le meme commentaire ; seul le materiel differe.\n" "\n" "Toute machine DEJA creee porte la perimee : la corriger ne suffit pas,\n" "il faut la RECREER — la cle se pose a la naissance.\n" "\n" " 1. relever : cat ~/.ssh/id_ed25519.pub\n" " 2. corriger : runner_cle_publique dans /plan/10-intrants.yml\n" " 3. recreer : make raser HOTE= ... puis make creer-vm HOTE=") return True, f"cle d'amorcage conforme (...{mat_local[-20:]}, {hote_local})." if __name__ == "__main__": ok, message = verifier() print(("OK : " if ok else "Refus: ") + message, file=sys.stdout if ok else sys.stderr) raise SystemExit(0 if ok else 2)