feat: Enhance SSH tunnel handling with Unix socket support

Support forwarding Unix sockets in SSH tunnels by extending
`SSHLocalTunnel`. Update logic to detect and handle remote Unix socket
configurations.

Signed-off-by: Sven Sager <akira@narux.de>
This commit is contained in:
2026-08-06 10:00:14 +02:00
parent 23c09949bb
commit 0582925de6
2 changed files with 48 additions and 9 deletions
+35 -1
View File
@@ -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:
+11 -6
View File
@@ -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,9 +72,14 @@ 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
# 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_tunnel_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()