From dcfb66f50560e6105489030d901e37aed646726a Mon Sep 17 00:00:00 2001 From: mlapaglia Date: Thu, 21 May 2026 22:22:06 -0400 Subject: [PATCH 1/2] disable it --- .../cloud_providers/storage/sftp_storage.py | 13 + .../providers/sftp/sftp_fields.html | 19 + .../unit/cloud_providers/test_sftp_storage.py | 336 ++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 tests/unit/cloud_providers/test_sftp_storage.py diff --git a/src/borgitory/services/cloud_providers/storage/sftp_storage.py b/src/borgitory/services/cloud_providers/storage/sftp_storage.py index fcbb3d35..10eaed78 100644 --- a/src/borgitory/services/cloud_providers/storage/sftp_storage.py +++ b/src/borgitory/services/cloud_providers/storage/sftp_storage.py @@ -37,6 +37,10 @@ class SFTPStorageConfig(CloudStorageConfig): private_key: Optional[str] = None remote_path: str = Field(..., min_length=1) host_key_checking: bool = Field(default=True) + disable_server_side_checksums: bool = Field( + default=False, + description="Disable MD5/SHA1 hash commands on the remote server. Required for restricted SFTP servers (e.g. Ugreen NAS) that do not allow remote command execution.", + ) @field_validator("host") @classmethod @@ -147,6 +151,7 @@ async def upload_repository( password=self._config.password, private_key=self._config.private_key, path_prefix=remote_path, + disable_hashcheck=self._config.disable_server_side_checksums, ): if not progress_callback: continue @@ -279,11 +284,13 @@ def get_rclone_mapping(cls) -> RcloneMethodMapping: "port": "port", "password": "password", "private_key": "private_key", + "disable_server_side_checksums": "disable_hashcheck", }, required_params=["repository", "host", "username"], optional_params={ "port": 22, "path_prefix": "", + "disable_server_side_checksums": False, }, ) @@ -341,6 +348,7 @@ async def sync_repository_to_sftp( password: Optional[str] = None, private_key: Optional[str] = None, path_prefix: str = "", + disable_hashcheck: bool = False, ) -> AsyncGenerator[ProgressData, None]: """Sync a Borg repository to SFTP using Rclone with SFTP backend""" @@ -359,6 +367,9 @@ async def sync_repository_to_sftp( "--verbose", ] + if disable_hashcheck: + command.append("--sftp-disable-hashcheck") + try: async with self._build_sftp_flags( host, username, port, password, private_key @@ -602,11 +613,13 @@ async def _test_sftp_write_permissions( "port": "port", "password": "password", "private_key": "private_key", + "disable_server_side_checksums": "disable_hashcheck", }, required_params=["repository", "host", "username"], optional_params={ "port": 22, "path_prefix": "", + "disable_server_side_checksums": False, }, ), ) diff --git a/src/borgitory/templates/partials/cloud_sync/providers/sftp/sftp_fields.html b/src/borgitory/templates/partials/cloud_sync/providers/sftp/sftp_fields.html index b72ab114..2aa1a17d 100644 --- a/src/borgitory/templates/partials/cloud_sync/providers/sftp/sftp_fields.html +++ b/src/borgitory/templates/partials/cloud_sync/providers/sftp/sftp_fields.html @@ -74,6 +74,25 @@ +
+ + +

+ Disable if your SFTP server does not allow remote command execution (md5sum/sha1sum). +

+