Merge pull request #9049 from ThomasWaldmann/keyfile-same-repo-path-1.4
Some checks failed
CI / lint (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
CI / pytest (macos-14, 3.11, py311-none) (push) Has been cancelled
CI / pytest (ubuntu-22.04, 3.10, py310-fuse3) (push) Has been cancelled
CI / pytest (ubuntu-22.04, 3.11, py311-fuse2) (push) Has been cancelled
CI / pytest (ubuntu-22.04, 3.9, py39-fuse2) (push) Has been cancelled
CI / pytest (ubuntu-24.04, 3.12, py312-fuse3) (push) Has been cancelled
CI / pytest (ubuntu-24.04, 3.13, py313-fuse3) (push) Has been cancelled
CI / pytest (ubuntu-24.04, 3.14, py314-fuse3) (push) Has been cancelled

docs: What happens when a new keyfile repo is created at the same path?
This commit is contained in:
TW 2025-10-10 20:25:10 +02:00 committed by GitHub
commit 98b831ba8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 66 additions and 2 deletions

View file

@ -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?

View file

@ -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)