allow key-import+BORG_KEY_FILE to create key files

Running 'borg key import' on a keyfile repository with the BORG_KEY_FILE
environment variable set works correctly if the BORG_KEY_FILE file
already exists. However, the command crashes if the BORG_KEY_FILE file
does not exist:

    $ BORG_KEY_FILE=newborgkey borg key import /home/strager/borg-backups/straglum borgkey
    Local Exception
    Traceback (most recent call last):
      [snip]
      File "[snip]/borg/crypto/key.py", line 713, in sanity_check
        with open(filename, 'rb') as fd:
    FileNotFoundError: [Errno 2] No such file or directory: '[snip]/newborgkey'

    Platform: Linux straglum 5.0.0-25-generic #26~18.04.1-Ubuntu SMP Thu Aug 1 13:51:02 UTC 2019 x86_64
    Linux: debian buster/sid
    Borg: 1.1.11  Python: CPython 3.7.7 msgpack: 0.5.6
    PID: 15306  CWD: /home/strager/Projects/borg
    sys.argv: ['[snip]/borg', 'key', 'import', '/home/strager/borg-backups/straglum', 'borgkey']
    SSH_ORIGINAL_COMMAND: None

Make 'borg key import' not require the BORG_KEY_FILE file to already
exist.

This commit does not change the behavior of 'borg key import' without
BORG_KEY_FILE. This commit also does not change the behavior of 'borg
key import' on a repokey repository.
This commit is contained in:
Matthew Glazar 2020-05-28 18:52:06 -07:00
parent 538d3245cd
commit 5a32de918e
5 changed files with 44 additions and 4 deletions

View File

@ -26,6 +26,11 @@ Examples
Remember your passphrase. Your data will be inaccessible without it.
Key updated
# Import a previously-exported key into the specified
# key file (creating or overwriting the output key)
# (keyfile repositories only)
$ BORG_KEY_FILE=/path/to/output-key borg key import /path/to/repo /path/to/exported
Fully automated using environment variables:
::

View File

@ -3869,6 +3869,15 @@ class Archiver:
If the ``--paper`` option is given, the import will be an interactive
process in which each line is checked for plausibility before
proceeding to the next line. For this format PATH must not be given.
For repositories using keyfile encryption, the key file which ``borg key
import`` writes to depends on several factors. If the ``BORG_KEY_FILE``
environment variable is set and non-empty, ``borg key import`` creates
or overwrites that file named by ``$BORG_KEY_FILE``. Otherwise, ``borg
key import`` searches in the ``$BORG_KEYS_DIR`` directory for a key file
associated with the repository. If a key file is found in
``$BORG_KEYS_DIR``, ``borg key import`` overwrites it; otherwise, ``borg
key import`` creates a new key file in ``$BORG_KEYS_DIR``.
""")
subparser = key_parsers.add_parser('import', parents=[common_parser], add_help=False,
description=self.do_key_import.__doc__,

View File

@ -720,6 +720,15 @@ class KeyfileKey(ID_HMAC_SHA_256, KeyfileKeyBase):
return keyfile
raise KeyfileNotFoundError(self.repository._location.canonical_path(), get_keys_dir())
def get_existing_or_new_target(self, args):
keyfile = self._find_key_file_from_environment()
if keyfile is not None:
return keyfile
keyfile = self._find_key_in_keys_dir()
if keyfile is not None:
return keyfile
return self._get_new_target_in_keys_dir(args)
def _find_key_in_keys_dir(self):
id = self.repository.id
keys_dir = get_keys_dir()

View File

@ -61,10 +61,7 @@ class KeyManager:
def store_keyblob(self, args):
if self.keyblob_storage == KeyBlobStorage.KEYFILE:
k = KeyfileKey(self.repository)
try:
target = k.find_key()
except KeyfileNotFoundError:
target = k.get_new_target(args)
target = k.get_existing_or_new_target(args)
self.store_keyfile(target)
elif self.keyblob_storage == KeyBlobStorage.REPO:

View File

@ -2807,6 +2807,26 @@ class ArchiverTestCase(ArchiverTestCaseBase):
assert key_contents2 == key_contents
def test_key_import_keyfile_with_borg_key_file(self):
self.cmd('init', self.repository_location, '--encryption', 'keyfile')
exported_key_file = os.path.join(self.output_path, 'exported')
self.cmd('key', 'export', self.repository_location, exported_key_file)
key_file = os.path.join(self.keys_path, os.listdir(self.keys_path)[0])
with open(key_file, 'r') as fd:
key_contents = fd.read()
os.unlink(key_file)
imported_key_file = os.path.join(self.output_path, 'imported')
with environment_variable(BORG_KEY_FILE=imported_key_file):
self.cmd('key', 'import', self.repository_location, exported_key_file)
assert not os.path.isfile(key_file), '"borg key import" should respect BORG_KEY_FILE'
with open(imported_key_file, 'r') as fd:
imported_key_contents = fd.read()
assert imported_key_contents == key_contents
def test_key_export_repokey(self):
export_file = self.output_path + '/exported'
self.cmd('init', self.repository_location, '--encryption', 'repokey')