diff --git a/CHANGELOG.md b/CHANGELOG.md index 07a1b8e..7a7eb2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # CHANGELOG — Set-OPS +## 2026-09-13 (4) — La cle USB ne savait pas relire ce qu'on venait d'y ecrire + +`exporter_cles --support-chiffre` ecrit un TAR CLAIR quand le support est deja chiffre au +repos : empiler GPG par-dessus ajouterait une phrase de passe a perdre sans rien proteger +de plus. Cette capacite a ete ajoutee cote EXPORT le 2026-09-12, et **pas cote +RESTAURATION**. + +`restaurer_cles.py` — le script qui rend la cle autonome — ne savait lire que du GPG. +Constate en eprouvant l'export, pas en le supposant reussi : + +``` +gpg: aucune donnée OpenPGP valable n'a été trouvée. +ECHEC du dechiffrement. + Phrase de passe erronee, ou archive abimee. +``` + +**Le message accusait une phrase de passe qu'on n'avait jamais posee**, sur une archive +parfaitement saine. C'est le pire genre de diagnostic : il envoie chercher la faute la ou +elle n'est pas, au moment ou l'on restaure — c'est-a-dire au moment ou l'on sait le moins. + +`tarfile.is_tarfile` reconnait la forme ; les deux sont desormais lues. On ne demande pas +un drapeau : ce serait une chose de plus a savoir le jour ou tout a brule. + +### Ce que l'export a aussi montre de bon + +Le garde-fou a REFUSE d'ecraser l'archive du 12 — « on n'ecrase pas une sauvegarde de +cles : elle est peut-etre la seule ». La nouvelle est datee, l'ancienne reste. + +Dix cles sortent desormais, dont `setops-vault-site-technolibre`, creee le jour meme. + + ## 2026-09-13 (3) — Un site expose ce dont ses locataires ont besoin pour l'habiter ### Le constat diff --git a/scripts/restaurer_cles.py b/scripts/restaurer_cles.py index 4a5a32a..c33a775 100644 --- a/scripts/restaurer_cles.py +++ b/scripts/restaurer_cles.py @@ -50,7 +50,7 @@ def _destination(nom: str) -> Path: def main() -> int: ap = argparse.ArgumentParser() - ap.add_argument("archive", help="Chemin de l'archive .tar.gpg") + ap.add_argument("archive", help="Chemin de l'archive (.tar ou .tar.gpg)") ap.add_argument("--lister", action="store_true", help="Montrer ce que l'archive contient, sans rien ecrire") ap.add_argument("--forcer", action="store_true", @@ -61,16 +61,35 @@ def main() -> int: if not arch.is_file(): raise SystemExit(f"REFUS : {arch} introuvable.") + # DEUX FORMES D'ARCHIVE, ET IL FAUT LES DEUX (mesure du 2026-09-13). + # + # `exporter_cles --support-chiffre` ecrit un TAR CLAIR quand le support est deja + # chiffre au repos (LUKS) : empiler GPG par-dessus ajouterait une phrase de passe a + # perdre sans rien proteger de plus. Cette capacite a ete ajoutee cote EXPORT sans + # l'etre cote RESTAURATION — et ce script, qui est ce qui rend la cle autonome, ne + # savait lire que du GPG. + # + # L'echec etait particulierement mauvais : gpg disait « aucune donnee OpenPGP valable » + # puis le script concluait « phrase de passe erronee, ou archive abimee ». Il accusait + # une phrase de passe qu'on n'avait jamais posee, sur une archive parfaitement saine. + # + # ON RECONNAIT LA FORME, on ne la demande pas : `tarfile.is_tarfile` lit l'en-tete. + # Un drapeau a passer serait une chose de plus a savoir le jour ou l'on restaure — + # c'est-a-dire le jour ou l'on sait le moins. with tempfile.TemporaryDirectory() as tmp: - clair = Path(tmp) / "cles.tar" - print(" La phrase de passe de l'archive va etre demandee.\n") - r = subprocess.run(["gpg", "--quiet", "--output", str(clair), - "--decrypt", str(arch)]) - if r.returncode != 0: - raise SystemExit( - "\nECHEC du dechiffrement.\n" - " Phrase de passe erronee, ou archive abimee. gpg verifie l'integrite\n" - " (AES256 + MDC) : s'il refuse, le contenu n'est pas fiable.") + if tarfile.is_tarfile(arch): + print(" Archive EN CLAIR — la protection etait celle du support (LUKS).\n") + clair = arch + else: + clair = Path(tmp) / "cles.tar" + print(" Archive chiffree. La phrase de passe va etre demandee.\n") + r = subprocess.run(["gpg", "--quiet", "--output", str(clair), + "--decrypt", str(arch)]) + if r.returncode != 0: + raise SystemExit( + "\nECHEC du dechiffrement.\n" + " Phrase de passe erronee, ou archive abimee. gpg verifie l'integrite\n" + " (AES256 + MDC) : s'il refuse, le contenu n'est pas fiable.") with tarfile.open(clair) as tar: noms = [n for n in tar.getnames() if "/" not in n and not n.startswith("..")]