Browse Source

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.
Matthew Glazar 5 years ago
parent
commit
5a32de918e

+ 5 - 0
docs/usage/key.rst

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

+ 9 - 0
src/borg/archiver.py

@@ -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__,

+ 9 - 0
src/borg/crypto/key.py

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

+ 1 - 4
src/borg/crypto/keymanager.py

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

+ 20 - 0
src/borg/testsuite/archiver.py

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