diff --git a/Makefile b/Makefile index a740ea0..3ecd1e2 100644 --- a/Makefile +++ b/Makefile @@ -354,6 +354,7 @@ inseminer: ansible-runtime ## Le SITE amorce le runner d'un tenant — TENANT=/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)