fix(ssh): support .ppk via puttygen auto-conversion + messagebox d'erreur si cle non lisible (fix connexion silencieuse vtdsi*)
This commit is contained in:
parent
fb29b59625
commit
e5ce3b2ec9
@ -670,15 +670,72 @@ def build_fqdn(server, env):
|
||||
return [server, f"{server}.sanef.groupe"]
|
||||
|
||||
def load_key(keyfile):
|
||||
if not keyfile or not os.path.exists(keyfile.strip('"').strip("'")):
|
||||
"""Charge une clé SSH privée. Retourne l'objet PKey ou None.
|
||||
Si format PuTTY (.ppk), tente conversion auto via puttygen vers OpenSSH PEM.
|
||||
Stocke le détail de l'échec dans load_key.last_error pour affichage UI."""
|
||||
load_key.last_error = None
|
||||
if not keyfile:
|
||||
return None
|
||||
keyfile = keyfile.strip('"').strip("'")
|
||||
if not os.path.exists(keyfile):
|
||||
load_key.last_error = f"Fichier clé introuvable : {keyfile}"
|
||||
return None
|
||||
|
||||
# Détection format PuTTY (.ppk)
|
||||
try:
|
||||
with open(keyfile, "r", encoding="utf-8", errors="ignore") as f:
|
||||
first_line = f.readline().strip()
|
||||
except Exception as ex:
|
||||
load_key.last_error = f"Lecture impossible : {ex}"
|
||||
return None
|
||||
|
||||
is_ppk = first_line.startswith("PuTTY-User-Key-File-")
|
||||
if is_ppk:
|
||||
# Paramiko ne lit pas .ppk nativement → tenter conversion auto via puttygen
|
||||
converted = keyfile + ".openssh"
|
||||
puttygen_paths = [
|
||||
r"C:\Program Files\PuTTY\puttygen.exe",
|
||||
r"C:\Program Files (x86)\PuTTY\puttygen.exe",
|
||||
"puttygen", # si dans PATH
|
||||
"puttygen.exe",
|
||||
]
|
||||
import subprocess
|
||||
converted_ok = False
|
||||
for pg in puttygen_paths:
|
||||
try:
|
||||
subprocess.run(
|
||||
[pg, keyfile, "-O", "private-openssh", "-o", converted],
|
||||
check=True, capture_output=True, timeout=10,
|
||||
)
|
||||
converted_ok = True
|
||||
break
|
||||
except (FileNotFoundError, subprocess.SubprocessError, OSError):
|
||||
continue
|
||||
if not converted_ok:
|
||||
load_key.last_error = (
|
||||
f"Format PuTTY (.ppk) détecté pour {os.path.basename(keyfile)}. "
|
||||
f"Paramiko ne lit pas ce format nativement et puttygen.exe est introuvable. "
|
||||
f"Convertir manuellement : "
|
||||
f'puttygen "{keyfile}" -O private-openssh -o "{keyfile}.openssh" '
|
||||
f"puis pointer la conf vers le fichier converti."
|
||||
)
|
||||
return None
|
||||
keyfile = converted
|
||||
|
||||
# Tenter chargement PEM/OpenSSH (multi-format)
|
||||
last_ex = None
|
||||
for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey]:
|
||||
try:
|
||||
return cls.from_private_key_file(keyfile)
|
||||
except Exception:
|
||||
except Exception as ex:
|
||||
last_ex = ex
|
||||
continue
|
||||
load_key.last_error = (
|
||||
f"Aucun loader paramiko n'a pu lire {os.path.basename(keyfile)} "
|
||||
f"(RSA / Ed25519 / ECDSA tentés). Dernière erreur : {last_ex}"
|
||||
)
|
||||
return None
|
||||
load_key.last_error = None
|
||||
|
||||
def ssh_connect(server, env, settings, pkey, pkey2, cyb_password=None):
|
||||
"""Connexion SSH avec fallback FQDN et clé. Retourne (client, hostname_effectif)"""
|
||||
@ -2294,8 +2351,28 @@ class PatchManagerV2:
|
||||
self._reload_keys()
|
||||
|
||||
def _reload_keys(self):
|
||||
self.pkey = load_key(self.settings.get("keyfile",""))
|
||||
self.pkey2 = load_key(self.settings.get("keyfile2",""))
|
||||
errors = []
|
||||
kf1 = self.settings.get("keyfile", "")
|
||||
if kf1:
|
||||
self.pkey = load_key(kf1)
|
||||
if self.pkey is None and load_key.last_error:
|
||||
errors.append(f"Clé 1 ({kf1}) :\n{load_key.last_error}")
|
||||
else:
|
||||
self.pkey = None
|
||||
kf2 = self.settings.get("keyfile2", "")
|
||||
if kf2:
|
||||
self.pkey2 = load_key(kf2)
|
||||
if self.pkey2 is None and load_key.last_error:
|
||||
errors.append(f"Clé 2 ({kf2}) :\n{load_key.last_error}")
|
||||
else:
|
||||
self.pkey2 = None
|
||||
if errors:
|
||||
try:
|
||||
messagebox.showwarning("Clés SSH non chargées", "\n\n".join(errors))
|
||||
except Exception:
|
||||
# Pas de UI dispo (lancement CLI ?), juste print
|
||||
for e in errors:
|
||||
print(f"[load_key] {e}")
|
||||
|
||||
# ==========================================================================
|
||||
# ONGLET 1 — LOGIQUE
|
||||
|
||||
Loading…
Reference in New Issue
Block a user