"""Private, pinned SSH access for this workspace; never expose credential values.""" from __future__ import annotations import importlib.util import os from pathlib import Path import re import paramiko from local_test_paths import SSH_KNOWN_HOSTS ROOT = Path(__file__).resolve().parents[3] def connect(): gate_path = ROOT / "测试相关资料/核桃派的用户名和密码和ip/prepare_credentials.py" spec = importlib.util.spec_from_file_location("qms_credentials", gate_path) gate = importlib.util.module_from_spec(spec) spec.loader.exec_module(gate) if not gate.ensure_credentials(gate_path.parent): raise RuntimeError("设备凭据门禁未通过") fields = gate._parse_fields(gate_path.with_name(gate.PRIVATE_NAME)) host = fields["IP"].strip() port_match = re.search(r"(?:^|\s)-p\s+(\d+)", fields["SSH"]) port = int(port_match.group(1)) if port_match else 22 trust = SSH_KNOWN_HOSTS trust.parent.mkdir(parents=True, exist_ok=True) client = paramiko.SSHClient() if trust.exists(): client.load_host_keys(str(trust)) # First-use trust is pinned in a private local store. Changed keys are # rejected by Paramiko before this policy can be invoked. class PinFirstUse(paramiko.MissingHostKeyPolicy): def missing_host_key(self, ssh, hostname, key): ssh.get_host_keys().add(hostname, key.get_name(), key) ssh.save_host_keys(str(trust)) client.set_missing_host_key_policy(PinFirstUse()) try: client.connect(host, port=port, username=fields["用户名"], password=fields["密码"], look_for_keys=False, allow_agent=False, timeout=10, banner_timeout=10, auth_timeout=10) except Exception: client.close() raise RuntimeError("SSH 连接或主机密钥检查失败;未输出凭据") from None return client, fields def redact(text, fields): values = sorted((v for v in fields.values() if len(v) >= 4), key=len, reverse=True) for value in values: text = text.replace(value, "[private]") return text def execute(client, command, *, password=None, timeout=30): if password is not None: import shlex command = "sudo -S -p '' -- sh -c " + shlex.quote(command) stdin, stdout, stderr = client.exec_command(command, timeout=timeout) if password is not None: stdin.write(password + "\n") stdin.flush() out, err = stdout.read().decode("utf-8", "replace"), stderr.read().decode("utf-8", "replace") return stdout.channel.recv_exit_status(), out, err