fix(ssh): verify the host key netOrk expects, report the one seen (NetOrk/netork#1167)
CI / test (3.10) (push) Successful in 54s
CI / test (3.11) (push) Successful in 40s
CI / test (3.12) (push) Successful in 51s
CI / test (3.10) (pull_request) Successful in 42s
CI / test (3.11) (pull_request) Successful in 35s
CI / test (3.12) (pull_request) Successful in 49s
CI / test (3.10) (push) Successful in 54s
CI / test (3.11) (push) Successful in 40s
CI / test (3.12) (push) Successful in 51s
CI / test (3.10) (pull_request) Successful in 42s
CI / test (3.11) (pull_request) Successful in 35s
CI / test (3.12) (pull_request) Successful in 49s
The driver accepted any SSH host key. It now verifies through napalm-device-types' HostKeyCheck (>= 4.19.0): with a known key in optional_args["ssh_host_keys"] any other key is refused before login, and every key seen is reported to optional_args["ssh_host_key_observer"]. Without either argument it connects as before (first use).
This commit is contained in:
1 parent
d134ccbb89
commit
6bb3035857
6 files changed
+179
-56
No files matched your search
+34
-23
@@ -48,6 +48,7 @@ from napalm_device_types import (
|
||||
SystemdServicesMixin,
|
||||
)
|
||||
from napalm.base.exceptions import ConnectionException
|
||||
from napalm_device_types.ssh_host_keys import HostKeyCheck, SshHostKeyMismatch
|
||||
|
||||
try:
|
||||
from proxmoxer import ProxmoxAPI
|
||||
@@ -318,39 +319,49 @@ class ProxmoxDriver(
|
||||
|
||||
# Fallback: direct paramiko SSH
|
||||
try:
|
||||
import paramiko
|
||||
|
||||
if self._ssh_client is None:
|
||||
ssh_user = self._ssh_username or self.username
|
||||
ssh_pass = self._ssh_password or self.password
|
||||
ssh_pkey = None
|
||||
if self._ssh_key and not ssh_pass:
|
||||
from io import StringIO as _StringIO
|
||||
ssh_pkey = paramiko.RSAKey.from_private_key(_StringIO(self._ssh_key))
|
||||
self._ssh_client = paramiko.SSHClient()
|
||||
self._ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
connect_kwargs: _JsonDict = {
|
||||
"hostname": self.hostname,
|
||||
"port": 22,
|
||||
"username": ssh_user,
|
||||
"timeout": self.timeout,
|
||||
}
|
||||
if ssh_pkey:
|
||||
connect_kwargs["pkey"] = ssh_pkey
|
||||
else:
|
||||
connect_kwargs["password"] = ssh_pass
|
||||
self._ssh_client.connect(**connect_kwargs)
|
||||
_, stdout, stderr = self._ssh_client.exec_command(command, timeout=self.timeout)
|
||||
_, stdout, stderr = self._node_ssh().exec_command(command, timeout=self.timeout)
|
||||
err = stderr.read().decode().strip()
|
||||
out = stdout.read().decode().strip()
|
||||
return out or err
|
||||
except ImportError:
|
||||
logger.warning("paramiko not installed — cannot exec SSH commands")
|
||||
except SshHostKeyMismatch as exc:
|
||||
# Refused before any login, and already reported to the caller's store.
|
||||
logger.warning("%s", exc)
|
||||
except Exception as exc:
|
||||
logger.debug("SSH exec command failed: %s", exc)
|
||||
|
||||
return ""
|
||||
|
||||
def _node_ssh(self) -> Any: # paramiko.SSHClient; paramiko is imported lazily
|
||||
"""The SSH session to the node on port 22, opened once.
|
||||
|
||||
Its host key is the one the caller expects for port 22, or on first use
|
||||
whichever the node presents; either way it is reported (netork#1167).
|
||||
"""
|
||||
import paramiko
|
||||
|
||||
if self._ssh_client is None:
|
||||
ssh_pass = self._ssh_password or self.password
|
||||
connect_kwargs: _JsonDict = {
|
||||
"username": self._ssh_username or self.username,
|
||||
"timeout": self.timeout,
|
||||
}
|
||||
if self._ssh_key and not ssh_pass:
|
||||
from io import StringIO as _StringIO
|
||||
|
||||
connect_kwargs["pkey"] = paramiko.RSAKey.from_private_key(_StringIO(self._ssh_key))
|
||||
else:
|
||||
connect_kwargs["password"] = ssh_pass
|
||||
client = paramiko.SSHClient()
|
||||
try:
|
||||
HostKeyCheck(self.optional_args, self.hostname, 22).connect(client, **connect_kwargs)
|
||||
except Exception:
|
||||
client.close()
|
||||
raise
|
||||
self._ssh_client = client
|
||||
return self._ssh_client
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Node info helpers
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
Reference in new issue
Block a user