Skip to content

Commit c2f4643

Browse files
Refactor SSH connection to make adding passwords easier. (#602)
* Refactor SSH method. * Tidy up. * Update datashuttle/utils/rclone.py Co-authored-by: Shrey Singh <[email protected]> * Update datashuttle/utils/ssh.py Co-authored-by: Shrey Singh <[email protected]> * Fix docstring. * Small tidy ups. --------- Co-authored-by: Shrey Singh <[email protected]>
1 parent 3f01b0b commit c2f4643

File tree

9 files changed

+200
-278
lines changed

9 files changed

+200
-278
lines changed

datashuttle/configs/config_class.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ def __init__(
6262

6363
self.logging_path: Path
6464
self.hostkeys_path: Path
65-
self.ssh_key_path: Path
6665
self.project_metadata_path: Path
6766

6867
def setup_after_load(self) -> None:
@@ -300,8 +299,6 @@ def init_paths(self) -> None:
300299
self.project_name
301300
)
302301

303-
self.ssh_key_path = datashuttle_path / f"{self.project_name}_ssh_key"
304-
305302
self.hostkeys_path = datashuttle_path / "hostkeys"
306303

307304
self.logging_path = self.make_and_get_logging_path()

datashuttle/datashuttle_class.py

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
TopLevelFolder,
2929
)
3030

31-
import paramiko
3231
import yaml
3332

3433
from datashuttle.configs import (
@@ -820,46 +819,26 @@ def setup_ssh_connection(self) -> None:
820819
"setup-ssh-connection-to-central-server", local_vars=locals()
821820
)
822821

823-
verified = ssh.verify_ssh_central_host(
822+
verified = ssh.verify_ssh_central_host_api(
824823
self.cfg["central_host_id"],
825824
self.cfg.hostkeys_path,
826825
log=True,
827826
)
828827

829828
if verified:
830-
ssh.setup_ssh_key(self.cfg, log=True)
831-
self._setup_rclone_central_ssh_config(log=True)
829+
private_key_str = ssh.setup_ssh_key_api(self.cfg, log=True)
830+
831+
self._setup_rclone_central_ssh_config(private_key_str, log=True)
832832

833833
rclone.check_successful_connection_and_raise_error_on_fail(
834834
self.cfg
835835
)
836836

837-
ds_logger.close_log_filehandler()
838-
839-
@requires_ssh_configs
840-
@check_is_not_local_project
841-
def write_public_key(self, filepath: str) -> None:
842-
"""Save the public SSH key to a specified filepath.
843-
844-
By default, only the SSH private key is stored in the
845-
datashuttle configs folder. Use this function to save
846-
the public key.
847-
848-
Parameters
849-
----------
850-
filepath
851-
Full filepath (including filename) to write the
852-
public key to.
853-
854-
"""
855-
key: paramiko.RSAKey
856-
key = paramiko.RSAKey.from_private_key_file(
857-
self.cfg.ssh_key_path.as_posix()
858-
)
837+
utils.log_and_message(
838+
"SSH key pair setup successfully. SSH key saved to the RClone config file."
839+
)
859840

860-
with open(filepath, "w") as public:
861-
public.write(key.get_base64())
862-
public.close()
841+
ds_logger.close_log_filehandler()
863842

864843
# -------------------------------------------------------------------------
865844
# Google Drive
@@ -1588,11 +1567,13 @@ def _make_project_metadata_if_does_not_exist(self) -> None:
15881567
"""
15891568
folders.create_folders(self.cfg.project_metadata_path, log=False)
15901569

1591-
def _setup_rclone_central_ssh_config(self, log: bool) -> None:
1570+
def _setup_rclone_central_ssh_config(
1571+
self, private_key_str: str, log: bool
1572+
) -> None:
15921573
rclone.setup_rclone_config_for_ssh(
15931574
self.cfg,
15941575
self.cfg.get_rclone_config_name("ssh"),
1595-
self.cfg.ssh_key_path,
1576+
private_key_str,
15961577
log=log,
15971578
)
15981579

datashuttle/tui/interface.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,14 @@ def setup_key_pair_and_rclone_config(
511511
) -> InterfaceOutput:
512512
"""Set up SSH key pair and associated rclone configuration."""
513513
try:
514+
rsa_key, private_key_str = ssh.generate_ssh_key_strings()
515+
514516
ssh.add_public_key_to_central_authorized_keys(
515-
self.project.cfg, password, log=False
517+
self.project.cfg, rsa_key, password, log=False
518+
)
519+
self.project._setup_rclone_central_ssh_config(
520+
private_key_str, log=False
516521
)
517-
self.project._setup_rclone_central_ssh_config(log=False)
518522

519523
rclone.check_successful_connection_and_raise_error_on_fail(
520524
self.project.cfg

datashuttle/tui/screens/setup_ssh.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,7 @@ def use_password_to_setup_ssh_key_pairs(self) -> None:
152152
)
153153

154154
if success:
155-
message = (
156-
f"Connection successful! SSH key "
157-
f"saved to {self.interface.get_configs().ssh_key_path}"
158-
)
155+
message = "Connection successful! SSH key saved to the RClone config file."
159156
self.query_one("#setup_ssh_ok_button").label = "Finish"
160157
self.query_one("#setup_ssh_cancel_button").disabled = True
161158
self.stage += 1

datashuttle/utils/rclone.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
from typing import TYPE_CHECKING, Dict, List, Literal, Optional
44

55
if TYPE_CHECKING:
6-
from pathlib import Path
7-
86
from datashuttle.configs.config_class import Configs
97
from datashuttle.utils.custom_types import TopLevelFolder
108

@@ -176,7 +174,7 @@ def setup_rclone_config_for_local_filesystem(
176174
def setup_rclone_config_for_ssh(
177175
cfg: Configs,
178176
rclone_config_name: str,
179-
ssh_key_path: Path,
177+
private_key_str: str,
180178
log: bool = True,
181179
) -> None:
182180
"""Set the RClone remote config for ssh.
@@ -194,25 +192,27 @@ def setup_rclone_config_for_ssh(
194192
canonical config name, generated by
195193
datashuttle.cfg.get_rclone_config_name()
196194
197-
ssh_key_path
198-
path to the ssh key used for connecting to
199-
ssh central filesystem
195+
private_key_str
196+
PEM encoded ssh private key to pass to RClone.
200197
201198
log
202199
whether to log, if True logger must already be initialised.
203200
204201
"""
205-
call_rclone(
202+
key_escaped = private_key_str.replace("\n", "\\n")
203+
204+
command = (
206205
f"config create "
207206
f"{rclone_config_name} "
208207
f"sftp "
209208
f"host {cfg['central_host_id']} "
210209
f"user {cfg['central_host_username']} "
211210
f"port {canonical_configs.get_default_ssh_port()} "
212-
f"key_file {ssh_key_path.as_posix()}",
213-
pipe_std=True,
211+
f'-- key_pem "{key_escaped}"'
214212
)
215213

214+
call_rclone(command, pipe_std=True)
215+
216216
if log:
217217
log_rclone_config_output()
218218

@@ -274,7 +274,7 @@ def setup_rclone_config_for_gdrive(
274274
f"{client_secret_key_value}"
275275
f"scope drive "
276276
f"root_folder_id {cfg['gdrive_root_folder_id']} "
277-
f"{extra_args}",
277+
f"{extra_args}"
278278
)
279279

280280
return process

0 commit comments

Comments
 (0)