66 lines
2.6 KiB
Python
66 lines
2.6 KiB
Python
"""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
|