From 9805aae66f512a55116ef021c368e62aab9b42c3 Mon Sep 17 00:00:00 2001 From: shrey Date: Sat, 8 Mar 2025 03:40:49 +0530 Subject: [PATCH 1/2] add: add public key to authorized keys on a windows machine --- datashuttle/utils/ssh.py | 67 +++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/datashuttle/utils/ssh.py b/datashuttle/utils/ssh.py index 2bf5db5c..e78c1913 100644 --- a/datashuttle/utils/ssh.py +++ b/datashuttle/utils/ssh.py @@ -2,6 +2,8 @@ from typing import TYPE_CHECKING +import paramiko.ssh_exception + if TYPE_CHECKING: from datashuttle.configs.config_class import Configs @@ -45,6 +47,33 @@ def connect_client_core( ) +def is_windows(client: paramiko.SSHClient) -> bool: + try: + stdin, stdout, stderr = client.exec_command("ver") + output = stdout.read().decode().strip().lower() + if "windows" in output: + return True + + stdin, stdout, stderr = client.exec_command( + "powershell -Command echo 'powershell'" + ) + output = stdout.read().decode().strip().lower() + if "powershell" in output: + return True + + return False + except Exception: + return False + + +def is_windows_user_admin(client: paramiko.SSHClient) -> bool: + stdin, stdout, stderr = client.exec_command("whoami /groups") + output = stdout.read().decode() + if "BUILTIN\\Administrators" in output: + return True + return False + + def add_public_key_to_central_authorized_keys( cfg: Configs, password: str, log=True ) -> None: @@ -61,15 +90,37 @@ def add_public_key_to_central_authorized_keys( connect_client_with_logging(client, cfg, password=password) else: connect_client_core(client, cfg, password=password) + if is_windows(client): + if is_windows_user_admin(client): + client.exec_command("mkdir C:\\ProgramData\\ssh") + client.exec_command( + # double >> for concatenate + f"echo {key.get_name()} {key.get_base64()}" + f">> C:\\ProgramData\\ssh\\administrators_authorized_keys" + ) + else: + client.exec_command("mkdir %USERPROFILE%\\.ssh") + client.exec_command( + # double >> for concatenate + f"echo {key.get_name()} {key.get_base64()}" + f">> %USERPROFILE%\\.ssh\\authorized_keys" + ) + client.exec_command( + "icacls %USERPROFILE%\\.ssh /inheritance:r /grant %USERNAME%:(OI)(CI)F" + ) + client.exec_command( + "icacls %USERPROFILE%\.ssh\\authorized_keys /inheritance:r /grant %USERNAME%:(F)" + ) - client.exec_command("mkdir -p ~/.ssh/") - client.exec_command( - # double >> for concatenate - f'echo "{key.get_name()} {key.get_base64()}" ' - f">> ~/.ssh/authorized_keys" - ) - client.exec_command("chmod 644 ~/.ssh/authorized_keys") - client.exec_command("chmod 700 ~/.ssh/") + else: + client.exec_command("mkdir -p ~/.ssh") + client.exec_command( + # double >> for concatenate + f'echo "{key.get_name()} {key.get_base64()}" ' + f">> ~/.ssh/authorized_keys" + ) + client.exec_command("chmod 644 ~/.ssh/authorized_keys") + client.exec_command("chmod 700 ~/.ssh/") def generate_and_write_ssh_key(ssh_key_path: Path) -> None: From e82c61a1503502c119d6847bcdad6556de0712fa Mon Sep 17 00:00:00 2001 From: shrey Date: Mon, 10 Mar 2025 18:57:43 +0530 Subject: [PATCH 2/2] add: docstring and comments --- datashuttle/utils/ssh.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/datashuttle/utils/ssh.py b/datashuttle/utils/ssh.py index e78c1913..f5ab7f0d 100644 --- a/datashuttle/utils/ssh.py +++ b/datashuttle/utils/ssh.py @@ -48,12 +48,18 @@ def connect_client_core( def is_windows(client: paramiko.SSHClient) -> bool: + """ + Checks the target machine for a Windows OS using two approaches + 1. execute "ver" : outputs "Microsoft Windows " + 2. check for the presence of powershell + """ try: stdin, stdout, stderr = client.exec_command("ver") output = stdout.read().decode().strip().lower() if "windows" in output: return True + # rechecking in case the previous check fails for some reason stdin, stdout, stderr = client.exec_command( "powershell -Command echo 'powershell'" ) @@ -105,11 +111,13 @@ def add_public_key_to_central_authorized_keys( f"echo {key.get_name()} {key.get_base64()}" f">> %USERPROFILE%\\.ssh\\authorized_keys" ) + # setup permissions for .ssh directory client.exec_command( "icacls %USERPROFILE%\\.ssh /inheritance:r /grant %USERNAME%:(OI)(CI)F" ) + # setup permissions for authorized_keys file client.exec_command( - "icacls %USERPROFILE%\.ssh\\authorized_keys /inheritance:r /grant %USERNAME%:(F)" + "icacls %USERPROFILE%\\.ssh\\authorized_keys /inheritance:r /grant %USERNAME%:(F)" ) else: