fix(ssh): 快速添加跳过主机密钥校验并中文化错误
凭据轮询在 SSH_STRICT 默认 true 时因 HostKeyNotVerifiable 在认证前失败; 探测始终 known_hosts=None,默认 strict 改为 false,错误信息统一中文。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -21,10 +21,16 @@ from server.domain.models import Server
|
||||
from server.utils.files_elevation import FilesElevation
|
||||
from server.utils.posix_paths import posix_dirname
|
||||
from server.infrastructure.database.crypto import decrypt_value
|
||||
from server.utils.sync_error_message import translate_ssh_error_message
|
||||
|
||||
logger = logging.getLogger("nexus.asyncssh_pool")
|
||||
|
||||
|
||||
def _localized_ssh_error(exc: Exception, *, max_len: int = 200) -> str:
|
||||
raw = str(exc)[:max_len]
|
||||
return translate_ssh_error_message(raw) or raw
|
||||
|
||||
|
||||
async def try_ssh_login(
|
||||
host: str,
|
||||
port: int,
|
||||
@@ -35,8 +41,6 @@ async def try_ssh_login(
|
||||
timeout: float = 8.0,
|
||||
) -> tuple[bool, str]:
|
||||
"""One-shot SSH login probe (not pooled). Returns (ok, error_message)."""
|
||||
from server.config import settings
|
||||
|
||||
if not password and not private_key:
|
||||
return False, "未提供密码或私钥"
|
||||
|
||||
@@ -45,9 +49,9 @@ async def try_ssh_login(
|
||||
"port": port or 22,
|
||||
"username": username or "root",
|
||||
"connect_timeout": timeout,
|
||||
# 凭据轮询探测未知主机:跳过主机密钥校验(运维平台标准;持久连接见 SSH_STRICT_HOST_CHECKING)
|
||||
"known_hosts": None,
|
||||
}
|
||||
if settings.SSH_STRICT_HOST_CHECKING.lower() in ("false", "0", "no"):
|
||||
connect_kwargs["known_hosts"] = None
|
||||
|
||||
if password:
|
||||
connect_kwargs["password"] = password
|
||||
@@ -59,9 +63,9 @@ async def try_ssh_login(
|
||||
conn = await asyncssh.connect(**connect_kwargs)
|
||||
return True, ""
|
||||
except asyncssh.Error as e:
|
||||
return False, str(e)[:200]
|
||||
return False, _localized_ssh_error(e)
|
||||
except Exception as e:
|
||||
return False, str(e)[:200]
|
||||
return False, _localized_ssh_error(e)
|
||||
finally:
|
||||
if conn is not None:
|
||||
try:
|
||||
@@ -240,7 +244,10 @@ class AsyncSSHPool:
|
||||
conn = await asyncssh.connect(**connect_kwargs)
|
||||
return conn
|
||||
except asyncssh.Error as e:
|
||||
raise ConnectionError(f"SSH connection failed to {server.domain}:{server.port}: {e}") from e
|
||||
msg = _localized_ssh_error(e)
|
||||
raise ConnectionError(
|
||||
f"SSH 连接失败 {server.domain}:{server.port or 22}:{msg}"
|
||||
) from e
|
||||
|
||||
async def _evict_one(self) -> bool:
|
||||
"""Evict the oldest idle connection from the pool.
|
||||
@@ -459,7 +466,7 @@ async def exec_ssh_command(server: Server, command: str, timeout: int = 30) -> d
|
||||
return {
|
||||
"status": "timeout",
|
||||
"stdout": "",
|
||||
"stderr": f"Command timed out after {timeout}s",
|
||||
"stderr": f"命令执行超时({timeout} 秒)",
|
||||
"exit_code": -1,
|
||||
}
|
||||
except ConnectionError as e:
|
||||
|
||||
Reference in New Issue
Block a user