fixes and improvements

This commit is contained in:
2026-05-07 15:43:22 +02:00
parent 1d1f052d18
commit 6d8b08d30f
6 changed files with 11 additions and 8 deletions
@@ -20,6 +20,8 @@ from typing import Any
import paramiko
from media_library_viewer_api.services.known_hosts import ensure_known_host
logger = logging.getLogger(__name__)
@@ -64,12 +66,14 @@ class RemoteSSHClient:
def connect(self) -> paramiko.SSHClient:
"""Create or reuse the Paramiko connection.
Unknown host keys are rejected. The application can synthesize a managed
known_hosts file under its cache directory so users do not need to mount
their local SSH directory into the container.
Unknown host keys are recorded on first contact in the managed
known_hosts file when one is configured. After that, strict checking
remains in effect so host key changes are still rejected.
"""
if self._client:
return self._client
if self.known_hosts_path:
ensure_known_host(self.host, self.port, Path(self.known_hosts_path), strict=True)
client = paramiko.SSHClient()
client.load_system_host_keys()
if self.known_hosts_path and Path(self.known_hosts_path).is_file():
@@ -18,7 +18,6 @@ from media_library_viewer_api.clients.jellyfin import JellyfinClient
from media_library_viewer_api.clients.jellyseerr import JellyseerrClient
from media_library_viewer_api.clients.local import LocalCommandClient
from media_library_viewer_api.clients.ssh import RemoteSSHClient
from media_library_viewer_api.services.known_hosts import ensure_known_host
from media_library_viewer_api.config import get_settings
from media_library_viewer_api.services.mail_queue import MailQueue, get_mail_queue as _get_mail_queue
from media_library_viewer_api.services.monitoring_poller import (
@@ -153,7 +152,6 @@ def get_ssh_client(request: Request = None):
return LocalCommandClient()
if machine and machine.get("host") and machine.get("username"):
known_hosts_path = get_settings().ssh_known_hosts_file
ensure_known_host(str(machine.get("host")), int(machine.get("port") or 22), known_hosts_path)
key_data = None
key_passphrase = None
ssh_key_id = str(machine.get("ssh_key_id") or "").strip()
@@ -190,7 +188,6 @@ def get_ssh_client(request: Request = None):
)
if not settings.ssh_key_path:
raise RuntimeError("No SSH machine is configured and SSH key settings must be configured")
ensure_known_host(settings.ssh_host, settings.ssh_port, settings.ssh_known_hosts_file)
return _ssh_client_for(("legacy", settings.ssh_host, settings.ssh_username, settings.ssh_port, settings.ssh_key_path, settings.ssh_password or None, None, None, str(settings.ssh_known_hosts_file)))
@@ -14,7 +14,6 @@ from media_library_viewer_api.clients.local import LocalCommandClient
from media_library_viewer_api.clients.ssh import RemoteSSHClient
from media_library_viewer_api.config import get_settings
from media_library_viewer_api.dependencies import get_settings_store
from media_library_viewer_api.services.known_hosts import ensure_known_host
from media_library_viewer_api.services.settings_store import SettingsStore
logger = logging.getLogger(__name__)
@@ -67,7 +66,6 @@ def _client_for_machine(store: SettingsStore, machine: dict[str, Any]):
raise HTTPException(status_code=400, detail="SSH machine is missing host or username")
settings = get_settings()
ensure_known_host(host, int(machine.get("port") or 22), settings.ssh_known_hosts_file)
private_key = str(machine.get("ssh_private_key") or "")
passphrase = str(machine.get("ssh_private_key_passphrase") or "")
@@ -41,12 +41,14 @@ def build_machine_client(machine: dict[str, Any]):
key_directory = str(machine.get("key_directory") or "").strip()
key_name = str(machine.get("key_name") or "").strip()
key_path = f"{key_directory}/{key_name}" if key_directory and key_name else None
settings = get_settings()
return RemoteSSHClient(
host=str(machine.get("host") or ""),
username=str(machine.get("username") or ""),
port=int(machine.get("port") or 22),
key_filename=key_path,
password=str(machine.get("password") or "") or None,
known_hosts_path=str(settings.ssh_known_hosts_file),
)