Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encrypt backup metadata #170

Merged
merged 2 commits into from
Jul 17, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions ch_backup/backup/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from typing import Callable, List, Optional, Sequence
from urllib.parse import quote

from nacl.exceptions import CryptoError

from ch_backup import logging
from ch_backup.backup.metadata import BackupMetadata, PartMetadata
from ch_backup.calculators import calc_encrypted_size, calc_tarball_size
Expand Down Expand Up @@ -50,7 +52,7 @@ def upload_backup_metadata(self, backup: BackupMetadata) -> None:
try:
logging.debug("Saving backup metadata in {}", remote_path)
self._storage_loader.upload_data(
backup.dump_json(light=False), remote_path=remote_path
backup.dump_json(light=False), remote_path=remote_path, encryption=True
)
logging.debug("Saving backup light metadata in {}", remote_light_path)
self._storage_loader.upload_data(
Expand Down Expand Up @@ -283,6 +285,15 @@ def get_backup_names(self) -> Sequence[str]:
self._config["path_root"], recursive=False, absolute=False
)

def _load_metadata(self, path: str, encryption: bool) -> BackupMetadata:
try:
data = self._storage_loader.download_data(path, encryption=encryption)
return BackupMetadata.load_json(data)
except CryptoError:
raise
except Exception as e:
raise StorageError("Failed to download backup metadata") from e

def get_backup(
self, backup_name: str, use_light_meta: bool = False
) -> Optional[BackupMetadata]:
Expand All @@ -298,11 +309,16 @@ def get_backup(
if not self._storage_loader.path_exists(path):
return None

# New backup metadata is encrypted
# Retry in case it is old and not encrypted
try:
data = self._storage_loader.download_data(path)
return BackupMetadata.load_json(data)
except Exception as e:
raise StorageError("Failed to download backup metadata") from e
return self._load_metadata(path, not use_light_meta)
except CryptoError:
logging.exception(
"Attempt to download encrypted metadata from {} has failed. Will try to download it as not encrypted",
path,
)
return self._load_metadata(path, False)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it seems it is better to do it with a flag for consistency with data encryption

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is decided to keep the current approach, to avoid downloading light metadata every time, especially when all backups will be encrypted.
But let's add log message here to discover cases when file is actually corrupted.


def get_backups(self, use_light_meta: bool = False) -> List[BackupMetadata]:
"""
Expand Down Expand Up @@ -333,11 +349,12 @@ def reload_backup(
else self._backup_metadata_path(backup.name)
)

# New backup metadata is encrypted
# Retry in case it is old and not encrypted
try:
data = self._storage_loader.download_data(path)
return BackupMetadata.load_json(data)
except Exception as e:
raise StorageError("Failed to download backup metadata") from e
return self._load_metadata(path, not use_light_meta)
except CryptoError:
return self._load_metadata(path, False)

def get_database_create_statement(
self, backup_meta: BackupMetadata, db_name: str
Expand Down