diff --git a/src/revpicommander/helper.py b/src/revpicommander/helper.py index a2e674c..900554e 100644 --- a/src/revpicommander/helper.py +++ b/src/revpicommander/helper.py @@ -16,6 +16,7 @@ from re import search from threading import Lock from uuid import uuid4 from xmlrpc.client import Binary, ServerProxy, Transport +from configparser import ConfigParser from PyQt5 import QtCore import asyncssh @@ -365,10 +366,12 @@ class ConnectionManager(QtCore.QThread): ssh_tunnel_server = None ssh_tunnel_port = 0 + ssh_tunnel_socket = None socket.setdefaulttimeout(revpi_settings.timeout) if revpi_settings.ssh_use_tunnel: + # We first connect to find out which target to tunnel ssh_tunnel_server = SSHLocalTunnel( revpi_settings.port, revpi_settings.address, @@ -377,6 +380,37 @@ class ConnectionManager(QtCore.QThread): try: ssh_tunnel_port = ssh_tunnel_server.connect_by_credentials(revpi_settings.ssh_user, ssh_pass) + # Check for Unix socket on remote system + try: + stdout, stderr = ssh_tunnel_server.send_cmd("cat /etc/revpipyload/revpipyload.conf") + if stdout: + config = ConfigParser() + config.read_string(stdout) + if config.has_section("XMLRPC"): + bindip = config.get("XMLRPC", "bindip", fallback="").strip() + if bindip == "socket": + ssh_tunnel_socket = "/run/revpipyload/xmlrpc.socket" + elif bindip.startswith("/") or bindip.startswith("./"): + ssh_tunnel_socket = bindip + + if ssh_tunnel_socket: + log.debug("Using remote unix socket: %s", ssh_tunnel_socket) + # Forward local port 0 (dynamic) to remote unix socket + ssh_tunnel_server.disconnect() + ssh_tunnel_server = SSHLocalTunnel( + ssh_tunnel_socket, + revpi_settings.address, + revpi_settings.ssh_port + ) + ssh_tunnel_port = ssh_tunnel_server.connect_by_credentials( + revpi_settings.ssh_user, ssh_pass + ) + else: + log.debug("Using remote TCP socket: %s", bindip) + + except Exception as e: + log.warning(f"Could not check remote config for unix socket: {e}") + if getattr(revpi_settings, "ssh_enable_revpipyload", False): ssh_tunnel_server.send_cmd("sudo systemctl enable --now revpipyload") @@ -723,7 +757,7 @@ def create_server_proxy(revpi_settings: RevPiSettings, ssh_tunnel_port: int = No :param ssh_tunnel_port: Use this port if an SSH tunnel is already established :return: ServerProxy instance """ - if revpi_settings.is_unix_socket: + if not ssh_tunnel_port and revpi_settings.is_unix_socket: return ServerProxy("http://localhost", transport=UnixStreamTransport(revpi_settings.address)) if ssh_tunnel_port: diff --git a/src/revpicommander/ssh_tunneling/server.py b/src/revpicommander/ssh_tunneling/server.py index 691a46d..d00a25a 100644 --- a/src/revpicommander/ssh_tunneling/server.py +++ b/src/revpicommander/ssh_tunneling/server.py @@ -18,15 +18,15 @@ log = getLogger("ssh_tunneling") class SSHLocalTunnel: - def __init__(self, remote_tunnel_port: int, ssh_host: str, ssh_port: int = 22): + def __init__(self, remote_target: Union[int, str], ssh_host: str, ssh_port: int = 22): """ - Connect to a ssh remote host and tunnel a port to your host. + Connect to a ssh remote host and tunnel a port or unix socket to your host. - :param remote_tunnel_port: Port on the remote host to tunnel through ssh + :param remote_target: Port or unix socket path on the remote host to tunnel through ssh :param ssh_host: ssh remote host address :param ssh_port: ssh remote host port """ - self._remote_tunnel_port = remote_tunnel_port + self._remote_target = remote_target self._ssh_host = ssh_host self._ssh_port = ssh_port @@ -72,10 +72,15 @@ class SSHLocalTunnel: config=None, # Do not parse local config ) as conn: self._conn = conn - # Forward local port 0 (dynamic) to remote 127.0.0.1:remote_tunnel_port - self._server = await conn.forward_local_port( - '127.0.0.1', 0, '127.0.0.1', self._remote_tunnel_port - ) + # Forward local port 0 (dynamic) to remote target (port or unix socket) + if isinstance(self._remote_target, int): + self._server = await conn.forward_local_port( + '127.0.0.1', 0, '127.0.0.1', self._remote_target + ) + else: + self._server = await conn.forward_local_port_to_path( + '127.0.0.1', 0, self._remote_target + ) self._local_tunnel_port = self._server.get_port() self._started.set()