"""Gestion du fichier known_hosts local (cles d'hotes approuvees via TOFU). Avec des serveurs en IP dynamique (DHCP), une IP reattribuee a une autre machine fait apparaitre une "cle d'hote modifiee" lors de la connexion : ce module permet de retirer facilement l'ancienne entree, en masse ou au cas par cas, sans avoir a editer le fichier a la main. """ from __future__ import annotations import os from dataclasses import dataclass from pathlib import Path import paramiko from app.paths import DATA_DIR # noqa: E402 (chemins valables aussi en .exe) from app.storage.atomic_file import atomic_write_text KNOWN_HOSTS_FILE = DATA_DIR / "known_hosts" @dataclass class KnownHostEntry: hostname: str key_type: str fingerprint: str class KnownHostsStore: def __init__(self, path: Path = KNOWN_HOSTS_FILE): self.path = path def _load(self) -> paramiko.HostKeys: hk = paramiko.HostKeys() if self.path.exists(): try: hk.load(str(self.path)) except Exception: pass return hk def list_entries(self) -> list[KnownHostEntry]: hk = self._load() entries: list[KnownHostEntry] = [] for hostname in hk.keys(): for key_type, key in hk[hostname].items(): entries.append(KnownHostEntry( hostname=hostname, key_type=key_type, fingerprint=key.get_fingerprint().hex(":"), )) entries.sort(key=lambda e: e.hostname) return entries def remove_host(self, hostname: str) -> None: hk = self._load() if hostname in hk: del hk[hostname] self._save(hk) def replace_host_key(self, hostname: str, key: paramiko.PKey) -> None: """Retire l'ancienne cle d'un hote et enregistre la nouvelle (cas d'une IP DHCP reattribuee).""" hk = self._load() if hostname in hk: del hk[hostname] hk.add(hostname, key.get_name(), key) self._save(hk) def clear_all(self) -> None: atomic_write_text(self.path, "") def _save(self, hk: paramiko.HostKeys) -> None: # paramiko.HostKeys.save() ecrit directement sur le chemin donne ; on le fait # ecrire dans un fichier temporaire puis on bascule dessus atomiquement, pour # ne jamais laisser known_hosts tronque en cas de coupure pendant l'ecriture. DATA_DIR.mkdir(parents=True, exist_ok=True) tmp_path = self.path.with_name(self.path.name + ".tmp") hk.save(str(tmp_path)) os.replace(tmp_path, self.path)