fix(get_config): use paramiko directly with group1-sha1 for Mocana SSH 6.3

- Replace netmiko with direct paramiko connection for SSH config fallback
- Explicitly set preferred_kex to include diffie-hellman-group1-sha1
  (required by old Mocana SSH 6.3 on HP 2530 / YA firmware)
- Hard timeout caps: banner_timeout=auth_timeout=8s, no keys/agent
This commit is contained in:
Christian Manivong
2026-06-29 13:53:09 +02:00
parent 8a182c4fa4
commit 831727f2af
+47 -19
View File
@@ -590,29 +590,57 @@ class ProcurveDriver(ConfigLifecycleMixin, SwitchDriver):
"""Open a one-shot SSH session to retrieve running-config.
Used as fallback when the REST API does not expose the config endpoint
(e.g. HP 2530 / YA firmware). Tries legacy KEX first (required for
older ProCurve firmware), then standard. Each attempt is capped at
8 seconds so this fallback never pushes the overall poll over budget.
Returns empty string on any failure.
(e.g. HP 2530 / YA firmware with Mocana SSH).
Uses paramiko directly (not netmiko) to:
- Explicitly enable diffie-hellman-group1-sha1 (required by Mocana SSH 6.3)
- Keep total blocking time ≤ 10 seconds
"""
_FALLBACK_TIMEOUT = 8 # seconds per attempt — keeps total ≤ 16 s
for legacy in (True, False):
import paramiko
_FALLBACK_TIMEOUT = 8
# KEX list that includes the old group1-sha1 required by Mocana SSH 6.3
_LEGACY_KEX = [
"diffie-hellman-group1-sha1",
"diffie-hellman-group14-sha1",
"diffie-hellman-group14-sha256",
"diffie-hellman-group-exchange-sha256",
]
_LEGACY_DISABLED = {
"pubkeys": ["rsa-sha2-256", "rsa-sha2-512"],
}
try:
kwargs = self._netmiko_kwargs(legacy=legacy)
kwargs["timeout"] = _FALLBACK_TIMEOUT
conn = ConnectHandler(**kwargs)
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Patch the preferred kex list on the class before connecting so
# paramiko will negotiate group1-sha1 with the old Mocana SSH server.
orig_kex = paramiko.Transport._preferred_kex
try:
return conn.send_command("show running-config")
finally:
conn.disconnect()
except Exception as exc:
logger.debug(
"SSH config fallback (%s) failed for %s: %s",
"legacy" if legacy else "standard",
self.hostname,
exc,
paramiko.Transport._preferred_kex = tuple(_LEGACY_KEX)
client.connect(
hostname=self.hostname,
port=self.port or 22,
username=self.username,
password=self.password,
timeout=_FALLBACK_TIMEOUT,
banner_timeout=_FALLBACK_TIMEOUT,
auth_timeout=_FALLBACK_TIMEOUT,
look_for_keys=False,
allow_agent=False,
disabled_algorithms=_LEGACY_DISABLED,
)
logger.warning("SSH config fallback failed for %s (all transports)", self.hostname)
finally:
paramiko.Transport._preferred_kex = orig_kex
_, stdout, _ = client.exec_command("show running-config", timeout=_FALLBACK_TIMEOUT)
config = stdout.read().decode("utf-8", errors="replace")
client.close()
return config
except Exception as exc:
logger.warning("SSH config fallback failed for %s: %s", self.hostname, exc)
return ""
# ------------------------------------------------------------------