connectiopn fix round 2 2
This commit is contained in:
@ -9,6 +9,7 @@ Pflicht: ohne gepinnten Fingerprint wird die Verbindung abgelehnt.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import asyncssh
|
||||
@ -380,6 +381,35 @@ def resolve_ssh_username(host: dict, credential_username: str | None) -> str:
|
||||
return username
|
||||
|
||||
|
||||
HOST_KEY_CONNECT_TIMEOUT = 10
|
||||
|
||||
|
||||
async def _fetch_server_host_key(address: str, port: int) -> asyncssh.SSHKey | None:
|
||||
"""Reiner SSH-Key-Exchange ohne Anmeldung, mit Zeitlimit.
|
||||
|
||||
ACHTUNG, das war der Bug hinter drei Fehlersuchen (Phase 12 + Nachtraege):
|
||||
asyncssh.get_server_host_key() nimmt -- ANDERS als asyncssh.connect() --
|
||||
KEIN **kwargs entgegen. Seine Parameterliste ist in asyncssh 2.18.0
|
||||
abschliessend:
|
||||
|
||||
get_server_host_key(host='', port=(), *, tunnel=(), proxy_command=(),
|
||||
family=(), flags=0, local_addr=(), sock=None,
|
||||
client_version=(), kex_algs=(),
|
||||
server_host_key_algs=(), config=(), options=None)
|
||||
|
||||
'connect_timeout' ist NICHT dabei (nur asyncssh.connect() reicht unbekannte
|
||||
Argumente ueber **kwargs an SSHClientConnectionOptions weiter). Ein Aufruf
|
||||
mit connect_timeout=... endete deshalb SOFORT -- vor jedem Socket, daher
|
||||
"in unter einer Sekunde" -- in einem TypeError. Das Zeitlimit wird hier
|
||||
darum von aussen per asyncio.wait_for() gesetzt; das ist zugleich
|
||||
unabhaengig davon, wie einzelne asyncssh-Versionen ihre Optionen benennen.
|
||||
"""
|
||||
return await asyncio.wait_for(
|
||||
asyncssh.get_server_host_key(address, port=port),
|
||||
timeout=HOST_KEY_CONNECT_TIMEOUT,
|
||||
)
|
||||
|
||||
|
||||
async def _verified_host_key(
|
||||
conn: aiosqlite.Connection, host: dict
|
||||
) -> asyncssh.SSHKey:
|
||||
@ -401,17 +431,25 @@ async def _verified_host_key(
|
||||
raise HostKeyNotPinnedError(host["id"])
|
||||
|
||||
try:
|
||||
observed_key = await asyncssh.get_server_host_key(
|
||||
host["address"], port=host["port"], connect_timeout=10
|
||||
)
|
||||
observed_key = await _fetch_server_host_key(host["address"], host["port"])
|
||||
except asyncio.TimeoutError as exc:
|
||||
raise HostKeyDiscoveryError(
|
||||
host["id"], f"Zeitlimit von {HOST_KEY_CONNECT_TIMEOUT}s ueberschritten"
|
||||
) from exc
|
||||
except TypeError:
|
||||
# NICHT maskieren: ein TypeError aus diesem Aufruf ist kein
|
||||
# Netzwerkproblem, sondern ein Aufruffehler im eigenen Code. Genau so
|
||||
# wurde der connect_timeout-Bug oben mehrfach als "Ziel nicht
|
||||
# erreichbar" fehlgedeutet, nachdem das except hier auf Exception
|
||||
# verbreitert worden war.
|
||||
raise
|
||||
except Exception as exc:
|
||||
# Bewusst dieselbe Verbreiterung wie in discover_and_store_host_key():
|
||||
# (asyncssh.Error, OSError) allein fing nicht jede Art von Fehlschlag
|
||||
# dieses externen Aufrufs ab (z.B. asyncio.TimeoutError vor Python
|
||||
# 3.11, kein OSError). Ein hier unbehandelter Fehler wuerde ungefiltert
|
||||
# bis in terminal_ws.py/sftp.py durchreichen -- SSH_SETUP_ERRORS
|
||||
# erkennt HostKeyDiscoveryError dort explizit und zeigt eine saubere
|
||||
# deutsche Meldung statt eines generischen Sitzungsabbruchs.
|
||||
# Bewusst breit: (asyncssh.Error, OSError) allein fing nicht jede Art
|
||||
# von Fehlschlag dieses externen Aufrufs ab. Ein hier unbehandelter
|
||||
# Fehler wuerde ungefiltert bis in terminal_ws.py/sftp.py
|
||||
# durchreichen -- SSH_SETUP_ERRORS erkennt HostKeyDiscoveryError dort
|
||||
# explizit und zeigt eine saubere deutsche Meldung statt eines
|
||||
# generischen Sitzungsabbruchs.
|
||||
raise HostKeyDiscoveryError(host["id"], str(exc)) from exc
|
||||
if observed_key is None:
|
||||
raise HostKeyDiscoveryError(host["id"], "Das Ziel hat keinen Host-Key gesendet")
|
||||
@ -554,9 +592,17 @@ async def discover_and_store_host_key(
|
||||
raise HostNotConfiguredError("Host-Key-Ermittlung ist nur fuer SSH-Ziele moeglich")
|
||||
|
||||
try:
|
||||
key = await asyncssh.get_server_host_key(
|
||||
host["address"], port=host["port"], connect_timeout=10
|
||||
)
|
||||
key = await _fetch_server_host_key(host["address"], host["port"])
|
||||
except asyncio.TimeoutError as exc:
|
||||
logger.warning("Host-Key-Ermittlung fuer Host %s: Zeitlimit ueberschritten", host_id)
|
||||
raise HostKeyDiscoveryError(
|
||||
host_id, f"Zeitlimit von {HOST_KEY_CONNECT_TIMEOUT}s ueberschritten"
|
||||
) from exc
|
||||
except TypeError:
|
||||
# Siehe _fetch_server_host_key(): Aufruffehler im eigenen Code duerfen
|
||||
# NICHT als "Ziel nicht erreichbar" verkleidet werden.
|
||||
logger.exception("Host-Key-Ermittlung fuer Host %s: fehlerhafter asyncssh-Aufruf", host_id)
|
||||
raise
|
||||
except Exception as exc:
|
||||
logger.warning("Host-Key-Ermittlung fuer Host %s fehlgeschlagen: %s", host_id, exc)
|
||||
raise HostKeyDiscoveryError(host_id, str(exc)) from exc
|
||||
|
||||
Reference in New Issue
Block a user