From 864cdf33a7de2ed89fb34427d53b096f7eeabf89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 6 May 2025 10:24:58 +0200 Subject: [PATCH 1/2] Host: actual setup ssh controlmaster before we were not entering the context manager --- pkgs/clan-cli/clan_cli/machines/machines.py | 13 +++++++------ pkgs/clan-cli/clan_cli/ssh/host.py | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/machines/machines.py b/pkgs/clan-cli/clan_cli/machines/machines.py index cc370b46b..e681d07d6 100644 --- a/pkgs/clan-cli/clan_cli/machines/machines.py +++ b/pkgs/clan-cli/clan_cli/machines/machines.py @@ -149,13 +149,14 @@ class Machine: @contextmanager def target_host(self) -> Iterator[Host]: - yield parse_deployment_address( + with parse_deployment_address( self.name, self.target_host_address, self.host_key_check, private_key=self.private_key, meta={"machine": self}, - ) + ) as target_host: + yield target_host @contextmanager def build_host(self) -> Iterator[Host | None]: @@ -165,18 +166,18 @@ class Machine: """ build_host = self.override_build_host or self.deployment.get("buildHost") if build_host is None: - with self.target_host() as target_host: - yield target_host + yield None return # enable ssh agent forwarding to allow the build host to access the target host - yield parse_deployment_address( + with parse_deployment_address( self.name, build_host, self.host_key_check, forward_agent=True, private_key=self.private_key, meta={"machine": self}, - ) + ) as build_host: + yield build_host @cached_property def deploy_as_root(self) -> bool: diff --git a/pkgs/clan-cli/clan_cli/ssh/host.py b/pkgs/clan-cli/clan_cli/ssh/host.py index 0b8d617e9..2acafb6b2 100644 --- a/pkgs/clan-cli/clan_cli/ssh/host.py +++ b/pkgs/clan-cli/clan_cli/ssh/host.py @@ -47,9 +47,10 @@ class Host: self.ssh_options["ControlPath"] = str(control_path / "clan-%h-%p-%r") self.ssh_options["ControlPersist"] = "30m" - def __enter__(self) -> None: + def __enter__(self) -> "Host": self._temp_dir = TemporaryDirectory(prefix="clan-ssh-") self.setup_control_master(Path(self._temp_dir.name)) + return self def __exit__( self, From ec76d5f8e5d947569e21af25d0f697bdfccf66c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 6 May 2025 11:08:40 +0200 Subject: [PATCH 2/2] only apply ssh control master to local ssh connection --- pkgs/clan-cli/clan_cli/machines/update.py | 6 +++--- pkgs/clan-cli/clan_cli/ssh/host.py | 26 ++++++++++++++--------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/machines/update.py b/pkgs/clan-cli/clan_cli/machines/update.py index abba56b43..609b6e367 100644 --- a/pkgs/clan-cli/clan_cli/machines/update.py +++ b/pkgs/clan-cli/clan_cli/machines/update.py @@ -179,11 +179,11 @@ def deploy_machine(machine: Machine) -> None: switch_cmd = [f"{machine._class_}-rebuild", "switch", *nix_options] test_cmd = [f"{machine._class_}-rebuild", "test", *nix_options] - env = host.nix_ssh_env(None) + remote_env = host.nix_ssh_env(None, local_ssh=False) ret = host.run( switch_cmd, RunOpts(check=False, msg_color=MsgColor(stderr=AnsiColor.DEFAULT)), - extra_env=env, + extra_env=remote_env, become_root=become_root, ) @@ -209,7 +209,7 @@ def deploy_machine(machine: Machine) -> None: msg_color=MsgColor(stderr=AnsiColor.DEFAULT), needs_user_terminal=True, ), - extra_env=env, + extra_env=remote_env, become_root=become_root, ) diff --git a/pkgs/clan-cli/clan_cli/ssh/host.py b/pkgs/clan-cli/clan_cli/ssh/host.py index 2acafb6b2..108b0025a 100644 --- a/pkgs/clan-cli/clan_cli/ssh/host.py +++ b/pkgs/clan-cli/clan_cli/ssh/host.py @@ -42,14 +42,8 @@ class Host: _temp_dir: TemporaryDirectory | None = None - def setup_control_master(self, control_path: Path) -> None: - self.ssh_options["ControlMaster"] = "auto" - self.ssh_options["ControlPath"] = str(control_path / "clan-%h-%p-%r") - self.ssh_options["ControlPersist"] = "30m" - def __enter__(self) -> "Host": self._temp_dir = TemporaryDirectory(prefix="clan-ssh-") - self.setup_control_master(Path(self._temp_dir.name)) return self def __exit__( @@ -188,15 +182,17 @@ class Host: # Run the ssh command return run(ssh_cmd, opts) - def nix_ssh_env(self, env: dict[str, str] | None) -> dict[str, str]: + def nix_ssh_env( + self, env: dict[str, str] | None, local_ssh: bool = True + ) -> dict[str, str]: if env is None: env = {} - env["NIX_SSHOPTS"] = " ".join(self.ssh_cmd_opts) + env["NIX_SSHOPTS"] = " ".join(self.ssh_cmd_opts(local_ssh=local_ssh)) return env - @property def ssh_cmd_opts( self, + local_ssh: bool = True, ) -> list[str]: ssh_opts = ["-A"] if self.forward_agent else [] if self.port: @@ -210,6 +206,16 @@ class Host: if self.private_key: ssh_opts.extend(["-i", str(self.private_key)]) + if local_ssh and self._temp_dir: + ssh_opts.extend(["-o", "ControlPersist=30m"]) + ssh_opts.extend( + [ + "-o", + f"ControlPath={Path(self._temp_dir.name) / 'clan-%h-%p-%r'}", + ] + ) + ssh_opts.extend(["-o", "ControlMaster=auto"]) + return ssh_opts def ssh_cmd( @@ -227,7 +233,7 @@ class Host: self.password, ] - ssh_opts = self.ssh_cmd_opts + ssh_opts = self.ssh_cmd_opts() if verbose_ssh or self.verbose_ssh: ssh_opts.extend(["-v"]) if tty: