From bebd542a2ec4feb499c6f941ff08d9a80459099f Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 21 Mar 2026 13:41:55 +0100 Subject: [PATCH] =?UTF-8?q?feat(tunnel):=20add=20direction=20field=20?= =?UTF-8?q?=E2=80=94=20support=20local=20(-L)=20port=20forwards?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously build_ssh_command only generated -R (reverse) tunnels. The k3s API tunnel needs -L (local forward: workstation:16443 → CoulombCore:6443) so kubectl can reach the cluster API directly. - TunnelConfig.direction: "reverse" (default) | "local" - config.py: parse direction from YAML, validate allowed values - manager.py: choose -R or -L flag based on direction Co-Authored-By: Claude Sonnet 4.6 --- src/bridge/config.py | 5 +++++ src/bridge/manager.py | 8 ++++++-- src/bridge/models.py | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/bridge/config.py b/src/bridge/config.py index 1000be3..241d22e 100644 --- a/src/bridge/config.py +++ b/src/bridge/config.py @@ -87,6 +87,10 @@ def _parse_tunnel(name: str, data: dict) -> TunnelConfig: timeout_seconds=hc.get("timeout_seconds", 5), ) + direction = str(data.get("direction", "reverse")) + if direction not in ("reverse", "local"): + raise ConfigError(f"Tunnel '{name}' direction must be 'reverse' or 'local', got: {direction!r}") + return TunnelConfig( name=name, host=str(data["host"]), @@ -97,6 +101,7 @@ def _parse_tunnel(name: str, data: dict) -> TunnelConfig: actor=str(data["actor"]), reconnect=reconnect, health_check=health_check, + direction=direction, ) diff --git a/src/bridge/manager.py b/src/bridge/manager.py index 2a91e04..371ae41 100644 --- a/src/bridge/manager.py +++ b/src/bridge/manager.py @@ -18,12 +18,16 @@ log = logging.getLogger(__name__) def build_ssh_command(cfg: TunnelConfig) -> List[str]: - """Build the SSH reverse tunnel command.""" + """Build the SSH tunnel command (reverse -R or local -L).""" key = os.path.expanduser(cfg.ssh_key) + if cfg.direction == "local": + forward_flag = ["-L", f"{cfg.local_port}:127.0.0.1:{cfg.remote_port}"] + else: + forward_flag = ["-R", f"{cfg.remote_port}:127.0.0.1:{cfg.local_port}"] return [ "ssh", "-N", - "-R", f"{cfg.remote_port}:127.0.0.1:{cfg.local_port}", + *forward_flag, "-i", key, "-o", "ServerAliveInterval=10", "-o", "ServerAliveCountMax=3", diff --git a/src/bridge/models.py b/src/bridge/models.py index 8beca65..899f0d4 100644 --- a/src/bridge/models.py +++ b/src/bridge/models.py @@ -40,6 +40,7 @@ class TunnelConfig: actor: str reconnect: ReconnectPolicy = field(default_factory=ReconnectPolicy) health_check: Optional[HealthCheckConfig] = None + direction: str = "reverse" # "reverse" (-R) or "local" (-L) @dataclass