From 80818c158845b87e5fb0d28daf29c0e9e67333e1 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 10 Oct 2025 14:12:48 +0200 Subject: [PATCH] docs: What happens when a new keyfile repo is created at the same path?, fixes #6230 Also add a test. --- docs/faq.rst | 24 +++++++++++++++++-- src/borg/testsuite/archiver.py | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index e9597a685..8b11adbb5 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -496,8 +496,8 @@ Another option is to not consider inode numbers in the files cache by passing Why are backups slow on a Linux server that is a member of a Windows domain? ---------------------------------------------------------------------------- -If a Linux server is a member of a Windows domain, username to userid resolution might be -performed via ``winbind`` without caching, which can slow down backups significantly. +If a Linux server is a member of a Windows domain, username to userid resolution might be +performed via ``winbind`` without caching, which can slow down backups significantly. You can use e.g. ``nscd`` to add caching and improve the speed. Security @@ -554,6 +554,26 @@ The Borg config directory has content that you should take care of: Make sure that only you have access to the Borg config directory. + +Note about creating multiple keyfile repositories at the same path +------------------------------------------------------------------ + +If you create a new keyfile-encrypted repository at the same filesystem +path multiple times (for example, when a previous repository at that path +was moved away or unmounted), Borg will not overwrite or reuse the existing +key file in your keys directory. Instead, it creates a new key file by +appending a numeric suffix to the base name (e.g., .2, .3, ...). + +This means you may see multiple key files like: + +- ~/.config/borg/keys/home_user_backup +- ~/.config/borg/keys/home_user_backup.2 +- ~/.config/borg/keys/home_user_backup.3 + +Each of these corresponds to a distinct repository created at the same +path at different times. This behavior avoids accidental key reuse or +overwrite. + .. _cache_security: Do I need to take security precautions regarding the cache? diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index 283e149fc..9b21a3758 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -3116,6 +3116,50 @@ class ArchiverTestCase(ArchiverTestCaseBase): after = file.read() assert before == after + def test_init_keyfile_same_path_creates_new_keys(self): + """Regression test for GH issue #6230. + + When creating a new keyfile-encrypted repository at the same filesystem path + multiple times (e.g., after moving/unmounting the previous one), Borg must not + overwrite or reuse the existing key file. Instead, it should create a new key + file in the keys directory, appending a numeric suffix like .2, .3, ... + """ + # First init at path A + self.cmd('init', '--encryption=keyfile', self.repository_location) + keys = sorted(os.listdir(self.keys_path)) + assert len(keys) == 1 + base_key = keys[0] + base_path = os.path.join(self.keys_path, base_key) + with open(base_path, 'rb') as f: + base_contents = f.read() + + # Simulate moving/unmounting the repo by changing the path and initializing again at the same path + # We remove the repo to allow re-init at the same path + shutil.rmtree(self.repository_path) + self.cmd('init', '--encryption=keyfile', self.repository_location) + keys = sorted(os.listdir(self.keys_path)) + assert len(keys) == 2 + assert base_key in keys + # The new file should be base_key suffixed with .2 + assert any(k == base_key + '.2' for k in keys) + second_path = os.path.join(self.keys_path, base_key + '.2') + with open(second_path, 'rb') as f: + second_contents = f.read() + assert second_contents != base_contents + + # Remove repo again and init a third time at same path + shutil.rmtree(self.repository_path) + self.cmd('init', '--encryption=keyfile', self.repository_location) + keys = sorted(os.listdir(self.keys_path)) + assert len(keys) == 3 + assert any(k == base_key + '.3' for k in keys) + third_path = os.path.join(self.keys_path, base_key + '.3') + with open(third_path, 'rb') as f: + third_contents = f.read() + # Ensure all keys are distinct + assert third_contents != base_contents + assert third_contents != second_contents + def check_cache(self): # First run a regular borg check self.cmd('check', self.repository_location)