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"]
|
return [server, f"{server}.sanef.groupe"]
|
||||||
|
|
||||||
def load_key(keyfile):
|
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
|
return None
|
||||||
keyfile = keyfile.strip('"').strip("'")
|
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]:
|
for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey]:
|
||||||
try:
|
try:
|
||||||
return cls.from_private_key_file(keyfile)
|
return cls.from_private_key_file(keyfile)
|
||||||
except Exception:
|
except Exception as ex:
|
||||||
|
last_ex = ex
|
||||||
continue
|
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
|
return None
|
||||||
|
load_key.last_error = None
|
||||||
|
|
||||||
def ssh_connect(server, env, settings, pkey, pkey2, cyb_password=None):
|
def ssh_connect(server, env, settings, pkey, pkey2, cyb_password=None):
|
||||||
"""Connexion SSH avec fallback FQDN et clé. Retourne (client, hostname_effectif)"""
|
"""Connexion SSH avec fallback FQDN et clé. Retourne (client, hostname_effectif)"""
|
||||||
@ -2294,8 +2351,28 @@ class PatchManagerV2:
|
|||||||
self._reload_keys()
|
self._reload_keys()
|
||||||
|
|
||||||
def _reload_keys(self):
|
def _reload_keys(self):
|
||||||
self.pkey = load_key(self.settings.get("keyfile",""))
|
errors = []
|
||||||
self.pkey2 = load_key(self.settings.get("keyfile2",""))
|
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
|
# ONGLET 1 — LOGIQUE
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user