Merge pull request #9050 from ThomasWaldmann/keyfile-same-repo-path-master
Some checks failed
Lint / lint (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / security (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
CI / linux (ubuntu-22.04, 3.10, mypy) (push) Has been cancelled
CI / linux (ubuntu-22.04, 3.10, py310-fuse2) (push) Has been cancelled
CI / linux (ubuntu-22.04, 3.11, docs) (push) Has been cancelled
CI / linux (ubuntu-22.04, 3.11, py311-fuse3) (push) Has been cancelled
CI / linux (ubuntu-24.04, 3.12, py312-fuse3) (push) Has been cancelled
CI / linux (ubuntu-24.04, 3.13, py313-fuse3) (push) Has been cancelled
CI / linux (ubuntu-24.04, 3.14, py314-fuse3) (push) Has been cancelled
CI / macOS (macos-14, 3.11, py311-none) (push) Has been cancelled
CI / windows (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:24:54 +02:00 committed by GitHub
commit b988f9c3a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 0 deletions

View file

@ -391,6 +391,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.
.. _home_data_borg:
How important is the $HOME/.local/share/borg directory?

View file

@ -55,3 +55,51 @@ def test_repo_create_refuse_to_overwrite_keyfile(archivers, request, monkeypatch
with open(keyfile) as file:
after = file.read()
assert before == after
def test_repo_create_keyfile_same_path_creates_new_keys(archivers, request):
"""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, ...
"""
archiver = request.getfixturevalue(archivers)
# First creation at path A
cmd(archiver, "repo-create", KF_ENCRYPTION)
keys = sorted(os.listdir(archiver.keys_path))
assert len(keys) == 1
base_key = keys[0]
base_path = os.path.join(archiver.keys_path, base_key)
with open(base_path, "rb") as f:
base_contents = f.read()
# Simulate moving/unmounting the repo by removing the path to allow re-create at the same path
import shutil
shutil.rmtree(archiver.repository_path)
cmd(archiver, "repo-create", KF_ENCRYPTION)
keys = sorted(os.listdir(archiver.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(archiver.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 create a third time at same path
shutil.rmtree(archiver.repository_path)
cmd(archiver, "repo-create", KF_ENCRYPTION)
keys = sorted(os.listdir(archiver.keys_path))
assert len(keys) == 3
assert any(k == base_key + ".3" for k in keys)
third_path = os.path.join(archiver.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